From b948283a96548b64943af3d760fdccecc3d51b66 Mon Sep 17 00:00:00 2001 From: Chris Hammer Date: Sun, 1 Jan 2023 22:36:12 -0500 Subject: [PATCH] commit version 22.12.12447 --- .phpcs.xml | 213 - VERSION | 2 +- alerter.php | 34 +- discovery.php | 31 +- html/ajax/actions.php | 13 +- html/ajax/actions/edit_widget.inc.php | 112 +- html/ajax/actions/settings_edit.inc.php | 6 +- html/ajax/actions/settings_user.inc.php | 66 + html/ajax/entity_popup.php | 22 +- html/ajax/input.php | 12 +- html/ajax/search.php | 3 +- html/ajax/widget.php | 18 +- html/css/bootstrap.css | 4 +- html/css/easymde.min.css | 7 + html/css/observium.css | 23 +- html/css/simplemde.min.css | 7 - html/data.php | 14 +- html/graph-realtime.php | 8 +- html/graph.php | 6 +- html/images/os/acksys.png | Bin 0 -> 1402 bytes html/images/os/acksys_2x.png | Bin 0 -> 2349 bytes html/images/os/affirmed.png | Bin 0 -> 1450 bytes html/images/os/affirmed_2x.png | Bin 0 -> 2560 bytes html/images/os/allot-dark.png | Bin 0 -> 1378 bytes html/images/os/allot-dark_2x.png | Bin 0 -> 1743 bytes html/images/os/allot.png | Bin 0 -> 1387 bytes html/images/os/allot_2x.png | Bin 0 -> 1767 bytes html/images/os/axis-dark.png | Bin 0 -> 1387 bytes html/images/os/axis-dark_2x.png | Bin 0 -> 2059 bytes html/images/os/axis.png | Bin 1245 -> 1386 bytes html/images/os/axis_2x.png | Bin 2807 -> 2082 bytes html/images/os/cumulus.png | Bin 1643 -> 1699 bytes html/images/os/cumulus_2x.png | Bin 3155 -> 2966 bytes html/images/os/genexis.png | Bin 0 -> 1367 bytes html/images/os/genexis_2x.png | Bin 0 -> 1988 bytes html/images/os/hardenedbsd.png | Bin 0 -> 1855 bytes html/images/os/hardenedbsd_2x.png | Bin 0 -> 3623 bytes html/images/os/lantronix.png | Bin 0 -> 1297 bytes html/images/os/lantronix_2x.png | Bin 0 -> 1747 bytes html/images/os/luve.png | Bin 0 -> 1540 bytes html/images/os/luve_2x.png | Bin 0 -> 2413 bytes html/images/os/monnit.png | Bin 0 -> 1330 bytes html/images/os/monnit_2x.png | Bin 0 -> 1952 bytes html/images/os/powershield.png | Bin 0 -> 1128 bytes html/images/os/powershield_2x.png | Bin 0 -> 1537 bytes html/images/os/powertek-dark.png | Bin 0 -> 1696 bytes html/images/os/powertek-dark_2x.png | Bin 0 -> 2996 bytes html/images/os/powertek.png | Bin 0 -> 1727 bytes html/images/os/powertek_2x.png | Bin 0 -> 3138 bytes html/images/os/seagate.png | Bin 0 -> 1386 bytes html/images/os/seagate_2x.png | Bin 0 -> 2000 bytes html/images/os/sigur.png | Bin 0 -> 1306 bytes html/images/os/sigur_2x.png | Bin 0 -> 1818 bytes html/images/os/snr.png | Bin 1640 -> 1309 bytes html/images/os/snr_2x.png | Bin 3246 -> 1624 bytes html/images/os/tfortis.png | Bin 0 -> 1301 bytes html/images/os/tfortis_2x.png | Bin 0 -> 1771 bytes html/images/os/unitrends-dark.png | Bin 0 -> 1039 bytes html/images/os/unitrends-dark_2x.png | Bin 0 -> 1269 bytes html/images/os/unitrends.png | Bin 0 -> 1033 bytes html/images/os/unitrends_2x.png | Bin 0 -> 1254 bytes html/images/os/waveos.png | Bin 0 -> 1311 bytes html/images/os/waveos_2x.png | Bin 0 -> 2065 bytes html/images/os/wisi.png | Bin 0 -> 1176 bytes html/images/os/wisi_2x.png | Bin 0 -> 1919 bytes html/images/os/zyxel-dark.png | Bin 0 -> 1215 bytes html/images/os/zyxel-dark_2x.png | Bin 0 -> 1515 bytes html/images/os/zyxel.png | Bin 1017 -> 1227 bytes html/images/os/zyxel_2x.png | Bin 1974 -> 1511 bytes html/img/router.png | Bin 0 -> 9672 bytes html/includes/actions/role_add.inc.php | 33 +- html/includes/actions/role_entity_add.inc.php | 49 +- html/includes/actions/role_entity_del.inc.php | 25 +- html/includes/alerting-navbar.inc.php | 5 +- html/includes/authenticate-functions.inc.php | 8 + html/includes/authenticate.inc.php | 80 +- html/includes/authentication/ldap.inc.php | 67 +- html/includes/authentication/mysql.inc.php | 42 +- html/includes/authentication/radius.inc.php | 26 +- html/includes/cache-data.inc.php | 36 +- html/includes/contacts-navbar.inc.php | 4 +- html/includes/entities/cbqos.inc.php | 10 +- html/includes/entities/counter.inc.php | 26 +- html/includes/entities/device.inc.php | 163 +- html/includes/entities/generic.inc.php | 34 +- html/includes/entities/mempool.inc.php | 10 +- html/includes/entities/oid_entry.inc.php | 16 +- html/includes/entities/p2pradio.inc.php | 8 +- html/includes/entities/port.inc.php | 75 +- html/includes/entities/printersupply.inc.php | 14 +- html/includes/entities/processor.inc.php | 10 +- html/includes/entities/pseudowire.inc.php | 31 +- html/includes/entities/sensor.inc.php | 146 +- html/includes/entities/sla.inc.php | 20 +- html/includes/entities/status.inc.php | 26 +- html/includes/entities/storage.inc.php | 49 +- html/includes/entities/virtualmachine.inc.php | 21 +- html/includes/functions.inc.php | 311 +- html/includes/graphs/bgp/auth.inc.php | 14 +- html/includes/graphs/common.inc.php | 38 +- html/includes/graphs/customer/bits.inc.php | 2 +- html/includes/graphs/device/bits.inc.php | 2 +- html/includes/graphs/device/processor.inc.php | 5 +- .../device/smokeping_all_common_avg.inc.php | 7 +- html/includes/graphs/device/storage.inc.php | 23 +- html/includes/graphs/generic_data.inc.php | 48 +- .../graphs/generic_definition.inc.php | 9 + html/includes/graphs/generic_duplex.inc.php | 2 +- .../graphs/generic_multi_bits.inc.php | 2 +- .../graphs/generic_multi_separated.inc.php | 21 +- html/includes/graphs/generic_simplex.inc.php | 15 +- html/includes/graphs/graph.inc.php | 77 +- html/includes/graphs/legend.inc.php | 3 + .../graphs/multi-oid-entry/auth.inc.php | 61 + .../graphs/multi-oid-entry/line.inc.php | 25 + .../graphs/multi-oid-entry/stacked.inc.php | 24 + html/includes/graphs/port/auth.inc.php | 6 + html/includes/graphs/sensor/airflow.inc.php | 30 - html/includes/graphs/sensor/apower.inc.php | 38 - .../graphs/sensor/concentration.inc.php | 32 - .../graphs/sensor/crestfactor.inc.php | 38 - html/includes/graphs/sensor/current.inc.php | 41 - html/includes/graphs/sensor/db.inc.php | 47 - html/includes/graphs/sensor/dbm.inc.php | 31 - html/includes/graphs/sensor/dewpoint.inc.php | 48 - html/includes/graphs/sensor/distance.inc.php | 33 - html/includes/graphs/sensor/dust.inc.php | 34 - html/includes/graphs/sensor/fanspeed.inc.php | 30 - html/includes/graphs/sensor/frequency.inc.php | 30 - html/includes/graphs/sensor/gauge.inc.php | 30 - html/includes/graphs/sensor/graph.inc.php | 48 +- .../graphs/sensor/illuminance.inc.php | 34 - html/includes/graphs/sensor/impedence.inc.php | 31 - html/includes/graphs/sensor/percent.inc.php | 38 - html/includes/graphs/sensor/power.inc.php | 34 - .../graphs/sensor/powerfactor.inc.php | 38 - html/includes/graphs/sensor/pressure.inc.php | 33 - .../includes/graphs/sensor/resistance.inc.php | 31 - html/includes/graphs/sensor/rpower.inc.php | 38 - html/includes/graphs/sensor/runtime.inc.php | 38 - html/includes/graphs/sensor/state.inc.php | 28 - .../graphs/sensor/temperature.inc.php | 33 - html/includes/graphs/sensor/velocity.inc.php | 34 - html/includes/graphs/sensor/voltage.inc.php | 39 - html/includes/graphs/sensor/volume.inc.php | 34 - html/includes/graphs/sensor/waterflow.inc.php | 30 - .../includes/graphs/sensor/wavelength.inc.php | 34 - html/includes/graphs/smokeping/in.inc.php | 8 +- html/includes/graphs/smokeping/out.inc.php | 8 +- html/includes/navbar.inc.php | 79 +- html/includes/notifications.inc.php | 71 +- html/includes/port-sort-select.inc.php | 3 + html/includes/port-sort.inc.php | 3 + html/includes/print/addresses.inc.php | 19 +- html/includes/print/alert.inc.php | 8 +- html/includes/print/alert_log.inc.php | 69 +- html/includes/print/arptable.inc.php | 23 +- html/includes/print/authlog.inc.php | 11 +- html/includes/print/common.inc.php | 108 + html/includes/print/dot1xtable.inc.php | 17 +- html/includes/print/events.inc.php | 21 +- html/includes/print/fdbtable.inc.php | 101 +- html/includes/print/inventory.inc.php | 24 +- html/includes/print/logalert.inc.php | 10 +- html/includes/print/mac_addresses.inc.php | 11 +- html/includes/print/navbar.inc.php | 21 +- html/includes/print/neighbours.inc.php | 23 +- html/includes/print/routing.inc.php | 645 +- html/includes/print/rows.inc.php | 57 +- html/includes/print/search.inc.php | 153 +- html/includes/print/syslogs.inc.php | 262 +- html/includes/print/vlan.inc.php | 127 +- html/includes/search/accesspoints.inc.php | 8 +- html/includes/search/devices.inc.php | 17 +- html/includes/search/groups.inc.php | 21 +- html/includes/search/inventory.inc.php | 14 +- html/includes/search/ip-addresses.inc.php | 22 +- html/includes/search/neighbours.inc.php | 15 +- html/includes/search/ports.inc.php | 33 +- html/includes/search/sensors.inc.php | 23 +- html/includes/search/slas.inc.php | 30 +- html/includes/search/status.inc.php | 25 +- html/index.php | 7 +- html/js/bootstrap-confirmation.min.js | 58 +- html/js/cose-base.js | 3094 + html/js/cytoscape-fcose.js | 1425 + html/js/cytoscape-layout-utilities.js | 1 + html/js/cytoscape-popper.js | 394 + html/js/cytoscape.min.js | 32 + html/js/easymde.min.js | 7 + html/js/jquery-migrate.min.js | 4 +- html/js/jquery.min.js | 4 +- html/js/layout-base.js | 5230 + html/js/observium.js | 59 +- html/js/popper.core.js | 6 + html/js/purify.min.js | 3 + html/js/shim.min.js | 10 + html/js/simplemde.min.js | 15 - html/js/tippy.js | 2518 + html/js/weathermap-editor.js | 659 + html/networkmap.php | 11 +- html/pages/about.inc.php | 16 +- html/pages/addhost.inc.php | 449 +- html/pages/alert_check.inc.php | 34 +- html/pages/alert_checks.inc.php | 12 +- html/pages/alert_log.inc.php | 13 +- html/pages/alert_regenerate.inc.php | 30 +- html/pages/apps/overview.inc.php | 7 +- html/pages/contacts.inc.php | 4 +- html/pages/customers.inc.php | 17 +- html/pages/dashboard.inc.php | 23 +- html/pages/delhost.inc.php | 11 +- html/pages/device.inc.php | 17 +- html/pages/device/edit.inc.php | 7 +- html/pages/device/edit/geo.inc.php | 73 +- html/pages/device/edit/mibs.inc.php | 3 +- html/pages/device/edit/snmp.inc.php | 394 +- html/pages/device/health.inc.php | 35 +- html/pages/device/logs/alertlog.inc.php | 13 +- html/pages/device/logs/eventlog.inc.php | 4 +- html/pages/device/logs/syslog.inc.php | 4 +- html/pages/device/notes.inc.php | 27 +- .../pages/device/overview/information.inc.php | 6 +- .../overview/information_extended.inc.php | 4 +- html/pages/device/overview/sensors.inc.php | 258 +- html/pages/device/overview/storage.inc.php | 55 +- html/pages/device/perf.inc.php | 103 +- html/pages/device/ports.inc.php | 4 +- html/pages/device/routing/ospf.inc.php | 198 +- html/pages/device/wifi/accesspoints.inc.php | 2 +- html/pages/devices/detail.inc.php | 26 +- html/pages/devices/status.inc.php | 2 + html/pages/eventlog.inc.php | 13 +- html/pages/graphs.inc.php | 106 +- html/pages/iftype.inc.php | 6 +- html/pages/locations.inc.php | 18 +- html/pages/map.inc.php | 979 +- html/pages/map_traffic.inc.php | 469 + html/pages/neighbours.inc.php | 9 +- html/pages/packages.inc.php | 13 +- html/pages/ports.inc.php | 2 +- html/pages/ports/list.inc.php | 21 +- html/pages/preferences/general.inc.php | 2 +- html/pages/roles.inc.php | 7 +- html/pages/routing/bgp.inc.php | 11 +- html/pages/search.inc.php | 32 +- html/pages/settings.inc.php | 13 +- html/pages/syslog.inc.php | 8 +- html/pages/syslog_rules.inc.php | 219 +- html/pages/user_add.inc.php | 203 +- html/pages/user_edit.inc.php | 127 +- html/pages/vlan.inc.php | 4 +- html/pages/wmap.inc.php | 132 + html/weathermap.php | 35 + includes/alerts.inc.php | 686 +- includes/cache.inc.php | 37 +- includes/common.inc.php | 273 +- includes/config-variables.inc.php | 1707 +- includes/db.inc.php | 125 +- includes/defaults.inc.php | 46 +- includes/definitions.inc.php | 446 +- includes/definitions/definitions.dat | Bin 393872 -> 403425 bytes includes/definitions/version.inc.php | 8 +- includes/definitions/wui.inc.php | 4 + includes/discovery/arp-table.inc.php | 6 +- includes/discovery/bgp-peers.inc.php | 10 +- .../discovery/counter/printer-mib.inc.php | 31 +- includes/discovery/functions.inc.php | 10 +- .../discovery/inventory/entity-mib.inc.php | 72 +- .../inventory/host-resources-mib.inc.php | 22 +- ...atel-ind1-interswitch-protocol-mib.inc.php | 4 +- .../neighbours/cisco-cdp-mib.inc.php | 10 +- .../foundry-sn-switch-group-mib.inc.php | 4 +- .../discovery/neighbours/isdp-mib.inc.php | 4 +- .../discovery/neighbours/lldp-mib.inc.php | 14 +- .../discovery/neighbours/ospf-mib.inc.php | 20 +- .../discovery/neighbours/ospfv3-mib.inc.php | 59 + includes/discovery/oids.inc.php | 2 +- includes/discovery/os/freebsd.inc.php | 10 +- includes/discovery/ports-stack.inc.php | 126 +- includes/discovery/ports.inc.php | 4 +- .../ports/coriant-groove-mib.inc.php | 129 + .../printersupplies/printer-mib.inc.php | 5 +- .../processors/host-resources-mib.inc.php | 52 +- .../pseudowires/cisco-ietf-pw-mib.inc.php | 7 +- .../pseudowires/juniper-vpn-mib.inc.php | 5 +- .../discovery/pseudowires/pw-std-mib.inc.php | 9 +- .../discovery/sensors/areca-snmp-mib.inc.php | 43 +- .../discovery/sensors/axis-video-mib.inc.php | 33 +- .../sensors/cisco-entity-sensor-mib.inc.php | 42 +- .../discovery/sensors/cpqpower-mib.inc.php | 36 +- .../discovery/sensors/ddm-mgmt-mib.inc.php | 92 +- .../discovery/sensors/dksf-48-4-x-x-1.inc.php | 2 + .../sensors/dksf-50-11-x-x-x.inc.php | 2 + .../discovery/sensors/dksf-60-4-x-x-x.inc.php | 2 + ...70-5-x-x-1.inc.php => dksf-70-mib.inc.php} | 31 +- .../discovery/sensors/eaton-epdu-mib.inc.php | 42 +- includes/discovery/sensors/egw4mib.inc.php | 96 + .../discovery/sensors/enlogic-pdu-mib.inc.php | 116 +- includes/discovery/sensors/eppc-mib.inc.php | 232 +- .../sensors/fortinet-fortigate-mib.inc.php | 14 +- .../sensors/huawei-entity-extent-mib.inc.php | 51 +- .../sensors/netonix-switch-mib.inc.php | 16 +- .../discovery/sensors/powernet-mib.inc.php | 6 +- includes/discovery/sensors/pwtv1-mib.inc.php | 215 + .../discovery/sensors/rad-gen-mib.inc.php | 4 +- .../sensors/rittal-cmc-iii-mib.inc.php | 17 +- .../sensors/roomalert12s-mib.inc.php | 84 + .../sensors/teracom-tcw240b-mib.inc.php | 3 +- .../sensors/teracom-tcw241-mib.inc.php | 144 + includes/discovery/sensors/ups-mib.inc.php | 14 +- includes/discovery/sensors/xups-mib.inc.php | 129 +- .../status/cisco-stackwise-mib.inc.php | 1 - includes/discovery/storage.inc.php | 121 +- .../storage/embedded-ngx-mib.inc.php | 30 - includes/discovery/storage/gpfs-mib.inc.php | 52 - .../storage/host-resources-mib.inc.php | 8 +- includes/discovery/storage/isilon-mib.inc.php | 30 - includes/discovery/storage/netapp-mib.inc.php | 98 - includes/discovery/storage/nimble-mib.inc.php | 49 - .../discovery/storage/ucd-snmp-mib.inc.php | 5 +- .../vlans/a3com-huawei-lswvlan-mib.inc.php | 57 + includes/discovery/vlans/dcn-mib.inc.php | 2 +- includes/discovery/vlans/q-bridge-mib.inc.php | 108 +- includes/discovery/vrf.inc.php | 4 +- includes/entities.inc.php | 190 +- includes/entities/device.inc.php | 230 +- includes/entities/ip-address.inc.php | 6 +- includes/entities/port.inc.php | 42 +- includes/entities/routing.inc.php | 58 +- includes/entities/status.inc.php | 32 +- includes/entities/storage.inc.php | 478 + includes/entities/wifi.inc.php | 6 +- includes/functions.inc.php | 60 +- includes/housekeeping/ports.inc.php | 4 +- includes/housekeeping/staledb.inc.php | 6 +- includes/polling/bgp/bgp4-mib.inc.php | 4 +- .../polling/bgp/cumulus-bgpun-mib.inc.php | 4 +- includes/polling/fdb-table.inc.php | 20 +- includes/polling/functions.inc.php | 43 +- .../graphs/teradici-pcoipv2-mib.inc.php | 10 +- includes/polling/ipmi.inc.php | 16 +- includes/polling/lsp.inc.php | 13 +- includes/polling/os/breeze.inc.php | 10 +- includes/polling/os/cisco.inc.php | 172 +- includes/polling/os/eppc-ups.inc.php | 52 +- includes/polling/os/fortinet.inc.php | 14 +- includes/polling/os/unix.inc.php | 271 +- includes/polling/os/wisi-tangram.inc.php | 27 + includes/polling/ospf.inc.php | 412 +- includes/polling/ospf/ospf-mib.inc.php | 447 + includes/polling/ospf/ospfv3-mib.inc.php | 462 + .../polling/ports/coriant-groove-mib.inc.php | 166 + includes/polling/sensors.inc.php | 4 +- includes/polling/storage.inc.php | 122 +- .../polling/storage/embedded-ngx-mib.inc.php | 30 - includes/polling/storage/gpfs-mib.inc.php | 37 - includes/polling/storage/isilon-mib.inc.php | 29 - includes/polling/storage/netapp-mib.inc.php | 57 - includes/polling/storage/nimble-mib.inc.php | 41 - includes/polling/system.inc.php | 49 +- includes/polling/unix-agent/nvidia.inc.php | 58 +- includes/polling/unix-agent/packages.inc.php | 2 +- includes/rewrites.inc.php | 237 +- includes/rrdtool.inc.php | 14 +- includes/snmp.inc.php | 123 +- includes/sql-config.inc.php | 20 +- includes/syslog.inc.php | 10 +- .../templates/notification/zoom_markdown.tpl | 43 + .../templates/test/notification_SYSLOG.json | 10 + includes/weathermap/.htaccess | 7 + includes/weathermap/CHANGES | 518 + includes/weathermap/COPYING | 340 + includes/weathermap/README.md | 36 + includes/weathermap/check-gdbug.php | 45 + includes/weathermap/check.php | 282 + includes/weathermap/configs/.htaccess | 4 + includes/weathermap/configs/example.conf | 212 + includes/weathermap/configs/index.php | 5 + includes/weathermap/configs/simple.conf | 99 + .../weathermap/editor-resources/editor.css | 141 + .../editor-resources/exclamation.png | Bin 0 -> 701 bytes .../weathermap/editor-resources/index.php | 5 + .../editor-resources/jquery-latest.min.js | 154 + includes/weathermap/editor.php | 1471 + includes/weathermap/images/Cloud-Filled.png | Bin 0 -> 5439 bytes includes/weathermap/images/Cloud-line.png | Bin 0 -> 3257 bytes includes/weathermap/images/Firewall.png | Bin 0 -> 797 bytes includes/weathermap/images/HPMini.png | Bin 0 -> 3949 bytes includes/weathermap/images/Host.png | Bin 0 -> 4331 bytes includes/weathermap/images/PAD.png | Bin 0 -> 905 bytes includes/weathermap/images/Router.png | Bin 0 -> 4689 bytes .../weathermap/images/WorkgroupSwitch.png | Bin 0 -> 4387 bytes includes/weathermap/images/application.png | Bin 0 -> 464 bytes .../images/application_side_list.png | Bin 0 -> 510 bytes .../images/application_xp_terminal.png | Bin 0 -> 507 bytes .../images/background1_950x625_197.jpg | Bin 0 -> 51814 bytes includes/weathermap/images/blue-ball-64.png | Bin 0 -> 3424 bytes includes/weathermap/images/blueboard1024.png | Bin 0 -> 154294 bytes includes/weathermap/images/btn_recalc.png | Bin 0 -> 3256 bytes includes/weathermap/images/bug.png | Bin 0 -> 774 bytes .../weathermap/images/button_editgroups.png | Bin 0 -> 1724 bytes .../weathermap/images/button_settings.gif | Bin 0 -> 1768 bytes .../weathermap/images/bw_gradient_1024.png | Bin 0 -> 116808 bytes includes/weathermap/images/comment.png | Bin 0 -> 413 bytes .../images/control_fastforward_blue.png | Bin 0 -> 736 bytes .../weathermap/images/control_pause_blue.png | Bin 0 -> 721 bytes .../weathermap/images/control_play_blue.png | Bin 0 -> 717 bytes .../weathermap/images/control_rewind_blue.png | Bin 0 -> 745 bytes .../weathermap/images/control_stop_blue.png | Bin 0 -> 695 bytes includes/weathermap/images/cross.png | Bin 0 -> 655 bytes includes/weathermap/images/emoticon_smile.png | Bin 0 -> 725 bytes .../weathermap/images/emoticon_unhappy.png | Bin 0 -> 723 bytes includes/weathermap/images/error.png | Bin 0 -> 666 bytes includes/weathermap/images/exclamation.png | Bin 0 -> 701 bytes includes/weathermap/images/filecabinet.png | Bin 0 -> 901 bytes includes/weathermap/images/flag_blue.png | Bin 0 -> 671 bytes includes/weathermap/images/flag_green.png | Bin 0 -> 672 bytes includes/weathermap/images/flag_orange.png | Bin 0 -> 669 bytes includes/weathermap/images/flag_pink.png | Bin 0 -> 651 bytes includes/weathermap/images/flag_purple.png | Bin 0 -> 656 bytes includes/weathermap/images/flag_yellow.png | Bin 0 -> 671 bytes includes/weathermap/images/green-ball-64.png | Bin 0 -> 3550 bytes includes/weathermap/images/grey-ball-64.png | Bin 0 -> 2838 bytes includes/weathermap/images/hollow32.png | Bin 0 -> 201 bytes includes/weathermap/images/index.php | 5 + includes/weathermap/images/monitor.png | Bin 0 -> 612 bytes includes/weathermap/images/page_white.png | Bin 0 -> 294 bytes includes/weathermap/images/pencil.png | Bin 0 -> 450 bytes includes/weathermap/images/red-ball-64.png | Bin 0 -> 3552 bytes .../weathermap/images/s_tab_weathermap.gif | Bin 0 -> 1813 bytes .../images/s_tab_weathermap_red.gif | Bin 0 -> 1315 bytes includes/weathermap/images/tab_weathermap.gif | Bin 0 -> 2614 bytes .../weathermap/images/tab_weathermap_red.gif | Bin 0 -> 2645 bytes includes/weathermap/images/tick.png | Bin 0 -> 537 bytes includes/weathermap/images/uk1024.png | Bin 0 -> 197892 bytes includes/weathermap/images/updown_0.png | Bin 0 -> 574 bytes includes/weathermap/images/updown_1.png | Bin 0 -> 531 bytes includes/weathermap/images/updown_2.png | Bin 0 -> 621 bytes includes/weathermap/images/world.png | Bin 0 -> 923 bytes includes/weathermap/images/world_link.png | Bin 0 -> 957 bytes includes/weathermap/images/wrench.png | Bin 0 -> 610 bytes includes/weathermap/images/wrench_orange.png | Bin 0 -> 584 bytes includes/weathermap/images/yellow-ball-64.png | Bin 0 -> 3440 bytes includes/weathermap/lib/.htaccess | 4 + .../weathermap/lib/HTML_ImageMap.class.php | 427 + includes/weathermap/lib/WMLine.class.php | 61 + includes/weathermap/lib/WMPoint.class.php | 161 + includes/weathermap/lib/WMVector.class.php | 121 + .../weathermap/lib/WeatherMap.functions.php | 2198 + .../lib/WeatherMap.keywords.inc.php | 781 + .../weathermap/lib/WeatherMapLink.class.php | 856 + .../weathermap/lib/WeatherMapNode.class.php | 1021 + includes/weathermap/lib/Weathermap.class.php | 3453 + includes/weathermap/lib/database.php | 30 + .../weathermap/lib/datasources/README.txt | 6 + .../WeatherMapDataSource_cactihost.php | 89 + .../WeatherMapDataSource_cactithold.php | 236 + .../WeatherMapDataSource_dbsample.php | 82 + .../WeatherMapDataSource_dsstats.php | 214 + ...WeatherMapDataSource_external.php.disabled | 86 + .../WeatherMapDataSource_fping.php | 138 + .../datasources/WeatherMapDataSource_mrtg.php | 81 + .../WeatherMapDataSource_obsdb.php | 65 + .../datasources/WeatherMapDataSource_rrd.php | 572 + .../WeatherMapDataSource_skeleton.php.txt | 49 + .../datasources/WeatherMapDataSource_snmp.php | 154 + .../WeatherMapDataSource_static.php | 48 + .../WeatherMapDataSource_tabfile.php | 60 + .../datasources/WeatherMapDataSource_time.php | 72 + .../WeatherMapDataSource_wmdata.php | 76 + includes/weathermap/lib/ds-common.php | 85 + includes/weathermap/lib/editor.inc.php | 711 + includes/weathermap/lib/geometry.php | 55 + includes/weathermap/lib/index.php | 5 + includes/weathermap/lib/poller-common.php | 340 + .../post/WeatherMapPostProcessorExample.php | 28 + .../lib/pre/WeatherMapPreProcessorExample.php | 15 + includes/weathermap/map-poller.php | 53 + includes/weathermap/overlib.js | 1491 + includes/weathermap/random-bits/.htaccess | 4 + includes/weathermap/random-bits/README | 70 + .../weathermap/random-bits/auto-overlib.pl | 119 + includes/weathermap/random-bits/bristle.php | 67 + .../random-bits/cacti-integrate.php | 436 + .../weathermap/random-bits/cacti-mapper.php | 348 + .../random-bits/convert-to-dsstats.php | 303 + includes/weathermap/random-bits/index.php | 5 + includes/weathermap/random-bits/map-split.php | 83 + .../weathermap/random-bits/map-tidyup.php | 105 + includes/weathermap/random-bits/suite-1.conf | 544 + includes/weathermap/random-bits/suite-1.png | Bin 0 -> 36265 bytes includes/weathermap/random-bits/suite-2.conf | 206 + includes/weathermap/random-bits/suite-2.png | Bin 0 -> 373770 bytes includes/weathermap/weathermap | 366 + libs/Mobile/Detect.php | 26 +- libs/UserAgentParser.php | 15 +- libs/cli/arguments/HelpScreen.php | 6 +- libs/flight2/Engine.php | 662 + libs/flight2/Flight.php | 117 + libs/flight2/LICENSE | 21 + libs/flight2/README.md | 920 + libs/flight2/VERSION | 1 + libs/flight2/autoload.php | 15 + libs/flight2/core/Dispatcher.php | 254 + libs/flight2/core/Loader.php | 233 + libs/flight2/net/Request.php | 320 + libs/flight2/net/Response.php | 321 + libs/flight2/net/Route.php | 156 + libs/flight2/net/Router.php | 118 + libs/flight2/template/View.php | 200 + libs/flight2/util/Collection.php | 250 + libs/flight2/util/LegacyJsonSerializable.php | 13 + libs/parsedown/Parsedown.php | 13 + mibs/acksys/ACKSYS-MIB | 9912 + mibs/acksys/ACKSYS-WLG-MIB | 2431 + mibs/allot/ALLOT-MIB | 5025 + mibs/allot/ALLOT-NX-MIB | 257 + mibs/apc/NETBOTZ410-MIB | 2324 +- mibs/arista/ARISTA-ASIC-COUNTERS-MIB | 233 + mibs/arista/ARISTA-BGP4V2-MIB | 118 +- mibs/arista/ARISTA-BGP4V2-TC-MIB | 40 +- mibs/arista/ARISTA-IF-MIB | 79 +- mibs/arista/ARISTA-PRODUCTS-MIB | 46 +- mibs/arista/ARISTA-TAPAGG-MIB | 256 + mibs/avtech/ROOMALERT12S-MIB | 319 + .../bachmann/BACHMANN-BLUENET2-CAPABILITY-MIB | 456 - mibs/bachmann/RNX-UPDU-MIB | 714 + mibs/buffalo/BUFFALO-NAS-MIB | 1663 + mibs/buffalo/BUFFALO-ROOT-MIB | 60 + mibs/cdata/CDATA-COMMON-SMI | 175 + mibs/cdata/CDATA-EPON-MIB | 2757 + mibs/cdata/CDATA-GPON-MIB | 6182 + mibs/cdata/CDATA-GPON-MIB2 | 5664 + mibs/cdata/EDFA-oa-MIB | 232 + mibs/cdata/EPON-EOC-MIB | 64 +- mibs/cdata/FD-OLT-MIB | 853 +- mibs/cdata/FD-ONU-MIB | 1845 +- mibs/cdata/FD-PERFORMANCE-MIB | 412 +- mibs/cdata/FD-SWITCH-MIB | 776 +- mibs/cdata/FD-SYSTEM-MIB | 1107 +- mibs/cdata/FD-TRAP-MIB | 115 +- mibs/cdata/LTNET-COMMONINFO-MIB | 89 + mibs/cdata/LTNET-ROOT | 21 + mibs/cdata/NSCRTV-PON-TREE-EXT-MIB | 7509 + mibs/cdata/SPC200 | 137 +- mibs/cdata/VENDOR-COMMON-MIB | 200 + mibs/cdata/XXX-MIB | 20 +- mibs/cisco/CISCO-ENTITY-VENDORTYPE-OID-MIB | 112 +- mibs/cisco/CISCO-PRODUCTS-MIB | 65 +- mibs/cisco/CISCO-RTTMON-MIB | 19 - mibs/cisco/CISCO-TEMPERATURE-MIB | 165 + mibs/cisco/CISCO-URWB-MIB | 703 + mibs/cisco/EXALINK-FUSION-MIB | 956 + mibs/coriant/CORIANT-GROOVE-MIB | 16519 +- mibs/cyberpower/CPS-MIB | 2264 +- mibs/dell/DELLTRAPS-MIB | 119 + mibs/eltek/SP2-MIB | 3464 +- mibs/eltex/EMUX-MIB | 1969 + mibs/eltex/EMUX-TRAPS-V1-MIB | 43 + .../EXTREME-NETSIGHT-ALARM-MIB | 0 mibs/firebrick/FIREBRICK-BGP-MIB | 181 + mibs/firebrick/FIREBRICK-CPU-MIB | 130 + mibs/firebrick/FIREBRICK-IPSEC-MIB | 146 + mibs/firebrick/FIREBRICK-L2TP-MIB | 308 + mibs/firebrick/FIREBRICK-MIB | 3 +- mibs/firebrick/FIREBRICK-MONITORING | 113 + mibs/firebrick/FIREBRICK-RUNSTATS-MIB | 86 + mibs/firebrick/FIREBRICK-VOIP-MIB | 182 + mibs/fortinet/FORTINET-CORE-MIB | 6 +- mibs/fortinet/FORTINET-FORTIGATE-MIB | 24 +- .../FORTINET-FORTIMANAGER-FORTIANALYZER-MIB | 902 +- mibs/fscom/FS-AG-MIB | 4 +- mibs/fscom/FS-CLUSTER-MIB | 172 - mibs/fscom/FS-CT-STANDARD-MIB | 2 +- mibs/fscom/FS-IP-PRIVATE-MIB | 16 +- mibs/fscom/FS-MAPINFO-MNG-MIB | 2 +- mibs/fscom/FS-V1-TRAP | 36 +- mibs/fscom/FS-VM-MIB | 40 +- mibs/fscom/FS-VPLS-BGP-MIB | 28 +- mibs/fscom/FS-VPLS-LDP-MIB | 98 +- mibs/fscom/FS-WLAN-FIT-AP-CF-MIB | 20 +- mibs/fscom/FS-WLAN-FIT-AP-IN-MIB | 4 +- mibs/fscom/GBNDeviceSWAPI-MIB | 4 +- mibs/fscom/GBNDeviceSwitch-MIB | 12 +- mibs/fscom/GBNL2PppoePlus-MIB | 146 +- mibs/fscom/GBNL2Switch-MIB | 79 +- mibs/fscom/GBNL3If-MIB | 14 +- mibs/fscom/GBNL3Rip-MIB | 15 +- mibs/fscom/GBNPlatformOAM-MIB | 115 +- mibs/fscom/GBNPlatformOAMSyslog-MIB | 7 +- mibs/fscom/GPON-MIB | 2974 + mibs/fscom/IPPDU-MIB | 583 + mibs/fscom/MSTP-MIB | 774 + mibs/fscom/NGPON-MIB | 11997 + mibs/fscom/NSCRTV-EPON-EPONLINKEDEOC-MGM-MIB | 241 + mibs/fscom/NSCRTV-EPON-ONU-MIB | 1374 + mibs/fscom/NSCRTV-EPON-PON-PORT-MIB | 1015 + mibs/fscom/NSCRTV-EPON-UNI-MIB | 880 + mibs/gude/GUDEADS-ESB7213-MIB | 124 +- mibs/hp/CPQHSV300V9-MIB | 7740 +- mibs/{nimble => hp}/NIMBLE-MIB | 0 mibs/{nimble => hp}/NIMBLE-TRAP-MIB | 6364 +- mibs/ibm/IBM-PDU-MIB | 1149 + mibs/ibm/IBM-RXR-MIB | 2 +- mibs/ibm/IBM-SVC-MIB | 40 +- mibs/kyocera/KYOCERA-MIB | 11 +- mibs/kyocera/KYOCERA-Private-MIB | 1364 - mibs/microsoft/AFFIRMED-ALARM-MIB | 967 + mibs/microsoft/AFFIRMED-ALARMS-MIB | 168 + mibs/microsoft/AFFIRMED-ARP-MIB | 91 + mibs/microsoft/AFFIRMED-CHASSIS-MIB | 4541 + mibs/microsoft/AFFIRMED-CLUSTER-MIB | 413 + mibs/microsoft/AFFIRMED-DATAPATH-MIB | 135 + mibs/microsoft/AFFIRMED-DATAPLANESTATS-MIB | 123 + mibs/microsoft/AFFIRMED-DATARECORD-MIB | 150 + mibs/microsoft/AFFIRMED-EMS-SNMP-TRAP-MIB | 72 + mibs/microsoft/AFFIRMED-EXTSTORAGE-MIB | 109 + mibs/microsoft/AFFIRMED-FM-MIB | 77 + mibs/microsoft/AFFIRMED-GEORED-MIB | 393 + mibs/microsoft/AFFIRMED-IM-MIB | 323 + mibs/microsoft/AFFIRMED-INFRASTRUCTURE-MIB | 1672 + mibs/microsoft/AFFIRMED-IPDATAPATH-MIB | 103 + mibs/microsoft/AFFIRMED-IPPROTOCOLS-MIB | 4412 + mibs/microsoft/AFFIRMED-LICENSE-MIB | 364 + mibs/microsoft/AFFIRMED-MME-TRAPS-MIB | 6931 + mibs/microsoft/AFFIRMED-MSF-MIB | 27 + .../microsoft/AFFIRMED-MULTIACTIVEROUTING-MIB | 69 + mibs/microsoft/AFFIRMED-NETWORKCONTEXT-MIB | 6404 + mibs/microsoft/AFFIRMED-PERFSTATMGMT-MIB | 163 + mibs/microsoft/AFFIRMED-PKI-MIB | 146 + mibs/microsoft/AFFIRMED-SCEFSUBSCRIBER-MIB | 22 + mibs/microsoft/AFFIRMED-SECURITY-MIB | 12860 + mibs/microsoft/AFFIRMED-SERVICECONSTRUCT-MIB | 16927 + mibs/microsoft/AFFIRMED-SERVICES-MIB | 43843 +++ mibs/microsoft/AFFIRMED-SNMP-AN3000-TRAPS-MIB | 5703 + mibs/microsoft/AFFIRMED-SNMP-DG | 214 + mibs/microsoft/AFFIRMED-SNMP-MIB | 65 + mibs/microsoft/AFFIRMED-SSF-MIB | 697 + mibs/microsoft/AFFIRMED-SUBSCRIBER-MIB | 24 + mibs/microsoft/AFFIRMED-SYSTEM-MIB | 114 + mibs/microsoft/AFFIRMED-TAM-MIB | 173 + mibs/microsoft/AFFIRMED-TEMS-NOTIFICATION-MIB | 655 + mibs/microsoft/AFFIRMED-TEMS-OBJECTS-MIB | 637 + mibs/microsoft/AFFIRMED-TEMS-SNMP-MIB | 47 + mibs/microsoft/AFFIRMED-TEMS-TC-MIB | 54 + mibs/microsoft/AFFIRMED-USERACCESS-MIB | 105 + mibs/microsoft/AFFIRMED-VTAP-MIB | 308 + mibs/microsoft/AFFIRMED-ZONE-MIB | 264125 +++++++++++++++ mibs/monnit/EGW4MIB | 1063 + mibs/monnit/MONNIT-EGW-MIB | 295 + mibs/monnit/POEXMIB | 1063 + mibs/netping/DKSF-561-1-X-X-1 | 313 + mibs/netping/DKSF-57-1-X-X-1 | 1254 + mibs/netping/DKSF-60-4-X-X-X | 123 +- mibs/netping/DKSF-70-3-X-X-1 | 800 - mibs/netping/DKSF-70-4-X-X-1 | 841 - mibs/netping/DKSF-70-5-X-X-1 | 1177 - mibs/netping/DKSF-70-6-X-X-1 | 1206 - mibs/netping/DKSF-70-MIB | 2052 + mibs/netping/DKSF-707-1-X-X-1 | 133 - mibs/nscrtv/NSCRTV-FTTX-EPON-MIB | 12875 + mibs/nscrtv/NSCRTV-FTTX-GPON-MIB | 5572 + .../nscrtv/NSCRTV-HFCEMS-OPTICALAMPLIFIER-MIB | 281 + mibs/nti/ENVIROMUX-1W-MIB | 121 +- mibs/nti/ENVIROMUX16D | 4032 +- mibs/nti/ENVIROMUX2D | 4035 +- mibs/nti/ENVIROMUX5D | 4031 +- mibs/nti/ENVIROMUXMICRO-MIB | 111 +- mibs/nti/ENVIROMUXMINI-MIB | 751 + mibs/nti/ENVMINILX | 751 + mibs/nti/IPDUS2 | 797 + mibs/nti/IPDUSX | 983 + mibs/paloalto/PAN-COMMON-MIB | 1014 +- mibs/paloalto/PAN-PRODUCTS-MIB | 232 + mibs/paloalto/PAN-TRAPS | 1817 +- mibs/powershield/PS-POWERSHIELD-MIB | 736 + mibs/powershield/voltronicMIB | 2758 + mibs/powertek/PWTv1-MIB | 6289 + mibs/powerwalker/EPPC-MIB | 2 +- mibs/qnap/NAS-ES-MIB | 726 + mibs/qnap/NAS-MIB | 2861 +- mibs/qnap/QTS-MIB | 433 + mibs/rfc/ADSL-DMT-LINE-MIB | 637 + mibs/rfc/IPSEC-ISAKMP-IKE-DOI-TC | 702 + mibs/rfc/IPSEC-POLICY-MIB | 6449 + mibs/rfc/IPSEC-SA-MON-MIB | 2720 + mibs/seagate/SEAGATESYSTEMTRAP-MIB | 119 + mibs/sigur/SIGUR-MIB | 394 + mibs/smartoptics/DCP-ALARM-MIB | 44 +- mibs/smartoptics/DCP-INTERFACE-MIB | 57 +- mibs/smartoptics/DCP-LINKVIEW-MIB | 8 +- mibs/smartoptics/DCP-OCH-MIB | 391 + mibs/smartoptics/DCP-TOPOLOGY-MIB | 134 + mibs/smartoptics/SO-TC-MIB | 69 +- mibs/tfortis/FORT-TELECOM-MIB | 1421 + mibs/ubiquiti/UBNT-EdgeMAX-MIB | 243 +- mibs/unitrends/UNITRENDS-SNMP | 1514 + mibs/vmware/VMW-TUNNEL-SERVER-AGENTCAP-MIB | 47 +- mibs/vmware/VMWARE-ESX-AGENTCAP-MIB | 653 +- mibs/vmware/VMWARE-HZECC-AGENTCAP-MIB | 57 + mibs/vmware/VMWARE-HZECC-EVENT-MIB | 227 + mibs/vmware/VMWARE-NSX-AGENTCAP-MIB | 780 + mibs/vmware/VMWARE-NSX-MANAGER-AGENTCAP-MIB | 75 +- mibs/vmware/VMWARE-NSX-MANAGER-MIB | 251 +- mibs/vmware/VMWARE-NSX-MIB | 13579 + mibs/vmware/VMWARE-PRODUCTS-MIB | 16 +- mibs/vmware/VMWARE-ROOT-MIB | 15 +- mibs/vmware/VMWARE-TUNNEL-SERVER-MIB | 213 +- mibs/vmware/VMWARE-VA-AGENTCAP-MIB | 473 +- mibs/vmware/VMWARE-VRNI-AGENTCAP-MIB | 189 +- mibs/vmware/VMWARE-VRNI-MIB | 1856 +- mibs/wisi/WISI-GTMODULES-MIB | 580 + mibs/wisi/WISI-GTSENSORS-MIB | 592 + mibs/wisi/WISI-GTSETTINGS-MIB | 2133 + mibs/wisi/WISI-ROOT-MIB | 21 + mibs/wisi/WISI-TANGRAM-MIB | 170 + notifications.php | 90 + poller-wrapper.py | 45 +- scripts/agent-local/hddtemp | 2 +- scripts/distro | 7 +- scripts/generate-rancid.php | 4 +- .../systemd/observium-notifications.service | 10 + scripts/systemd/observium-notifications.timer | 13 + test_alert.php | 11 +- tests/IncludesCommonTest.php | 17 +- tests/IncludesDbTest.php | 11 +- tests/IncludesFunctionsTest.php | 54 +- tests/SnmpDataTest.php | 1 + update/410.php | 4 +- update/413.php | 2 +- update/415.php | 10 +- update/466.sql | 2 + update/467.sql | 1 + update/468.sql | 2 + update/469.sql | 6 + update/470.sql | 2 + update/471.sql | 1 + update/472.sql | 1 + update/473.sql | 9 + update/474.sql | 1 + update/475.sql | 1 + update/476.sql | 1 + update/477.sql | 11 + 744 files changed, 620715 insertions(+), 27381 deletions(-) delete mode 100644 .phpcs.xml create mode 100644 html/ajax/actions/settings_user.inc.php create mode 100644 html/css/easymde.min.css delete mode 100644 html/css/simplemde.min.css create mode 100644 html/images/os/acksys.png create mode 100644 html/images/os/acksys_2x.png create mode 100644 html/images/os/affirmed.png create mode 100644 html/images/os/affirmed_2x.png create mode 100644 html/images/os/allot-dark.png create mode 100644 html/images/os/allot-dark_2x.png create mode 100644 html/images/os/allot.png create mode 100644 html/images/os/allot_2x.png create mode 100644 html/images/os/axis-dark.png create mode 100644 html/images/os/axis-dark_2x.png create mode 100644 html/images/os/genexis.png create mode 100644 html/images/os/genexis_2x.png create mode 100644 html/images/os/hardenedbsd.png create mode 100644 html/images/os/hardenedbsd_2x.png create mode 100644 html/images/os/lantronix.png create mode 100644 html/images/os/lantronix_2x.png create mode 100644 html/images/os/luve.png create mode 100644 html/images/os/luve_2x.png create mode 100644 html/images/os/monnit.png create mode 100644 html/images/os/monnit_2x.png create mode 100644 html/images/os/powershield.png create mode 100644 html/images/os/powershield_2x.png create mode 100644 html/images/os/powertek-dark.png create mode 100644 html/images/os/powertek-dark_2x.png create mode 100644 html/images/os/powertek.png create mode 100644 html/images/os/powertek_2x.png create mode 100644 html/images/os/seagate.png create mode 100644 html/images/os/seagate_2x.png create mode 100644 html/images/os/sigur.png create mode 100644 html/images/os/sigur_2x.png create mode 100644 html/images/os/tfortis.png create mode 100644 html/images/os/tfortis_2x.png create mode 100644 html/images/os/unitrends-dark.png create mode 100644 html/images/os/unitrends-dark_2x.png create mode 100644 html/images/os/unitrends.png create mode 100644 html/images/os/unitrends_2x.png create mode 100644 html/images/os/waveos.png create mode 100644 html/images/os/waveos_2x.png create mode 100644 html/images/os/wisi.png create mode 100644 html/images/os/wisi_2x.png create mode 100644 html/images/os/zyxel-dark.png create mode 100644 html/images/os/zyxel-dark_2x.png create mode 100644 html/img/router.png create mode 100644 html/includes/graphs/multi-oid-entry/auth.inc.php create mode 100644 html/includes/graphs/multi-oid-entry/line.inc.php create mode 100644 html/includes/graphs/multi-oid-entry/stacked.inc.php delete mode 100644 html/includes/graphs/sensor/airflow.inc.php delete mode 100644 html/includes/graphs/sensor/apower.inc.php delete mode 100644 html/includes/graphs/sensor/concentration.inc.php delete mode 100644 html/includes/graphs/sensor/crestfactor.inc.php delete mode 100644 html/includes/graphs/sensor/current.inc.php delete mode 100644 html/includes/graphs/sensor/db.inc.php delete mode 100644 html/includes/graphs/sensor/dbm.inc.php delete mode 100644 html/includes/graphs/sensor/dewpoint.inc.php delete mode 100644 html/includes/graphs/sensor/distance.inc.php delete mode 100644 html/includes/graphs/sensor/dust.inc.php delete mode 100644 html/includes/graphs/sensor/fanspeed.inc.php delete mode 100644 html/includes/graphs/sensor/frequency.inc.php delete mode 100644 html/includes/graphs/sensor/gauge.inc.php delete mode 100644 html/includes/graphs/sensor/illuminance.inc.php delete mode 100644 html/includes/graphs/sensor/impedence.inc.php delete mode 100644 html/includes/graphs/sensor/percent.inc.php delete mode 100644 html/includes/graphs/sensor/power.inc.php delete mode 100644 html/includes/graphs/sensor/powerfactor.inc.php delete mode 100644 html/includes/graphs/sensor/pressure.inc.php delete mode 100644 html/includes/graphs/sensor/resistance.inc.php delete mode 100644 html/includes/graphs/sensor/rpower.inc.php delete mode 100644 html/includes/graphs/sensor/runtime.inc.php delete mode 100644 html/includes/graphs/sensor/state.inc.php delete mode 100644 html/includes/graphs/sensor/temperature.inc.php delete mode 100644 html/includes/graphs/sensor/velocity.inc.php delete mode 100644 html/includes/graphs/sensor/voltage.inc.php delete mode 100644 html/includes/graphs/sensor/volume.inc.php delete mode 100644 html/includes/graphs/sensor/waterflow.inc.php delete mode 100644 html/includes/graphs/sensor/wavelength.inc.php create mode 100644 html/js/cose-base.js create mode 100644 html/js/cytoscape-fcose.js create mode 100644 html/js/cytoscape-layout-utilities.js create mode 100644 html/js/cytoscape-popper.js create mode 100644 html/js/cytoscape.min.js create mode 100644 html/js/easymde.min.js create mode 100644 html/js/layout-base.js create mode 100644 html/js/popper.core.js create mode 100644 html/js/purify.min.js create mode 100644 html/js/shim.min.js delete mode 100644 html/js/simplemde.min.js create mode 100644 html/js/tippy.js create mode 100755 html/js/weathermap-editor.js create mode 100644 html/pages/map_traffic.inc.php create mode 100644 html/pages/wmap.inc.php create mode 100644 html/weathermap.php create mode 100644 includes/discovery/neighbours/ospfv3-mib.inc.php create mode 100644 includes/discovery/ports/coriant-groove-mib.inc.php rename includes/discovery/sensors/{dksf-70-5-x-x-1.inc.php => dksf-70-mib.inc.php} (76%) create mode 100644 includes/discovery/sensors/egw4mib.inc.php create mode 100644 includes/discovery/sensors/pwtv1-mib.inc.php create mode 100644 includes/discovery/sensors/roomalert12s-mib.inc.php create mode 100644 includes/discovery/sensors/teracom-tcw241-mib.inc.php delete mode 100644 includes/discovery/storage/embedded-ngx-mib.inc.php delete mode 100644 includes/discovery/storage/gpfs-mib.inc.php delete mode 100644 includes/discovery/storage/isilon-mib.inc.php delete mode 100644 includes/discovery/storage/netapp-mib.inc.php delete mode 100644 includes/discovery/storage/nimble-mib.inc.php create mode 100644 includes/discovery/vlans/a3com-huawei-lswvlan-mib.inc.php create mode 100644 includes/entities/storage.inc.php create mode 100644 includes/polling/os/wisi-tangram.inc.php create mode 100644 includes/polling/ospf/ospf-mib.inc.php create mode 100644 includes/polling/ospf/ospfv3-mib.inc.php create mode 100644 includes/polling/ports/coriant-groove-mib.inc.php delete mode 100644 includes/polling/storage/embedded-ngx-mib.inc.php delete mode 100644 includes/polling/storage/gpfs-mib.inc.php delete mode 100644 includes/polling/storage/isilon-mib.inc.php delete mode 100644 includes/polling/storage/netapp-mib.inc.php delete mode 100644 includes/polling/storage/nimble-mib.inc.php create mode 100644 includes/templates/notification/zoom_markdown.tpl create mode 100644 includes/templates/test/notification_SYSLOG.json create mode 100644 includes/weathermap/.htaccess create mode 100644 includes/weathermap/CHANGES create mode 100644 includes/weathermap/COPYING create mode 100644 includes/weathermap/README.md create mode 100644 includes/weathermap/check-gdbug.php create mode 100644 includes/weathermap/check.php create mode 100755 includes/weathermap/configs/.htaccess create mode 100644 includes/weathermap/configs/example.conf create mode 100644 includes/weathermap/configs/index.php create mode 100644 includes/weathermap/configs/simple.conf create mode 100644 includes/weathermap/editor-resources/editor.css create mode 100644 includes/weathermap/editor-resources/exclamation.png create mode 100644 includes/weathermap/editor-resources/index.php create mode 100755 includes/weathermap/editor-resources/jquery-latest.min.js create mode 100755 includes/weathermap/editor.php create mode 100644 includes/weathermap/images/Cloud-Filled.png create mode 100644 includes/weathermap/images/Cloud-line.png create mode 100644 includes/weathermap/images/Firewall.png create mode 100644 includes/weathermap/images/HPMini.png create mode 100644 includes/weathermap/images/Host.png create mode 100644 includes/weathermap/images/PAD.png create mode 100644 includes/weathermap/images/Router.png create mode 100644 includes/weathermap/images/WorkgroupSwitch.png create mode 100644 includes/weathermap/images/application.png create mode 100644 includes/weathermap/images/application_side_list.png create mode 100644 includes/weathermap/images/application_xp_terminal.png create mode 100644 includes/weathermap/images/background1_950x625_197.jpg create mode 100644 includes/weathermap/images/blue-ball-64.png create mode 100644 includes/weathermap/images/blueboard1024.png create mode 100644 includes/weathermap/images/btn_recalc.png create mode 100644 includes/weathermap/images/bug.png create mode 100644 includes/weathermap/images/button_editgroups.png create mode 100644 includes/weathermap/images/button_settings.gif create mode 100644 includes/weathermap/images/bw_gradient_1024.png create mode 100644 includes/weathermap/images/comment.png create mode 100644 includes/weathermap/images/control_fastforward_blue.png create mode 100644 includes/weathermap/images/control_pause_blue.png create mode 100644 includes/weathermap/images/control_play_blue.png create mode 100644 includes/weathermap/images/control_rewind_blue.png create mode 100644 includes/weathermap/images/control_stop_blue.png create mode 100644 includes/weathermap/images/cross.png create mode 100644 includes/weathermap/images/emoticon_smile.png create mode 100644 includes/weathermap/images/emoticon_unhappy.png create mode 100644 includes/weathermap/images/error.png create mode 100644 includes/weathermap/images/exclamation.png create mode 100644 includes/weathermap/images/filecabinet.png create mode 100644 includes/weathermap/images/flag_blue.png create mode 100644 includes/weathermap/images/flag_green.png create mode 100644 includes/weathermap/images/flag_orange.png create mode 100644 includes/weathermap/images/flag_pink.png create mode 100644 includes/weathermap/images/flag_purple.png create mode 100644 includes/weathermap/images/flag_yellow.png create mode 100644 includes/weathermap/images/green-ball-64.png create mode 100644 includes/weathermap/images/grey-ball-64.png create mode 100644 includes/weathermap/images/hollow32.png create mode 100644 includes/weathermap/images/index.php create mode 100644 includes/weathermap/images/monitor.png create mode 100644 includes/weathermap/images/page_white.png create mode 100644 includes/weathermap/images/pencil.png create mode 100644 includes/weathermap/images/red-ball-64.png create mode 100644 includes/weathermap/images/s_tab_weathermap.gif create mode 100644 includes/weathermap/images/s_tab_weathermap_red.gif create mode 100644 includes/weathermap/images/tab_weathermap.gif create mode 100644 includes/weathermap/images/tab_weathermap_red.gif create mode 100644 includes/weathermap/images/tick.png create mode 100644 includes/weathermap/images/uk1024.png create mode 100644 includes/weathermap/images/updown_0.png create mode 100644 includes/weathermap/images/updown_1.png create mode 100644 includes/weathermap/images/updown_2.png create mode 100644 includes/weathermap/images/world.png create mode 100644 includes/weathermap/images/world_link.png create mode 100644 includes/weathermap/images/wrench.png create mode 100644 includes/weathermap/images/wrench_orange.png create mode 100644 includes/weathermap/images/yellow-ball-64.png create mode 100755 includes/weathermap/lib/.htaccess create mode 100644 includes/weathermap/lib/HTML_ImageMap.class.php create mode 100644 includes/weathermap/lib/WMLine.class.php create mode 100644 includes/weathermap/lib/WMPoint.class.php create mode 100644 includes/weathermap/lib/WMVector.class.php create mode 100644 includes/weathermap/lib/WeatherMap.functions.php create mode 100644 includes/weathermap/lib/WeatherMap.keywords.inc.php create mode 100644 includes/weathermap/lib/WeatherMapLink.class.php create mode 100644 includes/weathermap/lib/WeatherMapNode.class.php create mode 100644 includes/weathermap/lib/Weathermap.class.php create mode 100644 includes/weathermap/lib/database.php create mode 100644 includes/weathermap/lib/datasources/README.txt create mode 100644 includes/weathermap/lib/datasources/WeatherMapDataSource_cactihost.php create mode 100644 includes/weathermap/lib/datasources/WeatherMapDataSource_cactithold.php create mode 100755 includes/weathermap/lib/datasources/WeatherMapDataSource_dbsample.php create mode 100644 includes/weathermap/lib/datasources/WeatherMapDataSource_dsstats.php create mode 100644 includes/weathermap/lib/datasources/WeatherMapDataSource_external.php.disabled create mode 100644 includes/weathermap/lib/datasources/WeatherMapDataSource_fping.php create mode 100644 includes/weathermap/lib/datasources/WeatherMapDataSource_mrtg.php create mode 100644 includes/weathermap/lib/datasources/WeatherMapDataSource_obsdb.php create mode 100644 includes/weathermap/lib/datasources/WeatherMapDataSource_rrd.php create mode 100644 includes/weathermap/lib/datasources/WeatherMapDataSource_skeleton.php.txt create mode 100644 includes/weathermap/lib/datasources/WeatherMapDataSource_snmp.php create mode 100644 includes/weathermap/lib/datasources/WeatherMapDataSource_static.php create mode 100644 includes/weathermap/lib/datasources/WeatherMapDataSource_tabfile.php create mode 100644 includes/weathermap/lib/datasources/WeatherMapDataSource_time.php create mode 100644 includes/weathermap/lib/datasources/WeatherMapDataSource_wmdata.php create mode 100644 includes/weathermap/lib/ds-common.php create mode 100755 includes/weathermap/lib/editor.inc.php create mode 100644 includes/weathermap/lib/geometry.php create mode 100644 includes/weathermap/lib/index.php create mode 100644 includes/weathermap/lib/poller-common.php create mode 100644 includes/weathermap/lib/post/WeatherMapPostProcessorExample.php create mode 100644 includes/weathermap/lib/pre/WeatherMapPreProcessorExample.php create mode 100644 includes/weathermap/map-poller.php create mode 100644 includes/weathermap/overlib.js create mode 100755 includes/weathermap/random-bits/.htaccess create mode 100644 includes/weathermap/random-bits/README create mode 100755 includes/weathermap/random-bits/auto-overlib.pl create mode 100644 includes/weathermap/random-bits/bristle.php create mode 100644 includes/weathermap/random-bits/cacti-integrate.php create mode 100644 includes/weathermap/random-bits/cacti-mapper.php create mode 100644 includes/weathermap/random-bits/convert-to-dsstats.php create mode 100644 includes/weathermap/random-bits/index.php create mode 100644 includes/weathermap/random-bits/map-split.php create mode 100644 includes/weathermap/random-bits/map-tidyup.php create mode 100644 includes/weathermap/random-bits/suite-1.conf create mode 100644 includes/weathermap/random-bits/suite-1.png create mode 100644 includes/weathermap/random-bits/suite-2.conf create mode 100644 includes/weathermap/random-bits/suite-2.png create mode 100755 includes/weathermap/weathermap create mode 100644 libs/flight2/Engine.php create mode 100644 libs/flight2/Flight.php create mode 100644 libs/flight2/LICENSE create mode 100644 libs/flight2/README.md create mode 100644 libs/flight2/VERSION create mode 100644 libs/flight2/autoload.php create mode 100644 libs/flight2/core/Dispatcher.php create mode 100644 libs/flight2/core/Loader.php create mode 100644 libs/flight2/net/Request.php create mode 100644 libs/flight2/net/Response.php create mode 100644 libs/flight2/net/Route.php create mode 100644 libs/flight2/net/Router.php create mode 100644 libs/flight2/template/View.php create mode 100644 libs/flight2/util/Collection.php create mode 100644 libs/flight2/util/LegacyJsonSerializable.php create mode 100644 mibs/acksys/ACKSYS-MIB create mode 100644 mibs/acksys/ACKSYS-WLG-MIB create mode 100644 mibs/allot/ALLOT-MIB create mode 100644 mibs/allot/ALLOT-NX-MIB create mode 100644 mibs/arista/ARISTA-ASIC-COUNTERS-MIB create mode 100644 mibs/arista/ARISTA-TAPAGG-MIB create mode 100644 mibs/avtech/ROOMALERT12S-MIB delete mode 100644 mibs/bachmann/BACHMANN-BLUENET2-CAPABILITY-MIB create mode 100644 mibs/bachmann/RNX-UPDU-MIB create mode 100644 mibs/buffalo/BUFFALO-NAS-MIB create mode 100644 mibs/buffalo/BUFFALO-ROOT-MIB create mode 100644 mibs/cdata/CDATA-COMMON-SMI create mode 100644 mibs/cdata/CDATA-EPON-MIB create mode 100644 mibs/cdata/CDATA-GPON-MIB create mode 100644 mibs/cdata/CDATA-GPON-MIB2 create mode 100644 mibs/cdata/EDFA-oa-MIB create mode 100644 mibs/cdata/LTNET-COMMONINFO-MIB create mode 100644 mibs/cdata/LTNET-ROOT create mode 100644 mibs/cdata/NSCRTV-PON-TREE-EXT-MIB create mode 100644 mibs/cdata/VENDOR-COMMON-MIB create mode 100644 mibs/cisco/CISCO-TEMPERATURE-MIB create mode 100644 mibs/cisco/CISCO-URWB-MIB create mode 100644 mibs/cisco/EXALINK-FUSION-MIB create mode 100644 mibs/dell/DELLTRAPS-MIB create mode 100644 mibs/eltex/EMUX-MIB create mode 100644 mibs/eltex/EMUX-TRAPS-V1-MIB rename mibs/{extreme => enterasys}/EXTREME-NETSIGHT-ALARM-MIB (100%) create mode 100644 mibs/firebrick/FIREBRICK-BGP-MIB create mode 100644 mibs/firebrick/FIREBRICK-CPU-MIB create mode 100644 mibs/firebrick/FIREBRICK-IPSEC-MIB create mode 100644 mibs/firebrick/FIREBRICK-L2TP-MIB create mode 100644 mibs/firebrick/FIREBRICK-MONITORING create mode 100644 mibs/firebrick/FIREBRICK-RUNSTATS-MIB create mode 100644 mibs/firebrick/FIREBRICK-VOIP-MIB create mode 100644 mibs/fscom/GPON-MIB create mode 100644 mibs/fscom/IPPDU-MIB create mode 100644 mibs/fscom/MSTP-MIB create mode 100644 mibs/fscom/NGPON-MIB create mode 100644 mibs/fscom/NSCRTV-EPON-EPONLINKEDEOC-MGM-MIB create mode 100644 mibs/fscom/NSCRTV-EPON-ONU-MIB create mode 100644 mibs/fscom/NSCRTV-EPON-PON-PORT-MIB create mode 100644 mibs/fscom/NSCRTV-EPON-UNI-MIB rename mibs/{nimble => hp}/NIMBLE-MIB (100%) rename mibs/{nimble => hp}/NIMBLE-TRAP-MIB (52%) create mode 100644 mibs/ibm/IBM-PDU-MIB delete mode 100644 mibs/kyocera/KYOCERA-Private-MIB create mode 100644 mibs/microsoft/AFFIRMED-ALARM-MIB create mode 100644 mibs/microsoft/AFFIRMED-ALARMS-MIB create mode 100644 mibs/microsoft/AFFIRMED-ARP-MIB create mode 100644 mibs/microsoft/AFFIRMED-CHASSIS-MIB create mode 100644 mibs/microsoft/AFFIRMED-CLUSTER-MIB create mode 100644 mibs/microsoft/AFFIRMED-DATAPATH-MIB create mode 100644 mibs/microsoft/AFFIRMED-DATAPLANESTATS-MIB create mode 100644 mibs/microsoft/AFFIRMED-DATARECORD-MIB create mode 100644 mibs/microsoft/AFFIRMED-EMS-SNMP-TRAP-MIB create mode 100644 mibs/microsoft/AFFIRMED-EXTSTORAGE-MIB create mode 100644 mibs/microsoft/AFFIRMED-FM-MIB create mode 100644 mibs/microsoft/AFFIRMED-GEORED-MIB create mode 100644 mibs/microsoft/AFFIRMED-IM-MIB create mode 100644 mibs/microsoft/AFFIRMED-INFRASTRUCTURE-MIB create mode 100644 mibs/microsoft/AFFIRMED-IPDATAPATH-MIB create mode 100644 mibs/microsoft/AFFIRMED-IPPROTOCOLS-MIB create mode 100644 mibs/microsoft/AFFIRMED-LICENSE-MIB create mode 100644 mibs/microsoft/AFFIRMED-MME-TRAPS-MIB create mode 100644 mibs/microsoft/AFFIRMED-MSF-MIB create mode 100644 mibs/microsoft/AFFIRMED-MULTIACTIVEROUTING-MIB create mode 100644 mibs/microsoft/AFFIRMED-NETWORKCONTEXT-MIB create mode 100644 mibs/microsoft/AFFIRMED-PERFSTATMGMT-MIB create mode 100644 mibs/microsoft/AFFIRMED-PKI-MIB create mode 100644 mibs/microsoft/AFFIRMED-SCEFSUBSCRIBER-MIB create mode 100644 mibs/microsoft/AFFIRMED-SECURITY-MIB create mode 100644 mibs/microsoft/AFFIRMED-SERVICECONSTRUCT-MIB create mode 100644 mibs/microsoft/AFFIRMED-SERVICES-MIB create mode 100644 mibs/microsoft/AFFIRMED-SNMP-AN3000-TRAPS-MIB create mode 100644 mibs/microsoft/AFFIRMED-SNMP-DG create mode 100644 mibs/microsoft/AFFIRMED-SNMP-MIB create mode 100644 mibs/microsoft/AFFIRMED-SSF-MIB create mode 100644 mibs/microsoft/AFFIRMED-SUBSCRIBER-MIB create mode 100644 mibs/microsoft/AFFIRMED-SYSTEM-MIB create mode 100644 mibs/microsoft/AFFIRMED-TAM-MIB create mode 100644 mibs/microsoft/AFFIRMED-TEMS-NOTIFICATION-MIB create mode 100644 mibs/microsoft/AFFIRMED-TEMS-OBJECTS-MIB create mode 100644 mibs/microsoft/AFFIRMED-TEMS-SNMP-MIB create mode 100644 mibs/microsoft/AFFIRMED-TEMS-TC-MIB create mode 100644 mibs/microsoft/AFFIRMED-USERACCESS-MIB create mode 100644 mibs/microsoft/AFFIRMED-VTAP-MIB create mode 100644 mibs/microsoft/AFFIRMED-ZONE-MIB create mode 100644 mibs/monnit/EGW4MIB create mode 100644 mibs/monnit/MONNIT-EGW-MIB create mode 100644 mibs/monnit/POEXMIB create mode 100644 mibs/netping/DKSF-561-1-X-X-1 create mode 100644 mibs/netping/DKSF-57-1-X-X-1 delete mode 100644 mibs/netping/DKSF-70-3-X-X-1 delete mode 100644 mibs/netping/DKSF-70-4-X-X-1 delete mode 100644 mibs/netping/DKSF-70-5-X-X-1 delete mode 100644 mibs/netping/DKSF-70-6-X-X-1 create mode 100644 mibs/netping/DKSF-70-MIB delete mode 100644 mibs/netping/DKSF-707-1-X-X-1 create mode 100644 mibs/nscrtv/NSCRTV-FTTX-EPON-MIB create mode 100644 mibs/nscrtv/NSCRTV-FTTX-GPON-MIB create mode 100644 mibs/nti/ENVIROMUXMINI-MIB create mode 100644 mibs/nti/ENVMINILX create mode 100644 mibs/nti/IPDUS2 create mode 100644 mibs/nti/IPDUSX create mode 100644 mibs/powershield/PS-POWERSHIELD-MIB create mode 100644 mibs/powershield/voltronicMIB create mode 100644 mibs/powertek/PWTv1-MIB create mode 100644 mibs/qnap/NAS-ES-MIB create mode 100644 mibs/qnap/QTS-MIB create mode 100644 mibs/rfc/ADSL-DMT-LINE-MIB create mode 100644 mibs/rfc/IPSEC-ISAKMP-IKE-DOI-TC create mode 100644 mibs/rfc/IPSEC-POLICY-MIB create mode 100644 mibs/rfc/IPSEC-SA-MON-MIB create mode 100644 mibs/seagate/SEAGATESYSTEMTRAP-MIB create mode 100644 mibs/sigur/SIGUR-MIB create mode 100644 mibs/smartoptics/DCP-OCH-MIB create mode 100644 mibs/smartoptics/DCP-TOPOLOGY-MIB create mode 100644 mibs/tfortis/FORT-TELECOM-MIB create mode 100644 mibs/unitrends/UNITRENDS-SNMP create mode 100644 mibs/vmware/VMWARE-HZECC-AGENTCAP-MIB create mode 100644 mibs/vmware/VMWARE-HZECC-EVENT-MIB create mode 100644 mibs/vmware/VMWARE-NSX-AGENTCAP-MIB create mode 100644 mibs/vmware/VMWARE-NSX-MIB create mode 100644 mibs/wisi/WISI-GTMODULES-MIB create mode 100644 mibs/wisi/WISI-GTSENSORS-MIB create mode 100644 mibs/wisi/WISI-GTSETTINGS-MIB create mode 100644 mibs/wisi/WISI-ROOT-MIB create mode 100644 mibs/wisi/WISI-TANGRAM-MIB create mode 100644 notifications.php create mode 100644 scripts/systemd/observium-notifications.service create mode 100644 scripts/systemd/observium-notifications.timer create mode 100644 update/466.sql create mode 100644 update/467.sql create mode 100644 update/468.sql create mode 100644 update/469.sql create mode 100644 update/470.sql create mode 100644 update/471.sql create mode 100644 update/472.sql create mode 100644 update/473.sql create mode 100644 update/474.sql create mode 100644 update/475.sql create mode 100644 update/476.sql create mode 100644 update/477.sql diff --git a/.phpcs.xml b/.phpcs.xml deleted file mode 100644 index 74cb6a2c..00000000 --- a/.phpcs.xml +++ /dev/null @@ -1,213 +0,0 @@ - - - Observium rules for PHP CodeSniffer - - - - - . - - - - - */attic/* - - */devel/* - - */vendor/* - - */weathermap/* - - tests/php-compatibility/* - - - - - - - - - - - - - - - - irc\.php$ - */weathermap/* - - - - - - - - - - - - - - - - - libs/random_compat/* - - - libs/random_compat/* - - - libs/random_compat/* - - - libs/random_compat/* - libs/pear/Crypt/CHAP\.php - - - libs/random_compat/* - libs/pear/Crypt/CHAP\.php - - - - - libs/pear/Crypt/CHAP\.php - - - libs/pear/Crypt/CHAP\.php - - - libs/pear/Crypt/CHAP\.php - - - libs/pear/Crypt/CHAP\.php - - - libs/pear/Crypt/CHAP\.php - - - libs/pear/Crypt/CHAP\.php - - - libs/pear/Crypt/CHAP\.php - - - libs/pear/Crypt/CHAP\.php - - - - - libs/pear/Net/Socket\.php - - - libs/pear/Net/Socket\.php - - - libs/pear/Net/Socket\.php - - - - - includes/common\.inc\.php - libs/Fabiang/Xmpp/* - - - tests/* - libs/pear/* - - - libs/pear/Mail/mime* - - - includes/definitions\.inc\.php - - - includes/db/mysql\.inc\.php - - - html/includes/authenticate\.inc\.php - - - tests/IncludesEncryptTest\.php - includes/encrypt\.inc\.php - - - tests/IncludesEncryptTest\.php - includes/encrypt\.inc\.php - - - tests/IncludesEncryptTest\.php - includes/encrypt\.inc\.php - - - tests/IncludesEncryptTest\.php - includes/encrypt\.inc\.php - - - tests/IncludesEncryptTest\.php - includes/encrypt\.inc\.php - - - tests/IncludesEncryptTest\.php - includes/encrypt\.inc\.php - - - tests/IncludesEncryptTest\.php - includes/encrypt\.inc\.php - libs/random_compat/* - libs/pear/Crypt/CHAP\.php - - - tests/IncludesEncryptTest\.php - includes/encrypt\.inc\.php - libs/pear/Crypt/CHAP\.php - - - tests/IncludesEncryptTest\.php - includes/encrypt\.inc\.php - libs/pear/Crypt/CHAP\.php - - - tests/IncludesEncryptTest\.php - includes/encrypt\.inc\.php - libs/pear/Crypt/CHAP\.php - - - tests/IncludesEncryptTest\.php - includes/encrypt\.inc\.php - libs/pear/Crypt/CHAP\.php - - - html/includes/collectd/functions.php - - - html/includes/authenticate.inc.php - - - - html/includes/authentication/ldap.inc.php - - - html/includes/authentication/ldap.inc.php - - - html/includes/authentication/ldap.inc.php - - - html/includes/authentication/ldap.inc.php - - - html/includes/authentication/ldap.inc.php - - - includes/entities/sensor.inc.php - - - includes/db/mysqli.inc.php - - - - - - diff --git a/VERSION b/VERSION index fdeb1e34..aa1e3452 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Observium CE 22.5 +Observium CE 22.12 diff --git a/alerter.php b/alerter.php index d7ddedfb..540b546c 100755 --- a/alerter.php +++ b/alerter.php @@ -7,13 +7,13 @@ * * @package observium * @subpackage cli - * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2021 Observium Limited + * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2022 Observium Limited * */ chdir(dirname($argv[0])); -$options = getopt("h:i:m:n:p:dqrsV"); +$options = getopt("h:p:dqrsV"); include("includes/sql-config.inc.php"); @@ -40,7 +40,7 @@ if ($options['h'] === "all") { $where = " "; $doing = "all"; } elseif ($options['h']) { - $params = array(); + $params = []; if (is_numeric($options['h'])) { $where = "AND `device_id` = ?"; $doing = $options['h']; @@ -52,25 +52,26 @@ if ($options['h'] === "all") { } } +if (isset($options['p'])) { + print_cli_heading("%WConstrained to poller partition id ".$options['p']); + $where .= ' AND `poller_id` = ?'; + $params[] = $options['p']; +} + if (!$where) { print_message("%n USAGE: -$scriptname [-drqV] [-i instances] [-n number] [-m module] [-h device] +$scriptname [-drqV] [-p poller_id] [-h device] EXAMPLE: -h | Poll single device --h odd Poll odd numbered devices (same as -i 2 -n 0) --h even Poll even numbered devices (same as -i 2 -n 1) -h all Poll all devices --h new Poll all devices that have not had a discovery run before --i -n Poll as instance of - Instances start at 0. 0-3 for -n 4 +-p Poll for specific poller_id OPTIONS: - -h Device hostname, id or key odd/even/all/new. - -i Poll instance. - -n Poll number. + -h Device hostname, id or hostname or keys all. + -p Poller ID. -s Sends alerts even if they have already been sent. -q Quiet output. -V Show version and exit. @@ -79,7 +80,6 @@ DEBUGGING OPTIONS: -r Do not create or update RRDs -d Enable debugging output. -dd More verbose debugging output. - -m Specify module(s) (separated by commas) to be run. %rInvalid arguments!%n", 'color'); exit; @@ -100,14 +100,16 @@ $_SESSION['userlevel'] = 10; //$params[] = $config['poller_id']; $query = "SELECT * FROM `devices` WHERE `disabled` = 0 $where ORDER BY `device_id` ASC"; -foreach (dbFetch($query, $params) as $device) { +foreach (dbFetchRows($query, $params) as $device) { humanize_device($device); process_alerts($device); - process_notifications(array('device_id' => $device['device_id'])); // Send all notifications (also for syslog from queue) + if ($config['poller-wrapper']['notifications'] || $spam) { + process_notifications([ 'device_id' => $device['device_id'] ]); // Send all notifications (also for syslog from queue) + } - dbUpdate(array('last_alerter' => array('NOW()')), 'devices', '`device_id` = ?', array($device['device_id'])); + dbUpdate([ 'last_alerter' => [ 'NOW()' ] ], 'devices', '`device_id` = ?', [ $device['device_id'] ]); } diff --git a/discovery.php b/discovery.php index 24182df2..af68dfe3 100755 --- a/discovery.php +++ b/discovery.php @@ -122,14 +122,13 @@ if (isset($options['h'])) { } } -if (isset($options['i']) && $options['i'] && isset($options['n'])) { +if (isset($options['i'], $options['n']) && $options['i']) { $where .= ' AND MOD(device_id,' . $options['i'] . ') = ?'; $params[] = $options['n']; $doing = $options['n'] . '/' . $options['i']; } -if (!$where && !$options['u'] && !isset($options['a'])) -{ +if (!$where && !$options['u'] && !isset($options['a'])) { print_message("%n USAGE: $scriptname [-dquV] [-i instances] [-n number] [-m module] [-h device] @@ -169,10 +168,19 @@ if ($config['version_check'] && ($options['h'] !== 'new' || $options['u'])) { if (!$where) { // Only update Group/Alert tables if (isset($options['a'])) { - $silent = isset($options['q']); - // Not exist in CE - if (function_exists('update_group_tables')) { update_group_tables($silent); } - if (function_exists('update_alert_tables')) { update_alert_tables($silent); } + + if (OBS_DISTRIBUTED && function_exists('run_action_queue')) { + //run_action_queue('device_add'); + //run_action_queue('device_rename'); + //run_action_queue('device_delete'); + + // Update alert and group tables + run_action_queue('tables_update'); + } else { + $silent = isset($options['q']); + if (function_exists('update_group_tables')) { update_group_tables($silent); } // Not exist in CE + if (function_exists('update_alert_tables')) { update_alert_tables($silent); } + } } exit; @@ -215,11 +223,10 @@ if (($discovered_devices && !isset($options['m'])) || isset($options['a'])) { if (OBS_DISTRIBUTED && !isset($options['a']) && function_exists('add_action_queue') && $action_id = add_action_queue('tables_update', 'discovery', [ 'silent' => $silent ])) { print_message("Update alert and group tables added to queue [$action_id]."); - //log_event("Device with hostname '$hostname' added to queue [$action_id] for addition on remote Poller [${vars['poller_id']}].", NULL, 'info', NULL, 7); - } elseif (OBSERVIUM_EDITION !== 'community') { - // Not exist in CE - update_group_tables($silent); - update_alert_tables($silent); + //log_event("Device with hostname '$hostname' added to queue [$action_id] for addition on remote Poller [{$vars['poller_id']}].", NULL, 'info', NULL, 7); + } else { + if (function_exists('update_group_tables')) { update_group_tables($silent); } // Not exist in CE + if (function_exists('update_alert_tables')) { update_alert_tables($silent); } } } diff --git a/html/ajax/actions.php b/html/ajax/actions.php index b3729718..7c9c0fbb 100644 --- a/html/ajax/actions.php +++ b/html/ajax/actions.php @@ -6,7 +6,7 @@ * * @package observium * @subpackage ajax - * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2021 Observium Limited + * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2022 Observium Limited * */ @@ -235,13 +235,18 @@ switch ($vars['action']) { exit(); } - $widget = dbFetchRow("SELECT * FROM `dash_widgets` WHERE widget_id = ?", array($vars['widget_id'])); + $widget = dbFetchRow("SELECT * FROM `dash_widgets` WHERE `widget_id` = ?", array($vars['widget_id'])); $widget['widget_config'] = safe_json_decode($widget['widget_config']); // Verify config value applies to this widget here + $default_on = [ 'legend' ]; + if (isset($vars['config_field']) && isset($vars['config_value'])) { - if (empty($vars['config_value'])) { + if ( empty($vars['config_value']) || + (in_array($vars['config_field'], $default_on) && get_var_true($vars['config_value'])) || + (!in_array($vars['config_field'], $default_on) && get_var_false($vars['config_value'])) ) { + // Just unset the value if it's empty or it's a default value. unset($widget['widget_config'][$vars['config_field']]); } else { $widget['widget_config'][$vars['config_field']] = $vars['config_value']; @@ -265,7 +270,7 @@ switch ($vars['action']) { // Validate CSRF Token //r($vars); $json = ''; - if (!str_contains_array($vars['action'], [ 'widget', 'dash' ]) && // widget & dashboard currently not send request token + if (!str_contains_array($vars['action'], [ 'widget', 'dash', 'settings_user' ]) && // widget & dashboard currently not send request token !request_token_valid($vars, $json)) { $json = safe_json_decode($json); $json['reload'] = TRUE; diff --git a/html/ajax/actions/edit_widget.inc.php b/html/ajax/actions/edit_widget.inc.php index d248c3ab..0fde55d2 100644 --- a/html/ajax/actions/edit_widget.inc.php +++ b/html/ajax/actions/edit_widget.inc.php @@ -6,13 +6,13 @@ * * @package observium * @subpackage ajax - * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2021 Observium Limited + * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2022 Observium Limited * */ if ($readonly) { return; } // Currently edit allowed only for 7+ -$widget = dbFetchRow("SELECT * FROM `dash_widgets` WHERE widget_id = ?", array($vars['widget_id'])); +$widget = dbFetchRow("SELECT * FROM `dash_widgets` WHERE `widget_id` = ?", [ $vars['widget_id'] ]); $widget['widget_config'] = safe_json_decode($widget['widget_config']); @@ -22,11 +22,111 @@ switch ($widget['widget_type']) { if (safe_count($widget['widget_config'])) { +// echo ' +//
+// Title +//
+// '; + + //r($widget['widget_config']); + + //r(isset($widget['widget_config']['legend']) && $widget['widget_config']['legend'] === 'no'); + + $modal_args = [ + 'id' => 'modal-edit_widget_' . $widget['widget_id'], + 'title' => 'Configure Widget', + //'hide' => TRUE, + //'fade' => TRUE, + //'role' => 'dialog', + //'class' => 'modal-md', + ]; + + $form = [ + 'form_only' => TRUE, // Do not add modal open/close divs (it's generated outside) + 'type' => 'horizontal', + 'id' => 'edit_widget_' . $widget['widget_id'], + 'userlevel' => 7, // Minimum user level for display form + 'modal_args' => $modal_args, // !!! This generate modal specific form + //'help' => 'This will completely delete the rule and all associations and history.', + 'class' => '', // Clean default box class! + //'url' => generate_url([ 'page' => 'syslog_rules' ]), + 'onsubmit' => "return false", + ]; + $form['fieldset']['body'] = [ 'class' => 'modal-body' ]; // Required this class for modal body! + $form['fieldset']['footer'] = [ 'class' => 'modal-footer' ]; // Required this class for modal footer! + + $form['row'][1]['widget-config-title'] = [ + 'type' => 'text', + 'fieldset' => 'body', + 'name' => 'Title', + 'placeholder' => 'Graph Title', + 'class' => 'input-xlarge', + 'attribs' => [ + 'data-id' => $widget['widget_id'], + 'data-field' => 'title', + 'data-type' => 'text' + ], + 'value' => $widget['widget_config']['title'] + ]; + $form['row'][2]['widget-config-legend'] = [ + 'type' => 'checkbox', + 'fieldset' => 'body', + 'name' => 'Show Legend', + //'placeholder' => 'Yes, please delete this rule.', + //'onchange' => "javascript: toggleAttrib('disabled', 'delete_button_".$la['la_id']."'); showDiv(!this.checked, 'warning_".$la['la_id']."_div');", + 'attribs' => [ + 'data-id' => $widget['widget_id'], + 'data-field' => 'legend', + 'data-type' => 'checkbox' + ], + 'value' => safe_empty($widget['widget_config']['legend']) ? 'yes' : $widget['widget_config']['legend'] //'legend' + ]; + + + $form['row'][8]['close'] = [ + 'type' => 'submit', + 'fieldset' => 'footer', + 'div_class' => '', // Clean default form-action class! + 'name' => 'Close', + 'icon' => '', + 'attribs' => [ + 'data-dismiss' => 'modal', + 'aria-hidden' => 'true' + ] + ]; + + echo generate_form_modal($form); + unset($form); + + /* echo ' -
- Title -
- '; +
+
+
+ +
+ +
+
+ +
+ +
+ +
+
+
+ + + +
'; + */ + } else { diff --git a/html/ajax/actions/settings_edit.inc.php b/html/ajax/actions/settings_edit.inc.php index ccd514af..b8f11dbb 100644 --- a/html/ajax/actions/settings_edit.inc.php +++ b/html/ajax/actions/settings_edit.inc.php @@ -6,7 +6,7 @@ * * @package observium * @subpackage web - * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2021 Observium Limited + * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2022 Observium Limited * */ @@ -34,7 +34,7 @@ $updates = 0; // Set fields that were submitted with custom value if (safe_count($sets)) { - $query = 'SELECT * FROM `users_prefs` WHERE `user_id` = ?' . generate_query_values(array_keys($sets), 'pref'); + $query = 'SELECT * FROM `users_prefs` WHERE `user_id` = ?' . generate_query_values_and(array_keys($sets), 'pref'); // Fetch current rows in config file so we know which one to UPDATE and which one to INSERT $in_db = []; foreach (dbFetchRows($query, [ $user_id ]) as $row) { @@ -52,7 +52,7 @@ if (safe_count($sets)) { // Delete fields that were reset to default if (safe_count($deletes)) { - dbDelete('users_prefs', '`user_id` = ? ' . generate_query_values($deletes, 'pref'), [ $user_id ]); + dbDelete('users_prefs', '`user_id` = ? ' . generate_query_values_and($deletes, 'pref'), [ $user_id ]); $updates++; } diff --git a/html/ajax/actions/settings_user.inc.php b/html/ajax/actions/settings_user.inc.php new file mode 100644 index 00000000..d2cc7fe7 --- /dev/null +++ b/html/ajax/actions/settings_user.inc.php @@ -0,0 +1,66 @@ +', '|', $vars['setting'])) { + case "theme": + case "web_theme_default": + $pref = 'web_theme_default'; + if ($vars['value'] === 'reset') { + session_unset_var("theme"); + if ($config['web_theme_default'] === 'system') { + // Override default + session_unset_var("theme_default"); + } + + if (del_user_pref($_SESSION['user_id'], $pref)) { + print_json_status('ok', 'Theme reset.'); + } + } elseif (isset($config['themes'][$vars['value']]) || $vars['value'] === 'system') { + if (set_user_pref($_SESSION['user_id'], $pref, serialize($vars['value']))) { + print_json_status('ok', 'Theme set.'); + } + } else { + print_json_status('failed', 'Invalid theme.'); + } + break; + + case "big_graphs": + $pref = 'graphs|size'; + if (set_user_pref($_SESSION['user_id'], $pref, serialize('big'))) { + print_json_status('ok', 'Big graphs set.'); + session_unset_var("big_graphs"); // clear old + } + //session_set_var("big_graphs", TRUE); + //print_json_status('ok', 'Big graphs set.'); + break; + + case "normal_graphs": + $pref = 'graphs|size'; + if (set_user_pref($_SESSION['user_id'], $pref, serialize('normal'))) { + print_json_status('ok', 'Normal graphs set.'); + session_unset_var("big_graphs"); // clear old + } + //session_unset_var("big_graphs"); + //print_json_status('ok', 'Small graphs set.'); + break; + + case "sensors|web_measured_compact": + // BOOL values + $pref = $vars['setting']; + if (set_user_pref($_SESSION['user_id'], $pref, serialize(get_var_true($vars['value'])))) { + print_json_status('ok', 'Setting was set.', [ 'reload' => TRUE ]); + } + break; + +} +// EOF diff --git a/html/ajax/entity_popup.php b/html/ajax/entity_popup.php index 0769f1d9..eda71084 100644 --- a/html/ajax/entity_popup.php +++ b/html/ajax/entity_popup.php @@ -21,10 +21,12 @@ if (!$_SESSION['authenticated']) { print_error('Session expired, please log in a ob_start(); -$vars = get_vars(); +$vars = get_vars([ 'JSON', 'POST', 'GET' ]); $vars['page'] = "popup"; +if(isset($vars['debug'])) { r($vars); } + switch ($vars['entity_type']) { case "port": if (is_numeric($vars['entity_id']) && (port_permitted($vars['entity_id']))) { @@ -35,6 +37,23 @@ switch ($vars['entity_type']) { } break; + case "link": + if (is_numeric($vars['entity_id_a']) && (port_permitted($vars['entity_id_a']))) { + $port = get_port_by_id($vars['entity_id_a']); + echo generate_port_popup($port); + } else { + print_warning("You are not permitted to view this port."); + } + + if (is_numeric($vars['entity_id_b']) && (port_permitted($vars['entity_id_b']))) { + $port = get_port_by_id($vars['entity_id_b']); + echo generate_port_popup($port, '','none'); // suppress graph for b side of link + } else { + print_warning("You are not permitted to view this port."); + } + break; + + case "device": if (is_numeric($vars['entity_id']) && device_permitted($vars['entity_id'])) { $device = device_by_id_cache($vars['entity_id']); @@ -53,7 +72,6 @@ switch ($vars['entity_type']) { } break; - // FIXME : mac is not an observium entity. This should go elsewhere! case "mac": if (preg_match('/^' . OBS_PATTERN_MAC . '$/i', $vars['entity_id'])) { $mac = format_mac($vars['entity_id']); diff --git a/html/ajax/input.php b/html/ajax/input.php index 0f82d641..d71ce116 100644 --- a/html/ajax/input.php +++ b/html/ajax/input.php @@ -6,7 +6,7 @@ * * @package observium * @subpackage ajax - * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2021 Observium Limited + * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2022 Observium Limited * */ @@ -48,11 +48,11 @@ if ($cache_key && $options = get_cache_session($cache_key)) { list($ip_version) = explode('_', $vars['field']); $query_permitted = generate_query_permitted('ports'); $network_permitted = dbFetchColumn('SELECT DISTINCT(`' . $ip_version . '_network_id`) FROM `' . $ip_version . '_addresses` WHERE 1' . $query_permitted); - $query = 'SELECT `' . $ip_version . '_network` FROM `' . $ip_version . '_networks` WHERE 1 ' . generate_query_values($network_permitted, $ip_version . '_network_id'); + $query = 'SELECT `' . $ip_version . '_network` FROM `' . $ip_version . '_networks` WHERE 1 ' . generate_query_values_and($network_permitted, $ip_version . '_network_id'); if (!safe_empty($vars['query'])) { //$query .= ' AND `' . $ip_version . '_network` LIKE ?'; //$params[] = '%' . $vars['query'] . '%'; - $query .= generate_query_values($vars['query'], $vars['field'], '%LIKE%'); + $query .= generate_query_values_and($vars['query'], $vars['field'], '%LIKE%'); } $query .= ' ORDER BY `' . $ip_version . '_network`;'; //print_vars($query); @@ -69,7 +69,7 @@ if ($cache_key && $options = get_cache_session($cache_key)) { //$query_permitted = generate_query_permitted(); $query = 'SELECT DISTINCT `program` FROM `syslog`'; if (is_intnum($vars['device_id'])) { - $query .= ' WHERE ' . generate_query_values($vars['device_id'], 'device_id', NULL, FALSE); + $query .= ' WHERE ' . generate_query_values_ng($vars['device_id'], 'device_id'); } $array_filter = TRUE; // Search query string in array instead sql query (when this faster) break; @@ -86,7 +86,7 @@ if ($cache_key && $options = get_cache_session($cache_key)) { $query .= ' AND (`' . $column . '` LIKE ? OR `astext` LIKE ?)'; $params[] = '%' . $vars['query'] . '%'; $params[] = '%' . $vars['query'] . '%'; - //$query .= generate_query_values($vars['query'], $vars['field'], '%LIKE%'); + //$query .= generate_query_values_and($vars['query'], $vars['field'], '%LIKE%'); } break; @@ -100,7 +100,7 @@ if ($cache_key && $options = get_cache_session($cache_key)) { $query_permitted = generate_query_permitted('devices'); $query = 'SELECT DISTINCT `' . $column . '` FROM `bgpPeers` WHERE 1 ' . $query_permitted; if (!safe_empty($vars['query'])) { - $query .= generate_query_values($vars['query'], $column, '%LIKE%'); + $query .= generate_query_values_and($vars['query'], $column, '%LIKE%'); } break; diff --git a/html/ajax/search.php b/html/ajax/search.php index a42acf03..72a689d6 100644 --- a/html/ajax/search.php +++ b/html/ajax/search.php @@ -26,8 +26,7 @@ $query_limit = 8; // Limit per query $vars = get_vars([ 'POST', 'GET' ]); // Is there a POST/GET query string? -if (isset($vars['queryString'])) -{ +if (isset($vars['queryString'])) { $queryString = trim($vars['queryString']); // Is the string length greater than 0? diff --git a/html/ajax/widget.php b/html/ajax/widget.php index db54d8d2..bfc7c158 100644 --- a/html/ajax/widget.php +++ b/html/ajax/widget.php @@ -169,8 +169,14 @@ function print_dash_mod ($mod) echo '
'; echo ' '; echo '
'; - print_syslogs(array('short' => TRUE, 'pagesize' => ($height - 36) / 26, - 'priority' => $config['frontpage']['syslog']['priority'])); + + $syslog_vars = $mod['vars']; + + $syslog_vars = array_merge($syslog_vars, ['short' => TRUE, 'pagesize' => ($height - 36) / 26, + 'priority' => $config['frontpage']['syslog']['priority']]); + + print_syslogs($syslog_vars); + echo '
'; echo '
'; break; @@ -372,7 +378,7 @@ function print_dash_graph($mod, $width, $height) { if ($graph_array['width'] > 350) { $graph_array['height'] -= 6; - } // RRD graphs > 350px are 6 px wider because of larger legend font + } // RRD graphs > 350px are 6 px taller because of larger legend font $title_div = 'top:0px; left: 0px; padding: 4px; border-top-left-radius: 4px; border: 1px solid #e5e5e5; border-left: none; border-top: none; background-color: rgba(255, 255,255, 0.75); '; $title_div = 'widget-title'; @@ -411,7 +417,10 @@ function print_dash_graph($mod, $width, $height) { //$graph_array['format'] = 'png'; //$graph_array['img_id'] = generate_random_string(5); - $graph_array['legend'] = 'no'; + //$graph_array['legend'] = 'no'; + + $graph_array['rigid_height'] = 'yes'; // Force height of graph to be same as height of graph_type. + $graph_array['class'] = 'image-refresh'; $graph = generate_graph_tag($graph_array, TRUE); @@ -419,6 +428,7 @@ function print_dash_graph($mod, $width, $height) { $link_array = $graph_array; $link_array['page'] = "graphs"; unset($link_array['graph_only']); + unset($link_array['rigid_height']); unset($link_array['height'], $link_array['width']); $link = generate_url($link_array); diff --git a/html/css/bootstrap.css b/html/css/bootstrap.css index 56de8a23..257d066a 100644 --- a/html/css/bootstrap.css +++ b/html/css/bootstrap.css @@ -10404,7 +10404,7 @@ a.badge:focus { box-shadow: none; padding: 0; } -.qtip-content { +.qtip-content, .tippy-content { position: relative; padding: 5px 9px; overflow: hidden; @@ -10503,7 +10503,7 @@ a.badge:focus { * Tested with IE 8, IE 9, Chrome 18, Firefox 9, Opera 11. * Does not work with IE 7. */ -.qtip-bootstrap { +.qtip-bootstrap, .tippy-box { /** Taken from Bootstrap body */ font-size: 14px; line-height: 20px; diff --git a/html/css/easymde.min.css b/html/css/easymde.min.css new file mode 100644 index 00000000..c12d90a4 --- /dev/null +++ b/html/css/easymde.min.css @@ -0,0 +1,7 @@ +/** + * easymde v2.17.0 + * Copyright Jeroen Akkerman + * @link https://github.com/ionaru/easy-markdown-editor + * @license MIT + */ +.CodeMirror{font-family:monospace;height:300px;color:#000;direction:ltr}.CodeMirror-lines{padding:4px 0}.CodeMirror pre.CodeMirror-line,.CodeMirror pre.CodeMirror-line-like{padding:0 4px}.CodeMirror-gutter-filler,.CodeMirror-scrollbar-filler{background-color:#fff}.CodeMirror-gutters{border-right:1px solid #ddd;background-color:#f7f7f7;white-space:nowrap}.CodeMirror-linenumber{padding:0 3px 0 5px;min-width:20px;text-align:right;color:#999;white-space:nowrap}.CodeMirror-guttermarker{color:#000}.CodeMirror-guttermarker-subtle{color:#999}.CodeMirror-cursor{border-left:1px solid #000;border-right:none;width:0}.CodeMirror div.CodeMirror-secondarycursor{border-left:1px solid silver}.cm-fat-cursor .CodeMirror-cursor{width:auto;border:0!important;background:#7e7}.cm-fat-cursor div.CodeMirror-cursors{z-index:1}.cm-fat-cursor .CodeMirror-line::selection,.cm-fat-cursor .CodeMirror-line>span::selection,.cm-fat-cursor .CodeMirror-line>span>span::selection{background:0 0}.cm-fat-cursor .CodeMirror-line::-moz-selection,.cm-fat-cursor .CodeMirror-line>span::-moz-selection,.cm-fat-cursor .CodeMirror-line>span>span::-moz-selection{background:0 0}.cm-fat-cursor{caret-color:transparent}@-moz-keyframes blink{50%{background-color:transparent}}@-webkit-keyframes blink{50%{background-color:transparent}}@keyframes blink{50%{background-color:transparent}}.cm-tab{display:inline-block;text-decoration:inherit}.CodeMirror-rulers{position:absolute;left:0;right:0;top:-50px;bottom:0;overflow:hidden}.CodeMirror-ruler{border-left:1px solid #ccc;top:0;bottom:0;position:absolute}.cm-s-default .cm-header{color:#00f}.cm-s-default .cm-quote{color:#090}.cm-negative{color:#d44}.cm-positive{color:#292}.cm-header,.cm-strong{font-weight:700}.cm-em{font-style:italic}.cm-link{text-decoration:underline}.cm-strikethrough{text-decoration:line-through}.cm-s-default .cm-keyword{color:#708}.cm-s-default .cm-atom{color:#219}.cm-s-default .cm-number{color:#164}.cm-s-default .cm-def{color:#00f}.cm-s-default .cm-variable-2{color:#05a}.cm-s-default .cm-type,.cm-s-default .cm-variable-3{color:#085}.cm-s-default .cm-comment{color:#a50}.cm-s-default .cm-string{color:#a11}.cm-s-default .cm-string-2{color:#f50}.cm-s-default .cm-meta{color:#555}.cm-s-default .cm-qualifier{color:#555}.cm-s-default .cm-builtin{color:#30a}.cm-s-default .cm-bracket{color:#997}.cm-s-default .cm-tag{color:#170}.cm-s-default .cm-attribute{color:#00c}.cm-s-default .cm-hr{color:#999}.cm-s-default .cm-link{color:#00c}.cm-s-default .cm-error{color:red}.cm-invalidchar{color:red}.CodeMirror-composing{border-bottom:2px solid}div.CodeMirror span.CodeMirror-matchingbracket{color:#0b0}div.CodeMirror span.CodeMirror-nonmatchingbracket{color:#a22}.CodeMirror-matchingtag{background:rgba(255,150,0,.3)}.CodeMirror-activeline-background{background:#e8f2ff}.CodeMirror{position:relative;overflow:hidden;background:#fff}.CodeMirror-scroll{overflow:scroll!important;margin-bottom:-50px;margin-right:-50px;padding-bottom:50px;height:100%;outline:0;position:relative;z-index:0}.CodeMirror-sizer{position:relative;border-right:50px solid transparent}.CodeMirror-gutter-filler,.CodeMirror-hscrollbar,.CodeMirror-scrollbar-filler,.CodeMirror-vscrollbar{position:absolute;z-index:6;display:none;outline:0}.CodeMirror-vscrollbar{right:0;top:0;overflow-x:hidden;overflow-y:scroll}.CodeMirror-hscrollbar{bottom:0;left:0;overflow-y:hidden;overflow-x:scroll}.CodeMirror-scrollbar-filler{right:0;bottom:0}.CodeMirror-gutter-filler{left:0;bottom:0}.CodeMirror-gutters{position:absolute;left:0;top:0;min-height:100%;z-index:3}.CodeMirror-gutter{white-space:normal;height:100%;display:inline-block;vertical-align:top;margin-bottom:-50px}.CodeMirror-gutter-wrapper{position:absolute;z-index:4;background:0 0!important;border:none!important}.CodeMirror-gutter-background{position:absolute;top:0;bottom:0;z-index:4}.CodeMirror-gutter-elt{position:absolute;cursor:default;z-index:4}.CodeMirror-gutter-wrapper ::selection{background-color:transparent}.CodeMirror-gutter-wrapper ::-moz-selection{background-color:transparent}.CodeMirror-lines{cursor:text;min-height:1px}.CodeMirror pre.CodeMirror-line,.CodeMirror pre.CodeMirror-line-like{-moz-border-radius:0;-webkit-border-radius:0;border-radius:0;border-width:0;background:0 0;font-family:inherit;font-size:inherit;margin:0;white-space:pre;word-wrap:normal;line-height:inherit;color:inherit;z-index:2;position:relative;overflow:visible;-webkit-tap-highlight-color:transparent;-webkit-font-variant-ligatures:contextual;font-variant-ligatures:contextual}.CodeMirror-wrap pre.CodeMirror-line,.CodeMirror-wrap pre.CodeMirror-line-like{word-wrap:break-word;white-space:pre-wrap;word-break:normal}.CodeMirror-linebackground{position:absolute;left:0;right:0;top:0;bottom:0;z-index:0}.CodeMirror-linewidget{position:relative;z-index:2;padding:.1px}.CodeMirror-rtl pre{direction:rtl}.CodeMirror-code{outline:0}.CodeMirror-gutter,.CodeMirror-gutters,.CodeMirror-linenumber,.CodeMirror-scroll,.CodeMirror-sizer{-moz-box-sizing:content-box;box-sizing:content-box}.CodeMirror-measure{position:absolute;width:100%;height:0;overflow:hidden;visibility:hidden}.CodeMirror-cursor{position:absolute;pointer-events:none}.CodeMirror-measure pre{position:static}div.CodeMirror-cursors{visibility:hidden;position:relative;z-index:3}div.CodeMirror-dragcursors{visibility:visible}.CodeMirror-focused div.CodeMirror-cursors{visibility:visible}.CodeMirror-selected{background:#d9d9d9}.CodeMirror-focused .CodeMirror-selected{background:#d7d4f0}.CodeMirror-crosshair{cursor:crosshair}.CodeMirror-line::selection,.CodeMirror-line>span::selection,.CodeMirror-line>span>span::selection{background:#d7d4f0}.CodeMirror-line::-moz-selection,.CodeMirror-line>span::-moz-selection,.CodeMirror-line>span>span::-moz-selection{background:#d7d4f0}.cm-searching{background-color:#ffa;background-color:rgba(255,255,0,.4)}.cm-force-border{padding-right:.1px}@media print{.CodeMirror div.CodeMirror-cursors{visibility:hidden}}.cm-tab-wrap-hack:after{content:''}span.CodeMirror-selectedtext{background:0 0}.EasyMDEContainer{display:block}.CodeMirror-rtl pre{direction:rtl}.EasyMDEContainer.sided--no-fullscreen{display:flex;flex-direction:row;flex-wrap:wrap}.EasyMDEContainer .CodeMirror{box-sizing:border-box;height:auto;border:1px solid #ced4da;border-bottom-left-radius:4px;border-bottom-right-radius:4px;padding:10px;font:inherit;z-index:0;word-wrap:break-word}.EasyMDEContainer .CodeMirror-scroll{cursor:text}.EasyMDEContainer .CodeMirror-fullscreen{background:#fff;position:fixed!important;top:50px;left:0;right:0;bottom:0;height:auto;z-index:8;border-right:none!important;border-bottom-right-radius:0!important}.EasyMDEContainer .CodeMirror-sided{width:50%!important}.EasyMDEContainer.sided--no-fullscreen .CodeMirror-sided{border-right:none!important;border-bottom-right-radius:0;position:relative;flex:1 1 auto}.EasyMDEContainer .CodeMirror-placeholder{opacity:.5}.EasyMDEContainer .CodeMirror-focused .CodeMirror-selected{background:#d9d9d9}.editor-toolbar{position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none;padding:9px 10px;border-top:1px solid #ced4da;border-left:1px solid #ced4da;border-right:1px solid #ced4da;border-top-left-radius:4px;border-top-right-radius:4px}.editor-toolbar.fullscreen{width:100%;height:50px;padding-top:10px;padding-bottom:10px;box-sizing:border-box;background:#fff;border:0;position:fixed;top:0;left:0;opacity:1;z-index:9}.editor-toolbar.fullscreen::before{width:20px;height:50px;background:-moz-linear-gradient(left,#fff 0,rgba(255,255,255,0) 100%);background:-webkit-gradient(linear,left top,right top,color-stop(0,#fff),color-stop(100%,rgba(255,255,255,0)));background:-webkit-linear-gradient(left,#fff 0,rgba(255,255,255,0) 100%);background:-o-linear-gradient(left,#fff 0,rgba(255,255,255,0) 100%);background:-ms-linear-gradient(left,#fff 0,rgba(255,255,255,0) 100%);background:linear-gradient(to right,#fff 0,rgba(255,255,255,0) 100%);position:fixed;top:0;left:0;margin:0;padding:0}.editor-toolbar.fullscreen::after{width:20px;height:50px;background:-moz-linear-gradient(left,rgba(255,255,255,0) 0,#fff 100%);background:-webkit-gradient(linear,left top,right top,color-stop(0,rgba(255,255,255,0)),color-stop(100%,#fff));background:-webkit-linear-gradient(left,rgba(255,255,255,0) 0,#fff 100%);background:-o-linear-gradient(left,rgba(255,255,255,0) 0,#fff 100%);background:-ms-linear-gradient(left,rgba(255,255,255,0) 0,#fff 100%);background:linear-gradient(to right,rgba(255,255,255,0) 0,#fff 100%);position:fixed;top:0;right:0;margin:0;padding:0}.EasyMDEContainer.sided--no-fullscreen .editor-toolbar{width:100%}.editor-toolbar .easymde-dropdown,.editor-toolbar button{background:0 0;display:inline-block;text-align:center;text-decoration:none!important;height:30px;margin:0;padding:0;border:1px solid transparent;border-radius:3px;cursor:pointer}.editor-toolbar button{font-weight:700;min-width:30px;padding:0 6px;white-space:nowrap}.editor-toolbar button.active,.editor-toolbar button:hover{background:#fcfcfc;border-color:#95a5a6}.editor-toolbar i.separator{display:inline-block;width:0;border-left:1px solid #d9d9d9;border-right:1px solid #fff;color:transparent;text-indent:-10px;margin:0 6px}.editor-toolbar button:after{font-family:Arial,"Helvetica Neue",Helvetica,sans-serif;font-size:65%;vertical-align:text-bottom;position:relative;top:2px}.editor-toolbar button.heading-1:after{content:"1"}.editor-toolbar button.heading-2:after{content:"2"}.editor-toolbar button.heading-3:after{content:"3"}.editor-toolbar button.heading-bigger:after{content:"▲"}.editor-toolbar button.heading-smaller:after{content:"▼"}.editor-toolbar.disabled-for-preview button:not(.no-disable){opacity:.6;pointer-events:none}@media only screen and (max-width:700px){.editor-toolbar i.no-mobile{display:none}}.editor-statusbar{padding:8px 10px;font-size:12px;color:#959694;text-align:right}.EasyMDEContainer.sided--no-fullscreen .editor-statusbar{width:100%}.editor-statusbar span{display:inline-block;min-width:4em;margin-left:1em}.editor-statusbar .lines:before{content:'lines: '}.editor-statusbar .words:before{content:'words: '}.editor-statusbar .characters:before{content:'characters: '}.editor-preview-full{position:absolute;width:100%;height:100%;top:0;left:0;z-index:7;overflow:auto;display:none;box-sizing:border-box}.editor-preview-side{position:fixed;bottom:0;width:50%;top:50px;right:0;z-index:9;overflow:auto;display:none;box-sizing:border-box;border:1px solid #ddd;word-wrap:break-word}.editor-preview-active-side{display:block}.EasyMDEContainer.sided--no-fullscreen .editor-preview-active-side{flex:1 1 auto;height:auto;position:static}.editor-preview-active{display:block}.editor-preview{padding:10px;background:#fafafa}.editor-preview>p{margin-top:0}.editor-preview pre{background:#eee;margin-bottom:10px}.editor-preview table td,.editor-preview table th{border:1px solid #ddd;padding:5px}.cm-s-easymde .cm-tag{color:#63a35c}.cm-s-easymde .cm-attribute{color:#795da3}.cm-s-easymde .cm-string{color:#183691}.cm-s-easymde .cm-header-1{font-size:calc(1.375rem + 1.5vw)}.cm-s-easymde .cm-header-2{font-size:calc(1.325rem + .9vw)}.cm-s-easymde .cm-header-3{font-size:calc(1.3rem + .6vw)}.cm-s-easymde .cm-header-4{font-size:calc(1.275rem + .3vw)}.cm-s-easymde .cm-header-5{font-size:1.25rem}.cm-s-easymde .cm-header-6{font-size:1rem}.cm-s-easymde .cm-header-1,.cm-s-easymde .cm-header-2,.cm-s-easymde .cm-header-3,.cm-s-easymde .cm-header-4,.cm-s-easymde .cm-header-5,.cm-s-easymde .cm-header-6{margin-bottom:.5rem;line-height:1.2}.cm-s-easymde .cm-comment{background:rgba(0,0,0,.05);border-radius:2px}.cm-s-easymde .cm-link{color:#7f8c8d}.cm-s-easymde .cm-url{color:#aab2b3}.cm-s-easymde .cm-quote{color:#7f8c8d;font-style:italic}.editor-toolbar .easymde-dropdown{position:relative;background:linear-gradient(to bottom right,#fff 0,#fff 84%,#333 50%,#333 100%);border-radius:0;border:1px solid #fff}.editor-toolbar .easymde-dropdown:hover{background:linear-gradient(to bottom right,#fff 0,#fff 84%,#333 50%,#333 100%)}.easymde-dropdown-content{display:block;visibility:hidden;position:absolute;background-color:#f9f9f9;box-shadow:0 8px 16px 0 rgba(0,0,0,.2);padding:8px;z-index:2;top:30px}.easymde-dropdown:active .easymde-dropdown-content,.easymde-dropdown:focus .easymde-dropdown-content,.easymde-dropdown:focus-within .easymde-dropdown-content{visibility:visible}.easymde-dropdown-content button{display:block}span[data-img-src]::after{content:'';background-image:var(--bg-image);display:block;max-height:100%;max-width:100%;background-size:contain;height:0;padding-top:var(--height);width:var(--width);background-repeat:no-repeat}.CodeMirror .cm-spell-error:not(.cm-url):not(.cm-comment):not(.cm-tag):not(.cm-word){background:rgba(255,0,0,.15)} \ No newline at end of file diff --git a/html/css/observium.css b/html/css/observium.css index 4f983c57..f5262317 100644 --- a/html/css/observium.css +++ b/html/css/observium.css @@ -6275,6 +6275,10 @@ i.menu-icon, margin-right: 5px; margin-top: 1px; } +.dropdown-scrollable .dropdown-menu { + max-height: 1000px; + overflow-y: auto; +} .well { min-height: 20px; padding: 10px; @@ -6769,6 +6773,7 @@ i.menu-icon, color: #444; display: block; padding: 7px 10px; + padding-bottom: 4px; position: relative; background-color: #fafafa; } @@ -10449,7 +10454,8 @@ a.badge:focus { * Tested with IE 8, IE 9, Chrome 18, Firefox 9, Opera 11. * Does not work with IE 7. */ -.qtip-bootstrap { +.qtip-bootstrap, +.tippy-box { /** Taken from Bootstrap body */ font-size: 14px; line-height: 20px; @@ -10496,9 +10502,10 @@ a.badge:focus { top: 45%; border-style: none; } -.qtip-bootstrap .qtip-content { +.qtip-bootstrap .qtip-content, +.tippy-content { /** Taken from Bootstrap .popover-content */ - padding: 9px 14px; + padding: 9px 9px; } .qtip-bootstrap .qtip-icon { /** @@ -10916,11 +10923,13 @@ select.selectpicker { overflow: hidden; } .bootstrap-select .dropdown-toggle .caret { + right: 12px; + /* position: absolute; top: 50%; - right: 12px; margin-top: -2px; vertical-align: middle; +*/ } .input-group .bootstrap-select.form-control .dropdown-toggle { border-radius: inherit; @@ -12127,6 +12136,12 @@ form.pagination { .form-horizontal .col-md-4 .control-label { width: 120px; } +.dygraph-axis-label > .dygraph-axis-label-x { + color: #333333; +} +.dygraph-axis-label > .dygraph-axis-label-y { + color: #333333; +} /*EOF*/ #suggestions { display: none; diff --git a/html/css/simplemde.min.css b/html/css/simplemde.min.css deleted file mode 100644 index d62f4d77..00000000 --- a/html/css/simplemde.min.css +++ /dev/null @@ -1,7 +0,0 @@ -/** - * simplemde v1.11.2 - * Copyright Next Step Webs, Inc. - * @link https://github.com/NextStepWebs/simplemde-markdown-editor - * @license MIT - */ -.CodeMirror{color:#000}.CodeMirror-lines{padding:4px 0}.CodeMirror pre{padding:0 4px}.CodeMirror-gutter-filler,.CodeMirror-scrollbar-filler{background-color:#fff}.CodeMirror-gutters{border-right:1px solid #ddd;background-color:#f7f7f7;white-space:nowrap}.CodeMirror-linenumber{padding:0 3px 0 5px;min-width:20px;text-align:right;color:#999;white-space:nowrap}.CodeMirror-guttermarker{color:#000}.CodeMirror-guttermarker-subtle{color:#999}.CodeMirror-cursor{border-left:1px solid #000;border-right:none;width:0}.CodeMirror div.CodeMirror-secondarycursor{border-left:1px solid silver}.cm-fat-cursor .CodeMirror-cursor{width:auto;border:0!important;background:#7e7}.cm-fat-cursor div.CodeMirror-cursors{z-index:1}.cm-animate-fat-cursor{width:auto;border:0;-webkit-animation:blink 1.06s steps(1) infinite;-moz-animation:blink 1.06s steps(1) infinite;animation:blink 1.06s steps(1) infinite;background-color:#7e7}@-moz-keyframes blink{50%{background-color:transparent}}@-webkit-keyframes blink{50%{background-color:transparent}}@keyframes blink{50%{background-color:transparent}}.cm-tab{display:inline-block;text-decoration:inherit}.CodeMirror-ruler{border-left:1px solid #ccc;position:absolute}.cm-s-default .cm-header{color:#00f}.cm-s-default .cm-quote{color:#090}.cm-negative{color:#d44}.cm-positive{color:#292}.cm-header,.cm-strong{font-weight:700}.cm-em{font-style:italic}.cm-link{text-decoration:underline}.cm-strikethrough{text-decoration:line-through}.cm-s-default .cm-keyword{color:#708}.cm-s-default .cm-atom{color:#219}.cm-s-default .cm-number{color:#164}.cm-s-default .cm-def{color:#00f}.cm-s-default .cm-variable-2{color:#05a}.cm-s-default .cm-variable-3{color:#085}.cm-s-default .cm-comment{color:#a50}.cm-s-default .cm-string{color:#a11}.cm-s-default .cm-string-2{color:#f50}.cm-s-default .cm-meta,.cm-s-default .cm-qualifier{color:#555}.cm-s-default .cm-builtin{color:#30a}.cm-s-default .cm-bracket{color:#997}.cm-s-default .cm-tag{color:#170}.cm-s-default .cm-attribute{color:#00c}.cm-s-default .cm-hr{color:#999}.cm-s-default .cm-link{color:#00c}.cm-invalidchar,.cm-s-default .cm-error{color:red}.CodeMirror-composing{border-bottom:2px solid}div.CodeMirror span.CodeMirror-matchingbracket{color:#0f0}div.CodeMirror span.CodeMirror-nonmatchingbracket{color:#f22}.CodeMirror-matchingtag{background:rgba(255,150,0,.3)}.CodeMirror-activeline-background{background:#e8f2ff}.CodeMirror{position:relative;overflow:hidden;background:#fff}.CodeMirror-scroll{overflow:scroll!important;margin-bottom:-30px;margin-right:-30px;padding-bottom:30px;height:100%;outline:0;position:relative}.CodeMirror-sizer{position:relative;border-right:30px solid transparent}.CodeMirror-gutter-filler,.CodeMirror-hscrollbar,.CodeMirror-scrollbar-filler,.CodeMirror-vscrollbar{position:absolute;z-index:6;display:none}.CodeMirror-vscrollbar{right:0;top:0;overflow-x:hidden;overflow-y:scroll}.CodeMirror-hscrollbar{bottom:0;left:0;overflow-y:hidden;overflow-x:scroll}.CodeMirror-scrollbar-filler{right:0;bottom:0}.CodeMirror-gutter-filler{left:0;bottom:0}.CodeMirror-gutters{position:absolute;left:0;top:0;min-height:100%;z-index:3}.CodeMirror-gutter{white-space:normal;height:100%;display:inline-block;vertical-align:top;margin-bottom:-30px}.CodeMirror-gutter-wrapper{position:absolute;z-index:4;background:0 0!important;border:none!important;-webkit-user-select:none;-moz-user-select:none;user-select:none}.CodeMirror-gutter-background{position:absolute;top:0;bottom:0;z-index:4}.CodeMirror-gutter-elt{position:absolute;cursor:default;z-index:4}.CodeMirror-lines{cursor:text;min-height:1px}.CodeMirror pre{-moz-border-radius:0;-webkit-border-radius:0;border-radius:0;border-width:0;background:0 0;font-family:inherit;font-size:inherit;margin:0;white-space:pre;word-wrap:normal;line-height:inherit;color:inherit;z-index:2;position:relative;overflow:visible;-webkit-tap-highlight-color:transparent;-webkit-font-variant-ligatures:none;font-variant-ligatures:none}.CodeMirror-wrap pre{word-wrap:break-word;white-space:pre-wrap;word-break:normal}.CodeMirror-linebackground{position:absolute;left:0;right:0;top:0;bottom:0;z-index:0}.CodeMirror-linewidget{position:relative;z-index:2;overflow:auto}.CodeMirror-code{outline:0}.CodeMirror-gutter,.CodeMirror-gutters,.CodeMirror-linenumber,.CodeMirror-scroll,.CodeMirror-sizer{-moz-box-sizing:content-box;box-sizing:content-box}.CodeMirror-measure{position:absolute;width:100%;height:0;overflow:hidden;visibility:hidden}.CodeMirror-cursor{position:absolute}.CodeMirror-measure pre{position:static}div.CodeMirror-cursors{visibility:hidden;position:relative;z-index:3}.CodeMirror-focused div.CodeMirror-cursors,div.CodeMirror-dragcursors{visibility:visible}.CodeMirror-selected{background:#d9d9d9}.CodeMirror-focused .CodeMirror-selected,.CodeMirror-line::selection,.CodeMirror-line>span::selection,.CodeMirror-line>span>span::selection{background:#d7d4f0}.CodeMirror-crosshair{cursor:crosshair}.CodeMirror-line::-moz-selection,.CodeMirror-line>span::-moz-selection,.CodeMirror-line>span>span::-moz-selection{background:#d7d4f0}.cm-searching{background:#ffa;background:rgba(255,255,0,.4)}.cm-force-border{padding-right:.1px}@media print{.CodeMirror div.CodeMirror-cursors{visibility:hidden}}.cm-tab-wrap-hack:after{content:''}span.CodeMirror-selectedtext{background:0 0}.CodeMirror{height:auto;min-height:300px;border:1px solid #ddd;border-bottom-left-radius:4px;border-bottom-right-radius:4px;padding:10px;font:inherit;z-index:1}.CodeMirror-scroll{min-height:300px}.CodeMirror-fullscreen{background:#fff;position:fixed!important;top:50px;left:0;right:0;bottom:0;height:auto;z-index:9}.CodeMirror-sided{width:50%!important}.editor-toolbar{position:relative;opacity:.6;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none;padding:0 10px;border-top:1px solid #bbb;border-left:1px solid #bbb;border-right:1px solid #bbb;border-top-left-radius:4px;border-top-right-radius:4px}.editor-toolbar:after,.editor-toolbar:before{display:block;content:' ';height:1px}.editor-toolbar:before{margin-bottom:8px}.editor-toolbar:after{margin-top:8px}.editor-toolbar:hover,.editor-wrapper input.title:focus,.editor-wrapper input.title:hover{opacity:.8}.editor-toolbar.fullscreen{width:100%;height:50px;overflow-x:auto;overflow-y:hidden;white-space:nowrap;padding-top:10px;padding-bottom:10px;box-sizing:border-box;background:#fff;border:0;position:fixed;top:0;left:0;opacity:1;z-index:9}.editor-toolbar.fullscreen::before{width:20px;height:50px;background:-moz-linear-gradient(left,rgba(255,255,255,1) 0,rgba(255,255,255,0) 100%);background:-webkit-gradient(linear,left top,right top,color-stop(0,rgba(255,255,255,1)),color-stop(100%,rgba(255,255,255,0)));background:-webkit-linear-gradient(left,rgba(255,255,255,1) 0,rgba(255,255,255,0) 100%);background:-o-linear-gradient(left,rgba(255,255,255,1) 0,rgba(255,255,255,0) 100%);background:-ms-linear-gradient(left,rgba(255,255,255,1) 0,rgba(255,255,255,0) 100%);background:linear-gradient(to right,rgba(255,255,255,1) 0,rgba(255,255,255,0) 100%);position:fixed;top:0;left:0;margin:0;padding:0}.editor-toolbar.fullscreen::after{width:20px;height:50px;background:-moz-linear-gradient(left,rgba(255,255,255,0) 0,rgba(255,255,255,1) 100%);background:-webkit-gradient(linear,left top,right top,color-stop(0,rgba(255,255,255,0)),color-stop(100%,rgba(255,255,255,1)));background:-webkit-linear-gradient(left,rgba(255,255,255,0) 0,rgba(255,255,255,1) 100%);background:-o-linear-gradient(left,rgba(255,255,255,0) 0,rgba(255,255,255,1) 100%);background:-ms-linear-gradient(left,rgba(255,255,255,0) 0,rgba(255,255,255,1) 100%);background:linear-gradient(to right,rgba(255,255,255,0) 0,rgba(255,255,255,1) 100%);position:fixed;top:0;right:0;margin:0;padding:0}.editor-toolbar a{display:inline-block;text-align:center;text-decoration:none!important;color:#2c3e50!important;width:30px;height:30px;margin:0;border:1px solid transparent;border-radius:3px;cursor:pointer}.editor-toolbar a.active,.editor-toolbar a:hover{background:#fcfcfc;border-color:#95a5a6}.editor-toolbar a:before{line-height:30px}.editor-toolbar i.separator{display:inline-block;width:0;border-left:1px solid #d9d9d9;border-right:1px solid #fff;color:transparent;text-indent:-10px;margin:0 6px}.editor-toolbar a.fa-header-x:after{font-family:Arial,"Helvetica Neue",Helvetica,sans-serif;font-size:65%;vertical-align:text-bottom;position:relative;top:2px}.editor-toolbar a.fa-header-1:after{content:"1"}.editor-toolbar a.fa-header-2:after{content:"2"}.editor-toolbar a.fa-header-3:after{content:"3"}.editor-toolbar a.fa-header-bigger:after{content:"▲"}.editor-toolbar a.fa-header-smaller:after{content:"▼"}.editor-toolbar.disabled-for-preview a:not(.no-disable){pointer-events:none;background:#fff;border-color:transparent;text-shadow:inherit}@media only screen and (max-width:700px){.editor-toolbar a.no-mobile{display:none}}.editor-statusbar{padding:8px 10px;font-size:12px;color:#959694;text-align:right}.editor-statusbar span{display:inline-block;min-width:4em;margin-left:1em}.editor-preview,.editor-preview-side{padding:10px;background:#fafafa;overflow:auto;display:none;box-sizing:border-box}.editor-statusbar .lines:before{content:'lines: '}.editor-statusbar .words:before{content:'words: '}.editor-statusbar .characters:before{content:'characters: '}.editor-preview{position:absolute;width:100%;height:100%;top:0;left:0;z-index:7}.editor-preview-side{position:fixed;bottom:0;width:50%;top:50px;right:0;z-index:9;border:1px solid #ddd}.editor-preview-active,.editor-preview-active-side{display:block}.editor-preview-side>p,.editor-preview>p{margin-top:0}.editor-preview pre,.editor-preview-side pre{background:#eee;margin-bottom:10px}.editor-preview table td,.editor-preview table th,.editor-preview-side table td,.editor-preview-side table th{border:1px solid #ddd;padding:5px}.CodeMirror .CodeMirror-code .cm-tag{color:#63a35c}.CodeMirror .CodeMirror-code .cm-attribute{color:#795da3}.CodeMirror .CodeMirror-code .cm-string{color:#183691}.CodeMirror .CodeMirror-selected{background:#d9d9d9}.CodeMirror .CodeMirror-code .cm-header-1{font-size:200%;line-height:200%}.CodeMirror .CodeMirror-code .cm-header-2{font-size:160%;line-height:160%}.CodeMirror .CodeMirror-code .cm-header-3{font-size:125%;line-height:125%}.CodeMirror .CodeMirror-code .cm-header-4{font-size:110%;line-height:110%}.CodeMirror .CodeMirror-code .cm-comment{background:rgba(0,0,0,.05);border-radius:2px}.CodeMirror .CodeMirror-code .cm-link{color:#7f8c8d}.CodeMirror .CodeMirror-code .cm-url{color:#aab2b3}.CodeMirror .CodeMirror-code .cm-strikethrough{text-decoration:line-through}.CodeMirror .CodeMirror-placeholder{opacity:.5}.CodeMirror .cm-spell-error:not(.cm-url):not(.cm-comment):not(.cm-tag):not(.cm-word){background:rgba(255,0,0,.15)} \ No newline at end of file diff --git a/html/data.php b/html/data.php index f2720886..e827a686 100644 --- a/html/data.php +++ b/html/data.php @@ -1,24 +1,22 @@ - * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2019 Observium Limited + * @subpackage web + * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2022 Observium Limited * */ + include_once("../includes/sql-config.inc.php"); include($config['html_dir'] . "/includes/functions.inc.php"); include($config['html_dir'] . "/includes/authenticate.inc.php"); -if (is_numeric($_GET['id']) && ($config['allow_unauth_graphs'] || port_permitted($_GET['id']))) -{ +if (is_numeric($_GET['id']) && ($config['allow_unauth_graphs'] || port_permitted($_GET['id']))) { $port = get_port_by_id($_GET['id']); $device = device_by_id_cache($port['device_id']); //$title = generate_device_link($device); @@ -28,9 +26,9 @@ if (is_numeric($_GET['id']) && ($config['allow_unauth_graphs'] || port_permitted $time = time(); $HC = ($port['port_64bit'] ? 'HC' : ''); - $data = snmp_get_multi_oid($device, "if${HC}InOctets.".$port['ifIndex']." if${HC}OutOctets.".$port['ifIndex'], array(),"IF-MIB"); + $data = snmp_get_multi_oid($device, "if{$HC}InOctets.".$port['ifIndex']." if{$HC}OutOctets.".$port['ifIndex'], [], "IF-MIB"); - printf("%lf|%s|%s\n", $time, $data[$port['ifIndex']]["if${HC}InOctets"], $data[$port['ifIndex']]["if${HC}OutOctets"]); + printf("%lf|%s|%s\n", $time, $data[$port['ifIndex']]["if{$HC}InOctets"], $data[$port['ifIndex']]["if{$HC}OutOctets"]); } else { echo("unauthenticated"); exit; diff --git a/html/graph-realtime.php b/html/graph-realtime.php index 9a129290..16683236 100644 --- a/html/graph-realtime.php +++ b/html/graph-realtime.php @@ -199,8 +199,12 @@ function fetch_data() { function plot_data(obj) { // Show datetimelegend var now = new Date(); - var datetime = (now.getMonth()+1) + "/" + now.getDate() + "/" + now.getFullYear() + ' ' + - LZ(now.getHours()) + ":" + LZ(now.getMinutes()) + ":" + LZ(now.getSeconds()); + //var datetime = (now.getMonth()+1) + "/" + now.getDate() + "/" + now.getFullYear() + ' ' + + // LZ(now.getHours()) + ":" + LZ(now.getMinutes()) + ":" + LZ(now.getSeconds()); + + datetime = now.toLocaleString(); + //datetime = now.toISOString(); + SVGDoc.getElementById('datetime').firstChild.data = datetime; if (!obj.success) diff --git a/html/graph.php b/html/graph.php index 6de537c2..c7d24a71 100644 --- a/html/graph.php +++ b/html/graph.php @@ -15,9 +15,9 @@ // Define this is graph define('OBS_GRAPH', TRUE); -include_once("../includes/sql-config.inc.php"); +$start = microtime(TRUE); // Needs common.php -$start = utime(); // Needs common.php +include_once("../includes/sql-config.inc.php"); include($config['html_dir'] . "/includes/functions.inc.php"); @@ -44,7 +44,7 @@ $vars = get_vars('GET', $auth); include($config['html_dir'] . "/includes/graphs/graph.inc.php"); -$runtime = utime() - $start; +$runtime = microtime(TRUE) - $start; print_debug("Runtime ".$runtime." secs"); diff --git a/html/images/os/acksys.png b/html/images/os/acksys.png new file mode 100644 index 0000000000000000000000000000000000000000..03975dcde95627fed2fc1b7303ce1956864849bb GIT binary patch literal 1402 zcmeAS@N?(olHy`uVBq!ia0vp^20*O9!3-pmxw!Qh7?>FXd_r7(G7I~6Ja_E4o;3UJ zk|VFD?0!+O!cS@X@}HEdbsRma}h zO|$gRckGYd-Z=A1%Y~kTs;+m<%9qsUC0C`~Ta|O5$aBwxWy>!pojD9Nmb1VkvY3H^ zTNs2H8D`CqU|?W8>FMGaV$u8dl4Jg*0tVI(;s2&=yTEuzi-(&hMNm|U8`K>v2zz+N{4>MELr9U?5C5NW`-U; z#r{<+L9c(hLPN#l^et1jKb?Mxhhf`^w`B|YD$f4Ax8m0Aw{a}lGYbHmZ94dWglFj#NZ&-@a!7sle z(_U~))4#$RxJ>WJw=04=@!SkE=HCAt$Lsr+p>uW9!UmS(e>_z7Ec`F5z4Yjv9Z zji$#-gN*%TIQgBE#iaQ@?r_-YUVZe(-OqEpg(n<+G%I<@!bx8>9Szf4c{_w@yFy!8TQBu`gAmvv4FO#qFDs7?R? literal 0 HcmV?d00001 diff --git a/html/images/os/acksys_2x.png b/html/images/os/acksys_2x.png new file mode 100644 index 0000000000000000000000000000000000000000..64ce9241654a631d2ad0f5b31358d7c959d8c6c2 GIT binary patch literal 2349 zcmeH|`8O2$8^=G>*cw!qB<=`Bh!8>%BQr_%C2N|&)gVijYfH?GJ?qU_N`}dj>`BNF zS;K^eG0cpmY(phm<7&O`IrpFVp7VX3^M0OR-se2edH?cGv9>f3hDpKz0K%r1k+ypq zvnPZAbZ^pD3Hks)fwj3k%G@WY@YU+@%$8ji6aMGh$S3o->iOGG-siV*1aA%pl4qa1 z{wNVV3iG7qQ#krKdRfm0AJWX#EN1=i+UcmVUe;Dn$(%<%dt`1~7f*lqa^+eMOFL%V zr+9W`b{k*2K&V~ln%M9ynX}JkX~c|&l+1-!yhq=k76}-Ms+lMEen985Z<6OU@pMNb z%PM_JKX&{|#?+p{Fm+NsYRou}u1BDor!Y#pR)wz*?s+UTrqUXhAbK5g;j~Mslis8m zLGL$-PZy;EM()%sT+e2qk{MX?+_~6svusvc)3TWNn{zn&iIBHwA~UO%lS|<^5LvR} zG!|#aAZ5~t$kK|R5W6{iDuR~TvMlC55>z;Cn#2gHnJ?^E8JyZY6FDaA@)}?Uf!hFZ zdJS$+fkQv=90F!Nz-0hf_X4{4{EwY_Wm zZ(Vi{0YFgnUqQfwherY6^)f{o**hhC8k@4RcZBn=e}3^zy<4TzkE$DrK=8*;z+T@N zZ7hAIt!F@C{rSB zq0&4}bG@9ogZK0&PahaIOYq@V1*-Cj>peJqp-N?_!SRbC;;wO=BzeC?3KT3}ICyto z4egYdgk?-!@WELi5T3iS`Taz^zSOC@27vIY)l0&;GtPsD0grZzuBD&F=!yb?-AF-m z*-qGM2F0=R&;%q)u$62v@V(k{$I;bG!bJ!n9H_ZjQq{SHd!(@0bPZ05aC7!;nrLYw zBOz#Ef6l(H*4@!nBlt1mvW<`o`cSIkg6Zs80Iv(y>ri?rhhgKd4Gi(&PC(CJ@XLhU zvO!8gz&kN+n{tW0T=)Vn!beq1N24%)rhpe%udnk%az^V|4%N%ZS1+K9Jeh-skq2Y4 zVJEtagyZhi^aEU3uVQ%cMFLMXXz7?#L;r0fEif=4ZH~Y$uPS3{IK1Lr2sbsp$^|t) z{S2K6WF1htku5@PnDil;O}m%|Ywvrz>tzi-J7X0XhjFfCVq>t#^1ye`!ahO z_$5sdV@^R3s2D!*fX~lb(HPo}fO_jH-E5^DhCPgs@<>m|6<7y0AB3_^6t}pfb`>Yj z9$1c>yz(NgxsP~(=^HP~La^WKaB{lDZ-`-4@?OdZ)XJN^ z^zwmo{i;gOY8STk=}Q7U`|yZjX?5~#vRv(b@6vWapyFi=UD*}f?wufb26yulMuN{E z6{(Qt2V}}7T#2e1nX+XTD~j76#P03y0h}Gfu_WqKpJ-0m0aE#vq?G1*?d7eGyi>X) z{JZSGZ&P}iUl*E7zx72>e3zUpAu<>=?k|4abC!Z}RNx1xH}hD^xP2 zdD4nESOhF^N#o)kd3WqE#ji4ibYguk_T3hZ-|>Ac8#>YReC9!Od9e{jt2iRQ7y<}2 zXWTGaNd+yMw;CU#65oPa<#MQihy<{5%4S4l%U2f(oSbhSIxpirC*ZyPnHJW+x_QP# z<}Rkb&@6wsDwx#B2wbP-R8}URW!487G)@+NQPZ-&@@M_6%C7@1pLq6{ztx$qV#aC0 zt7M#WO^HX3c_Xajn_G(A8o!hoIq@h)?=K6;f!oQ`KRM>U*a6!gx)Q%X50{>H zVEy*G4c_5sA}c6olqG6wY+v8aj0K{a{F~;1Hh$ax>nq>d#qI> zQX1{tyzkqirp2_Ywdde?f7LaIu;%vz%(R<@dEXb$NZJ%Ia_gSU!vjxPSTWa^V|Mam z-BVpzN1h#E)-&W$Mcmev&o32BmMd+KSFfyZ&up~+VQt$$SF27}CZ&ZmhdG)xn{%nW wR322SSbrfBagM4tQag6}HhC_HRF#qAZcM7cqo?0IJKVGXMYp literal 0 HcmV?d00001 diff --git a/html/images/os/affirmed.png b/html/images/os/affirmed.png new file mode 100644 index 0000000000000000000000000000000000000000..5aa6a2a6daff03951feb81009ea01d7c7e26887e GIT binary patch literal 1450 zcmeAS@N?(olHy`uVBq!ia0vp^20*O9!3-pmxw!Qh7?>FXd_r7(G7HZY)!$vU@kC!iHd6WIlVSDWSH;Cz8X`66i+n&QG&RneN*lFatP1}B( zrrl0M*Nt*UyDfb-?cBR*=f17G59~X9e6y0-0k?=lr_Uesj6Qwi_6BL43zcn${Nk^) zPu#BKxYycm8H4DBl7?du8LPRJj>qQgFmc{t;Jm}ob;Gff*ZXFy6VbZTI`ME&!m+Tl z&5C9_j-5P}T5=>L<<`{s$K!I&UB9(m)q0O@AkezA#SKRz(obHyb-AhMfSKnW`@s9l z*Wa8td;8K=%NSTzv&t`HVAvtA0kmihkIE7TzGVy|D_LY0GO*8QVBD~M&zdbeXE89W z*syiwo&&&O83iLS1nx-Z{Q-L0w@DBCf1%9(j-LCf!RKcD^8Ws_SFV44 zdUoo=%VE1s-`B2S|Gw)(!P*!8*VZriZF4`fe9o6ub6Wzt70$1m=6yV)DQ~iGcGA3G z$7&{Vu0DSdXfS7iM`SSr1Gg{;GcwGYBf-GH_}$aRF~p+x?WCLj!HyC~&z~u|ATTV~KkXhV3_>US@5$x32y++XBJDuZK5ozLEH!?Xc2m{_o4ouVr^GRlcox z(N5%$@{`aHjB}=4T)zFxV*Lv;>9&itIr zMkiG7w)spsv-exV$D-ur=7MiH`ps7UeXIo7%DzRuJ>{mb z?@`uoyemKZOKnZgE~kf;{zZWn`*&>L?0qp~ z&&Md$o!^3!&wsa6l@rXL`ZIEYk!8NP&CCTC^zQZ7On>QktyN;OZe9A}8C8EdU)y!Y zUfs9k+8_O>g?HNmN{`gIO?28HP_4Bw;gm+$#oirSjIkT;)UJEFe6PM%M@^}|b##IQ zgJM9y&TIAkeWnJ@93?E87cVeczyG;%`Vp6e74w(>jq+E|oHgwgL-Xb-H}mvbW<1{Y zd%yV9ZINqNeoI@hORMv@^Rc7W>327E#lJqi{oF#E@;9gFe7yge{f+3}$ssqT+d=uv M)78&qol`;+0F7b8>;M1& literal 0 HcmV?d00001 diff --git a/html/images/os/affirmed_2x.png b/html/images/os/affirmed_2x.png new file mode 100644 index 0000000000000000000000000000000000000000..e095b452d92272511f9bc9a355a0ffe890336f9a GIT binary patch literal 2560 zcmeH|`8U)J8^*t5on*^SPsx(9uSJAtkbN10>?Ov&FIhvD5R$D35wZ@lFEhh1V+M_F ztdl21(-cy&Z;$?r_nh}W=W|`ZT<1RLzJ9q=%uMuHm;{&r0ASI-2eCMF=QHkrY0q@x zd;Dzxpasl~EOm@L0*E1TjhE#H?Yx?JWm`qn7P`>w@^?R6QkpBP=|K0Biz-@#m3#H> z&*i^e9H07B-%Nh}u31RA4c*_>KR7=+L4XH$D(PW|J`O-15&fcv+=9DQ4DinWLmpum z{78qiHhGMM)`oRRX?Ms$#zLRYzp3a}HY5fHBZs_eiF5Ss!3?7HtUuR$ zASGt@sG1B|yY?B__glGOVerY=w5f#5E+xI$v|OB%_f+!BzX7v{6A{n4m5qkoLa{K{ zubt?Ijvi8cMjvi?EHoaeVKHF#pociVRQ;jP*r6I=ssRf$k57_k$upS+)%5%b4vD7r zo=SkLiiWS6_Mcj?a0S5Dz%5(SLlx7l)OK3jjz|K9x|065S27Z6bJh7SN3^z@t~)7$jxj4sE)tj5x&X{W>o8kd&EYRijaHpICm7B5uf7d1DIKsvg=%wP7uP>BM*MDRawh|YxjLiS$Y*UWs)~2 z)(x`C$-<5|l@|y(CHkAkGdZ|r!Wc>6sPVU`+G`(P){55W*iN|T4iji)c~2!yBmZPq z8gC=}zEwQtR0??l0=+QYadgk7|Dwt3(=SBA_fy1x-ksC!y87VUN?RwL)76dd^k0W8 zzHhsm`u$AeM|QhX(;e@h5Hj0a9{pS>@iW2`ri_s0*I_@0xVMc$k3-X(<2QKbZbgmM z`>`ex+cG}#Hh-zLjijyw)*Pt0=pYa8I8)z-sw;xSKjqM;a(N`#iPWl3Jq z2a=?gD6B>C(s$7jYVyB`j742$vlg3d#TpTnNRmt#E+l^%UFNzO;+v&zu4kH#_|{Mv z$BPimTJrAm=X^jh2Cezk3Me#|RecpSfzNH`Y-(L!7w@ONy090#arMINR(?#)HHu@Z ztJ3eFnLkoLY_DB-;zS4`agL_=U7SQuQ_^9B{y8fB^mK+ z{(5f~*;0TNep=3#yS~cAbW=etgHVd0_%)vWKepqovVTPQG4(K%XJN1!MQ=H>WB?xo)Vnn^!)@Kp+ zZfiH{zhHF&`LbZP@fm#Tptn%*4K zIUu`0Ce=j`3$?9dP$qtI&lqHLA=p4W+#HDsr_G(Ck16uGm*-vpy3D)YGS|FV{;=xP z`|-4qE~>5Yt@9%ahLfd(3B3$HVt+XJp2Hp9s<1D=|D#mSfO5r|re;nsBF0GP*92rcWO9WT5piz(BuBP4;6=3F|Kp#&_9B z(EJ!sSfYE8?ytNM{SiY1NM6f|1t`uN1L1oE1ClFJWYw-)t#&<8dxeJ=7^IT)NGEkFN^y7iuH9&AP8;e`2PSTPY$O5 literal 0 HcmV?d00001 diff --git a/html/images/os/allot-dark.png b/html/images/os/allot-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..611412273000d8d9236b5f81ed62e216a07d1542 GIT binary patch literal 1378 zcmeAS@N?(olHy`uVBq!ia0vp^20*O9!3-pmxw!Qh7?>FXd_r7(G7ImCTV1_;@yeCU zAawmIlmTR4zI^G%-78lwU%7VuwUpYGYgeyaz53i%AFkj_i2UUnHzC~5fr?K>^nr>W zt6N?E@dhaPF;wZffZ9DllTX=74<)R>SBn2`<^hVo*Hr%CqWq&!@U?>G=Xm)am4g4u z7=VKBZIwS4DSgRS_&QPfDucy227|NjKb?O5|7Wk@buJ5_#_J;PAA^)X=P7nBb z^oL=>^;Z<0iF&-ftTfL(ghAGk`Ojh9xZ()O~eq-I^Iu8~xU(jK%XqorxT0zFe-KUPb zrEe~KY}Lpw&wu>Q6`?t$nVgSr>%CRq%QXKUL)m3^ujl3b#t&SNzp`aX;yrPhl|AoG z3r9jp(ei1lmao=*&6fD$TDr$e$4#rw-;iW1+pzQ@+dYvBrW)txWj~#NJpW4j;^2~#-xV}ufr{lY@;>erV*#yxQ>Ao*0v2uOZTQT&`BcU#C2Xx1}B zEuhG!V8zE$W*;1t?+IFbOOgK^ulPMj{F$uL*DA$N$x5GG<-hmHe(P5JoUQm{f&BL_ zIiQxiVzytZ6s`-of6h_-T&(y~O5>fn@&|wA_g2c6_AkBm z&@ZE4Gz2IKftCA?)&lkGl?3?(GdO)pR)12!yf-`S-{fl&|4wr_n_ctV_UnYuoHZs7 zyz)LM$1G5aXt-Ck`Th6qtM9Ev!hkwC3p^r=85p>QL70(Y)*K07*0T0=aSVxQeLFoi z|CWbHn>>HQaS4t%p^YY)(n?a9sz?6)|KE7U%Mbv37JZoab;(86 z7)GnRJ)Wj7nY0;P+~(hR)0@Ekgz0~y!7pU8^U*4v^!t}XU(*C78 zf*bY;S!5ll+0CJ?^hWdXYEAzKy*poc91_ac-J;C; zCI0*O24?T8{-F|VYwMryZdh=GBcBk?i?X7M)m&5nxdz|8ZvjS#%>$-1<_u; z`KPUB*e`x^hgG2Sp4b_Nf=91czNuTvb%pu5MxHvunr+|X6TVK6(yG4c&euR3qZlzegq^8tUJwMf_&G zy_q&!BG}naE$>j$j$F%9wLhpJ9Ke9TqymbEL1 z^~KV&2k%{a{+jDS)OtUan%rf-FIl?x$yjcB7_U+D@b~;R;!{Dn+SAp~Wt~$(69AVY BGzkCz literal 0 HcmV?d00001 diff --git a/html/images/os/allot.png b/html/images/os/allot.png new file mode 100644 index 0000000000000000000000000000000000000000..36e8982a83c489d5c312e9eafeb0bcaf329439ef GIT binary patch literal 1387 zcmeAS@N?(olHy`uVBq!ia0vp^20*O9!3-pmxw!Qh7?>FXd_r7(G7H_kecU}fy?p#U zy?s5rd|W(yfebefZxHDX67=@+ednk9T1w5s+te)5(2x5sCFm1p&+ zc)q2fCVbwRzfHH(7k7TN&c9uDXQA?*&$7A8%z&nF7I;J!Gca%qgD@k*tT_@442-8e zT^vI!df!eum@gD4aIF6Q+vZD7joUO6Hn}=vc)|Hm>pZCSuci+!_Gb@ex|2;mxVUvre##HXrZbxsPX$$c>qUG)0UeLn* ze!cO`50*0iPhK69l}NW_&3I>Ur+0tm4Ccy7?$J{_3iTouuDbrrZC5u35=_@$KdC##1Mf6Q`fNzAKd7Pfcj?%Rd(m zRED34FZ`_R(!l(FUsXZjt>o=8{VZpgPWF_9>KNRbwY+W7e$G4W2eKO!;~ND$CDS_9 zOcc(Xb-2b>F!f0D61j#Hp6@}--t9T-EVw)haY%nz5S{`dF~Sdh9}pZwH|vK9^lgHP+s2tsd3tM l38ibl8`fgY)qDMb3&7^ZtNRlc%en%Q~loCIGh&XPp25 literal 0 HcmV?d00001 diff --git a/html/images/os/allot_2x.png b/html/images/os/allot_2x.png new file mode 100644 index 0000000000000000000000000000000000000000..79786f86f729e852ae3bb75a196a526ef272fa70 GIT binary patch literal 1767 zcmeAS@N?(olHy`uVBq!ia0vp^2|(l^{W1cTaC1`8iez$bDt1^EFoSbBf|) z36tAGjt|AHy?y+@wJUsZRQ_Ef`O#Y8d$+}^ zUgu{_en3M$7b@Noae5?a{Ix~lm7MxLaqAEM%I^$S-a9H^74-a4qwu9d@l%S@Ybmvl z5lVgmApeYl(GZ|K1mbMZSOHDZDGBlmW_Tavk(RBz-S+;8fB*imxBL_|oGp0GBl5)7 zIhj%|A5_bp=_f9@c5n03(+cWU3yb@J`ZxlFziD-R0 zJvRTAheYdqL%ufEhpSC$+yXOJ-xb*fQV2ce&o3 z7~nVed#O~xJB~f8ejh7)kkiQcB^ms#k$O^vb}fiEh!V}3t4!l;M4cBhsy2qY6`Rq+wk?Xh38O$FlXDUq(d$?1Bb)C8mgW6snZU_14 zkHmH<%zk=S^?<~^d7f&__Y3#Ez0<_W^|IXP|NYffzRUNVOTNQ0hd(7|m(TipY%@~( zn>TWOiV*od&3)GOO}Eqn;=Rf`l ziA_*XybzJj{;B0y&XPaZzcXyUU0QT;x@jxxJBAydqwnm?QJcu*9>ehC`;EJeIogd0 z*VoPe`z6LqEUJFUO&)|A6POS67Q?o{k~ptOT& z?d;NRq7O~v^p>0#t(pJ3LgRL!N5UIz??)Sts-5_mdCO}1lz#$69!FzWmmO3N7F_mJ zp5?&7TZWeVrmvOjtNp1}?XKN=CDTBQK|SbmnOmJ&v~tN!tp`@;E@m%fUD~rv*IwN3 zW&85?smo)n85NB8b01svq3Ew8`+}&{85{5CdGlMOEuXTi^r%qe=DKfO4Hr#BRxur0 zezkY=` zK6MGr&3li1`@StFWQQPQ^y~NTSKiM&XZt^8Z6>?K8{PS5i(E1e+-cifa<%%`>KvD6 z-}_Ik7G9lT&#=@e-eD#02_3NoH@H1+b3aIVCRyzoEjGV#`rqR(XSq)4nXErSXyI-} eM_?96`oQclFXd_r7(G7JA>g5Q7t|NZy> z=Wk3IxX6bZ9U%J!qZCNwydH>T&;XG@a6#|SY2E*qbpMN({=a1S|GdHfGa5MzVrLmt zwD~yxpEUxhJZn@xSN1;x+kXbt{|qc9%H032YT3o}g0!D8{?B0g|D^H%nq z@b8{Q_fk2a%SXXz2v9c!-p?$$12oZ~B*-tAA@rVsqQ+KU3AXmvg-bFjr(HVA{X-&7 z+~>zryY%mBfA&<*{P+9c_El^DMSPn5?Z@9wo!^cF^>P+?L>4nJa0`PlBg3pY5)2HC zS3O-ELo9mVPV&yb3ZBwGc-Q4Gqqv4HY+y zVaroc`|Txfh34|6{Mz-+k@ni_*}Q(`{EAeV5(8A+xG-Z~BXdrSl8722HJ>7tzosGhZgfjIqGk?x_XO1BcZ~(SaQ2 znR69@F}NI^@n`lIZH4Dc*cPpE3YvA#&?=#S{`VdE%kI~oknJqz_qx8; zwdCCd(F;-zwJdhOk1tt$TU+mY!M(igw-*U`eNtodyru4~r?ux`MpFXwgWnz%-u6rx zx8`n5Fq*RIj>VB`fm*3<<6qm2f8_3C=#js@=u@^X?=>Z>Qz?_LHj1Uso-o_?c4U=2 z+i?wNM}L3Ds>>OFwdYMf*ZS!#ACqFv$|Gz&x>wC^NNV3;;{2v~(Chbvh=5k5fCux- zuFZHZ{k)xp(J`aJz(&q1_|fmi_P*UFOWdRKFLmtR;-P$@Z`$1wSB0gY9!Ztzdak;; xsv*d0+pe}`)&^s_hj$9X&&E}+sk~}`QC_WhQr(5q%XmO((9_k=Wt~$(697z*i&+2w literal 0 HcmV?d00001 diff --git a/html/images/os/axis-dark_2x.png b/html/images/os/axis-dark_2x.png new file mode 100644 index 0000000000000000000000000000000000000000..6ba1c22134b5b4fe552e32f4f574fd9e57cc0d23 GIT binary patch literal 2059 zcmeHHc{J2(82*0tWh^7vhq;lh)G!kv*EY5>2O)&)S+Wf?v z-g)Qr+o72d?@nYcoIe5pFK{BdS`ouy;P0{g{(B7u*#C-y|0>(SkdGg5vk6%E^*(s; z0Njs(2QXXRdKQw1oeSKr3j@pp@Dt29$jt&B*j}fciq51c!p!~6x#~N=N&@o354E`_ zy;$yB-8h(yg~#;aWh3~DiI)o!-ZF*RdOMXr@S_9&R|jI6^^$kI3@OeIE+7r5F*DR7 zsI<6Vk?q5bfA!2+|Mgo_4#$RrK2n;w#EE}(b^DmVzSlMgh{r4)m}BA_o6$*Nx>(3n|;&#?X9&g4IUq@Z{3ZFY8}ww2B*ls%aC4O(|1Rr>ctV zon-R!LM|m=2>i__vb?uw{`a_2Qtyi4gkxWe;Ojlr*j0*^U&?Xd5kxUpdr~<{c+gm# zmB%C3G@wnk%C?q>YB7X1CU~!vh@MrV$&$ji%sMiq&I z(6!sHr*}IOhjfZ+QD3mo>Y?&Fh(2u$CU_VTyrCZS%P|DGs`Kal+sHTvFETNo+!OWgF#@DU0OV;-cq*c~SX_=HL znB82;z;IrU81_Ub2~rQ#pIJ0q6C-NULT?4_VOkP}STZEpW7tPh4Y>YJ{f1AS781_1 zwUwg4gS5!KeWgC`lOaOy?3C7Uvs&|g^TFopWme4S&byFb*>b_bc>>R-r5?58h? zF!c&fyNQyO-6QW-hG?arAmUV0V%%eO@LZuPi&fl(7#kv-`UCUW4Y3s;5E%#68c09Y zr|LnT%Q7@x+iuFZXSi3PQj)$q@=(BediGWfL@H~9TI|%*XtRf}%qlesBtV~6Ka&xv zCZWR#!fvrP2%MQwpM%Ea9dFC!P3dpzDP$B#4w7ahlGLZZwxwF!M$g2{Yu>xLIy-l< zGtZvCO#)Qs?~@o)G>6IzVaDpaEUB>Wv`&vcRUkh=lyw>nC~#qo*j!!U`M7nPcte2x^|vX6kftR6_53KWO@JtjwwKucsT#Lm~n5yk)*cNXE$K zz;K%nhhHJk-k0-nF*sv=ipBP&mg7L^2)Kq;L-X4zrs78+Eu?z8X%_)ymPZ}KDF(k_ zlwY6l*0mbaFIu3k4I9^_kZo*5Yf3Tw5Xyf5g1z0u%-&EhVN8TmSHNk1!Fxfie>RqY zEpN9Br<`%5O~z473PA%gE;H(m8Y|$ToP=dGs}p_2P*jZ?$G$;lOBZiNj1uv#m=6|R^n-HR^wk%CW@f;4}^s~wGRIScfnN#gY5E=G0CF!zSqfcb; z))$Wooeb(a)6l;9LBTt`m9m_ELj~FWSXPTq8-7^Z_RnT8ZTI~}CFXd_r7(G7CX!7?_!W6e@56 z3NtWpFfgJDgLwa$&445q14z^VJ7)hb>i=Ug2eMyJ(Eooy@BbN-|Ceudf*Xzh<1nAf_YBeu+W*KZEJ*8wO_?4D8~0znn7!8FI!LsO-Op>HjabtqWz% z7`TCgqhK@yXdMDyccruf?a(O+@(X5Y7m+T{exMS_x`KDd3%%3R6TW}vwErtEaf3Jg zhg$RgZHs>|`}gZ##J}%RgB@L&*DU%qseOzbGo~*j}Vl;o{bji$r zd(1a){?PiL@zIto8}+XDs{3nSV6OcVlI)XH8-IDD#Lu-Ndrvi+-TI|w#-MFT&D!tdhaCg4sp*MmHc08?ebLEM+aBFAUxx-bwJfYTK3ijQqtuGVcPpDLw z@^bRsd|?6OB?!C~_W3sjmfKA*}s?`_f1$m0i8rU#u`$#g_Q<;9zcTn^q3 z(s$2%wISu9!oDx}PUmiHOJrg^taNVJ5P80ezA#D5*XguRDIdK zP(FJ5QsX<^S0`E$5X`sO@q2&sv5)cM%MI9+ z-5YYuSL7sY)&7;bT|1|$;Ohn6t`KhND}gapkNG=)6~`@4UF%z-&~xjv+*2mA#)Q?y tWjm&AnD)ELoZ*y~+uR?r`#$tnu%8iJkaj`OX9Xxxdb;|#taD0e0sw;FL=FG| delta 1236 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 JPDHLkV1k}-q?Z5y diff --git a/html/images/os/cumulus.png b/html/images/os/cumulus.png index ee2b918515c51cc8061b4ae6b45b281010351f39..c1d974cf8143871f09859bea45eb12477e64c65a 100644 GIT binary patch delta 1246 zcmV<41R?wD45JN@DSu39ae&H=aK~jDns-&Vl)T=@d&+sR+r`G^&j5{Rqu8O8(u^*q zb1$cPtlqh9!len7aIn|SKCxwo%b=y#o?pLsLa}&t$8kZcj2oYCVZU)+y=`8+euu}Y z5SVm0tC6|d%f#cXPPL8+nLa|WbU&_# z0DV-N)u^)(0iFRi>_Sb;0000cbW%=J0HFNcn>ElXApT#a?9>rr_8Ie!metJapYrGO zr)cb~JO|&ZwSH(HOMa6r0~mi7NklKo%xOEcM<|B>N zqN?p8VS%#j3L()25`Q18D%KS&P%COGPzf|i+em>pZQ|Htlc_WFX6C(lEL;NguC9)b z&f?zlBe<9fId4Q|oO;EWPA^pQ&*B=nmEjmLEub%_$vcLH4YJ1;Usrz=Pr=lt{W{bC zHOR`Q9!=@u>k1T485B0vH#`9V1^{mH*5QmRTP}}XojaOpygIr2jN||)09m}V3X*g& z90C%6kuZ4t8JBMp>bIu{u0?t|Ag!^>^Xp})w52DXZd-r>3%5o*V;R$_vXNb=mL1k5 zBT$@t^-~T8`GuuQC^vs&dU--x<8Uc+c-9zMiU(ir8j6H0M!F3E2)g<3##K}K&2#-# z04vE7Xyvwg&KmAY^ADQRLi%0q$?rsqVcrh;-XaT@ZAu)pE1?$&Oo#Bn(zm ziN=7TTpqLmjpY4SKW-qdCc)AC0hEcWdBlXSjD&JCOZV>l+Ub9n?ZQB5p)GwlWtT*S zM4Tik5o$xM`2CGXsPD`Asi9okyJL26s z=0hKVcJnC%Ykyzu9Jh}Ay)#le=i}JCZAMZ|3)_Z5_3wWHcE>$~aU$ei>S`oiz(;uV zy95k~qP0OMiQae-Cp7I$m|;jS2T|*z`)c0FczgX+yJ_W|F;W`M%atnIp6#2tLGu^u ziI#7?NborADCJtBOUw8@xB$nspN~>eIF&3Hlhv0ZL9+HB{TCbzPn| zsrjux{y#Fjj=0eRMl7FQ2YC_GSPjz-vvmZ!xKD zT)l2ixmn-v=KzdUPPc4w$ZHFlRQ~(^T)`DwAIW z7=IB-L_t(IjYX4NZxdG(M%SD}M-+Hk= zuB?$2udknV=d!6#paJ+fk78vV)6g(;nt$P8ZC0n)tUw1IUv-~e`IqBCWT2H+v2IP5 zUiEFUl=ik_(O+4YQKcdZ15KBOsaVbo}*uUO>m&OrB3pFeA-lQYF37dC<@Q7wEZX+6*q=Cs)W@a=YN=f z;w0M==e?sDCvG1Jns`_V_T8emFaeNE2Dc95_{0612ZzAv+D31FvFiD)@=h^&4Mf{r<0v>YnM&Fc9 z-i}*g6?fHF^{4vYMaEHMrwClZ!&dU6pgCZ_G^OX*+Jo zWNQYM(y$iee-NzfdFknX+2I-hlf*$vuWLD13Up8pN97>fTR8c4ML#vuO|x}0d9T=h z*5_s-Q6c^|M-xTl`Q=w;IGQBN0Zjj l^OmKMn%Y|*PgSV^_!rWcj$2sAU1b0O002ovPDHLkV1mUdBmMvY diff --git a/html/images/os/cumulus_2x.png b/html/images/os/cumulus_2x.png index 48d004c8de82adf62f73f5919815c184c9a07147..4b057fc6b03b0e4f5abb887e755a2dadfd762f55 100644 GIT binary patch delta 2481 zcmV;i2~PIY7?u~1DSu39amD1w7Myl#zmapoqm$E>n$fVM)4Wc!l{%_|pV^wO-Mxs; zg(IMTDWrLE#CLtke_y_CS-N;~$8BrDfgwry0oZ&J8!N3(BMyKZjAZbhbfo@Y9) zWzFi_0DoBC@H*$~_V8f9W1Q8tddhJqre!awV@|kSMYCU8xsh_il=u7jIjnyGfrPE!C8@tb;+Tqd2uayhiv?cmk) z?APu`un4i%jP$cu|B%NI0EDsL;9+?vF+ISchXo;6#lB(?gF|eJ})vv^k!^{wgi8n>T0t1$f>rwbbfYR@GQk2-?078#x zgzR{FIhnd6r#@SMud#^&P*5c!KiGg6AchpUYy7T!xCX`>^^QV-<71bI5y;92n{tSO znUWbuMCO&(?F2zP=IDh0adaj$t5lB?mymp=Cldo54PWns6&OWQNX-*sHBH}-@EZ}omO z0Vw^dp9X|~3(AN}Qh^=e_}G-JSRgCL+QQdzt_Hmn-v{0`1nq2^xi`P4bZqjSx`GBJDJr zF@auxn@vb_9dl14bNoDXfB?x{flwIHkVtBKHqeD+-xDnq{X(jTop(kIU_Jg$u{TbE zbZmnZPv5~7;L^d+)1Gi(2~~TllwEcG({CTaxN`-Xvw2idc?X|Z&W?I{dI;ykL`~XgI z0a)K$?8=PTvO{7EkQtcLuAu?wx@=8igtUd~)7fw@!kKHpsppqpHE+MzfCD-TX);7( zBftfFDxz`PFJ~Xax_SpNcD#?ylX9>!q^8uj#$US7!N}=&e%e~J2IU*D-del>Tv>sC zQ%7&)HF}@IK$^pX0^qU*%_5xcl?gAwsn68~0Ok~y9#_CI!aXFdR*oP<7wDjYHXbkE zZr+45doNu9;B=?_+nY64P1*ziazzpVLjXkA1>4Z{0I&b%RlquWGe278{=MgJdvMSQ zjB3+#6c)lvA#g`kbfvPlP`8$XpWNkt{qE)b{(AUr;(J!nCdk_@THcqYJEoA!kn7kR zt;`!+XE(Ns&^;Xu6CiP2)D#6WWC%eE&>93?z=S3%^XISGp0$+&oVMuAaImr45VKm7 z2|#O_LSks$TKtfWu+NJZ08Uw*-oPKX+XFf#GDH+QY7nA}+hKQM^-sKOJ?Qy=TL7=` zb$UArs=2GDbu_dTP#BR@=tBJb!#{I&_kIX)dgILjUjRjo#?8x2aXgW6SLZ{&ZSgB!d#eO`lmrZg2{eip-f?$?^u5W@3T^B$@ z-s?d1IY7aM;G>XYLW%o-$BuM?XYCiAO5`&`H6uw@iq?_?gqI5=S}@e(r0M|8UVDG=(fdZ2 z!@mxu02p4JW0IERq@rW%9KkO<4@-bwC41^pF{+O1bbNX4(EVqBoC9eK)i1^t{OZxm zLGfZTD@Qxejew-7VhdsBIx44>2H}DlHIPm|f)0r6nfley_}>1$;9s6QP#Fptbdw%z zPU3NirJ4G*U~+A0Ux*&OP!33%irkd~cSKcF2^JKU$Qlq)k8qw%{=R?zz|xg%*XM?q zPGJND?r7j>5rhkWYE&sT9A$^~Xm)UEdV!o?c^Llp+Ppidu*KA2PsSEdPJ8y>*QZye z$JV|$*mma4vcdv01uCOKJ*Bd@phiYXH&znk}#Q8RW>;L}%huZRG(7P#S00000NkvXXu0mjfQ-;nQ delta 2592 zcmV+*3g7jX7tvRq<)gotvRk^pVyq| z_3wVklxM$;QMH9Yt%zd2e6wKz$N`gZ0cL-?_^ucL000SaNLh0L01m_e01m_fl`9S# z000R2Nkl2n%a7R7(>J$OLVY!-_Jk}?F@j;dl$#&P1rl~iS!{MR#{OikIT zOyVWV;*^afTL>i327+$XO)vAI+2myU3sAg%ea^k-opW1MFGh`EuP%4YY{$%YHtm1T zy5He;zcF@VIp@g=y^oW&9mJllx2PJ;>cFaAjH-#fy4% z-60y?QJuY--ZLvZW@U%kyFaXpUT%L#fCKnY#$^Cxuc>x@xI?2ms=Mvzru21{w=2@k zhbnVvWG>6S`+MqIGoHpJB<6z*vC_&c@3!XmXMQqn@VG>Jmtj^8^>gN80-kdRpF&g@Iu^X~mD z6Pi|z7E+zZORbFA>boa@9bEq@t|p*-OvQ|Zl%Zv1XCzQ+kel52)yeKwjoqr#6}3Sq z2t}dwW&3cPLSunT1@w?Vb|QZX8Yor&L9H(cMNWoTm68RYw|4~qY0z}LxS1un^rj>( z!3B;x4;WaviWP>kLv_b%j5bEQhRqmvdPimYnMi?q8tVZ0Q+ z`ivjt#i|{j;KhAk=?OlT=_{8Id{`-7(>m2nIzSA31!FU@Kdic^YIBr(v*m3gxLCC( z5C_Es+w<$>U=s8I^e}3M7CV*7VQmbi@n^ZAl0f)(_d{W4IPgu1k2DJ zeJ0nhjdlWnVA&?p^S*)3w69&HZshI1w?1UBs3T&0*IHL*`&OZywnD)b2(voIeB^JL`sQ zt~q;9j`nC3BQZoIpbOwp;IthniYt0ND^{EGwoWAwlp_GGV$_Klgf6ZysM7)x^f8vs zHGo=IiR9&_@&y**q7!j^rKe)RNhSUD1GW(P*S&t$Pex+!6(l<`mD!*44Ik1Lf>4mN znc&5l)zK=`czl07R;fE8{pg-?o}N2{ugIiJYOQ>T5kU~J41ozs`HhXhUVA3xi^_KU z19xbpeP)VQScyR0a?h|M2w?=lu;UYNO2Tbyy!rL$9nm`Rj_!|BgVS)yoP33ulra); zqV=MVEIQ#fa{p}d#dj^(x|!LC*(=b~^E$ftc{yG#YCwOm)Csq=nf?*B#+yLp#CyKY zanzlu7$9~ji6EfGObjzQjcY;jIowbG(geQUc*WLAiOAmOVt6Tgy8xVknB5-9No-Qs zhOM7WE8267IHzg>flKH)a-`<=FUm7T7cuytQ}`af@ax|K@ab{;>*Fyf=rJ#Yt0h2G zM;3|(jNX5}?|`GV7tzubqkR?m-ZBZKiJCf|Mu@qhi+1_oEBGpS82x#_cPfuZOGkbN zLEX#_q9C>n<%dl5crke|<_vGIpBDEc0%yMO&yxx(8wIv!#3Vpeh7%Ttibnp?i{x?l z@DPBzFKFr++2yNzQ>NtNrzQ%gAb7vwB=&w}Zuz==jsrg(o4@?}5D%DbPZ&t)5!dh}A( z%KE9qQ?+@XO^(qnG9>0x z+)%HAk6v2>!9~uF9~}rQ{W`KY6JNP7s{()4`pQ9B+O6S|5~!+){n6|0{7^C8Z9kiO zbeCx>XNezp#6Vhvybz^m`K!b*QbS~KOAb2?OQP{lLTW9)O^79{ zbQ-sR3l?>pAF6p^N2g{nGh_g1VAJS?Rh4zL)h~!dPcOt^8!CvhZZR`(b!-_|_4Bk9 z7mjR)*-=5i(Dk>J`WA_02yO-%$?bpo>L|xZRo&CN+%=7?wwEFNzqnPtW#SvfY{ySb zimmvDOt9&?n>qe%lx7c4%$;FrW&qcnPaT=c<5oT~a5o85Kdw4?SG}sAmUYi`kqF_paqG=0O8j21xH?Argne z&!qYVw`81@jNzJeXlmWmx`}^ma>}Day<6K@7Q{jf58lNvw$Y-Q%r#58nM*d20yEoL z_f>2DR(<=lK5^jugd%Q84~c~E!En6uiFeR4vz>IVvuSszs#f)46uq%r(O~`O&3Jiy zQ~-_;VK4R}4$V(I`?QG(goscOB0@xnDmtSLqgTsxONmpRJ>8I}3G+P7Np@IB8%A(! z_?Y(-Di$lXCr{6+m7`+esN{||jNV$NyO!zd-2VV^F`()(HQ|W>0000L!nve>rVzARG7;niQ$PqxXuZ>&GapIo-CeCN6%?OU^JtWCC`x;a-wgvZQZ zwU(*peSleQ`sN(Q>o1m(%zLcL9m%n<>WkZAHqPmZY#}CFd zEUS6me5rwX`~EZDp$8^znE9qhZRYoEp%pp}9X6}huUM<^R;w&J>BPxIDb^!b{^mb; zX<#h(?d2H@%cA9n9*7?K@{{50JKcW85<9h3rrIkeNVArDT)lRDiCNjjFn2kVlnIe5 zv{~;@+jZi}VxuLdXL**JGHUV7X1w)0txS^Ny?DMtIXlCogYOm|Y>Qi#^u6_0WOl^5 z{gnkK*VAw6MxCus++&$Y!ugJudCTKJxpEot={f>t5AUB6 zxaWNPlkc)4^B;+J6kgD833)EOeeZ|g=XaXSF{^ge+&GC>sYPP_gWYrPi{|Y6;*+`e m?w5tPSo58Ouc_&7KU{A;`7P_iyu=hx%Jg*gb6Mw<&;$Szm{S!1 literal 0 HcmV?d00001 diff --git a/html/images/os/genexis_2x.png b/html/images/os/genexis_2x.png new file mode 100644 index 0000000000000000000000000000000000000000..b43f4dfb33072bdfc2a335a92e6ee9774108a09e GIT binary patch literal 1988 zcmeHH`#TeQ9R6-IlY5fK(P0~UcuhhI9(Q-Yi@CpbQob#YFR7PB1O4Y z6Kds}Tb2+#Z;P`tF(Z99+UYvE}3!ekJh#CQyMv=50ApjwKPjfL^MFllNv{ z^Nf1_S+e+1|Cz1NwkQ|h7ywN0KM5(axm5yyj0f(pz4x)SqT8R&ObX zx-UE$lfL|NH<$4cGKC#JJ}8^ZP=3dk#Xn9I`;=ng(wNKL5<|MrswdRmu$S8Y3bYKR zvGfUEIR3+k$qL{^H9PK$d|d}`>_`clM1@ps)vL3-67R0c7B;{ccgm)jN)t~uUo%L*F?YWHnU(XI2PD^Bq#BCjM$LH(LVC(JA< z7ZXrpkBe@_+wSVU89iiMHr`NO%X8$xVe2dNI`L6E%^|8jajzi7_vvy~^6?W3tT?Cq z-}sw~rf6@+9LQDfUl7_jP~x}ap-x1jlDD-!Q3mxZEg#<#0bZr2({r0jPvp@sSPomJ zV@?k>^V)wms^A?7{@jDq8hF=w-k}$WmUvd$D;fo%x$Yy*6fI@i5&LR7E^*~rBBUV0 zK|v8LsB_W)+|0hmq5oAp>7c)fg$?^pdw*_5Hnjh#|Xn1Em zApl#NyyB9*U#osTIp|5cdSlvX-|9;pk(ogE?1b!|#`i@1n4+7c&c`n*+E{+aDdD)Y zaEdNW5TVb=aE0>Yc6E*E(UWY8ZR&EY3M>t2gd-T{=cXl7oIoYs@zh?jFel`mBu^uS zRqiBcM#s*Ij|ygkxxPdlS>O6Pp2!=(O( zjSIKMgbNS$^TC#wt;>>!bP5D`+5jTga6&zkWsSI``Zt0+>`=C*G5#qN*64?9XKFN| z%9}G|EM;exm)a_{ljeGm5J8<@Ev?H6&98AxA*xmHpJ+!`O%8vl{V-4{Gl@#A3SFvCxaJnf`?H=6)!QH7&ot z&+QPGV{t_@t|JZ+d+@>&HjJ^oNBqLw4IRSCfUIvU49W9rs7CTl7oV*OGfjPQPhd3dqKdos;`xe1<01TNSXL1AkM-nqeZ?=113yCMj5$}a ubNI-#2CmQupq^uhU2$b2x93Naw53q*lF96>c?IpCV~E2N4_7*nQ~m=~|AU7B literal 0 HcmV?d00001 diff --git a/html/images/os/hardenedbsd.png b/html/images/os/hardenedbsd.png new file mode 100644 index 0000000000000000000000000000000000000000..d8819305beb152cf45794ce0cc5f0a8a0b09f3b7 GIT binary patch literal 1855 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 literal 0 HcmV?d00001 diff --git a/html/images/os/luve_2x.png b/html/images/os/luve_2x.png new file mode 100644 index 0000000000000000000000000000000000000000..3953e453ca5dccc40851aace2e9e799be76fd653 GIT binary patch literal 2413 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 ZlRq}QIHKX;3 zAu#+x;JIAU6QHknN`m}?89etJ$tAtutDbmqY4Sr35ui9{fk$L90|U1(2s1Lwnj^u$ zz|`;Q;usRq`gZEcq9+zSF7gL`yZ`_9j-9lFVTSWfx$iBDm92L930Jubc?q3VVKoPX z|Ll)Wiv~|kb%@(!IU#Q4+{beQlWaU(4^+HmTFKF@wL_8V)%we7Yo`8P%feyF^hvEz z=k~8fd?|-B)-~`mNVkPlw7PD!USay|`h`hfQiL7;*EP;*Pk#P=;f!AjoCfRncV|vr zqxbka>uUAa3CCvq)z>u={qxICDEZ*6ASZ4c^+WP6mgbuY|Ilppwrbj>t@Pt@^JlNRsGjg_FyvCB&&v9x2|7# z$*_~}gm(}x_kpM9w^{PY?RN55)^cfuvFRkMz-7V9ugN`56fbe`zrAn*YgCfak}2S?(ufvURg`~2)+y7rEW5B2|Q)J$!`00yKfHh z4ZK%ATz<6tDW{%7QnK~rB@dWvIXj-+aEsx;@$AFQE}o^HHq0CQu1tLUF!8$Z*~DKO z67r{h<;y4?Rh@P#H#b=QtIn(4zP>Med<)~xf5sDd^_t@;(S2YYidLNuMHf=o;nW;Tr@7# z*NG@;+QqLHbp2wKbF6Q9aAJ#2UF0TC#1jGx|7tmVZvz(Q10|}^>bP0l+XkKs{3Mc literal 0 HcmV?d00001 diff --git a/html/images/os/powertek-dark.png b/html/images/os/powertek-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..9b149ace9bedbb56102558924400e3eecf17ac6b GIT binary patch literal 1696 zcmeHG=~vPT6oo-2v^1@0Q!_Iwm9%oKES)j8lr(cYm5t?^=8}Rx6;MGyRKyKL#0_&n zDK`?=zFVx^jl-s8&=Zr>W@o1B>bRMinK?`r7KcZnW79L~2?P=;IW+9k;2eskymXE=VM@w6s(>G}yr72?Tt0IlHgF zFFZUHPa;z&X=pTxl1`P^;|OE`gGi+h?1F`&0rycm?UyKfWk*0B7}njn7FtMTA@%V?Cu?k$Kry5 z!`Pha>e|}uY})wrP;oJl$Dib=a!dL*75~{vB`4@=*TAe{uo!mz(AC!O>A}&hDUAl{_e7FIYkQ zb^V9^r_b%|+}-(RFCs`r)=#9%dOI+$4@3zFNO5-U|eTDo5RsImi}M zqod3zeFmAT7L0)t^p#hip+6jh#1+9iVCa) znA#rVq9>MJU(g+Z>S7jv$8KqU^+q=tpN9ELYupzN4|l5ftQlGMkL=oaHO%L^*u;fW zb)E#jr$EzUcD0EjlpdaY217ldy@fJ6xCXMos)CI(6;?*;?a8mBKiqKn)-VtN75FvY zzZP8!gtT;1O%lh3vQB9i&rj$AfB$&Z;HH(-?EPV< zT+>G9U9F1&KY>dmroVVj)cc#U4!+V+q(bmDepQQ85>na0C&$CqsR*Y|69i&u!5;Hl zMWDL-`PesJ;neHyK8||Z1O9XEK1xPM9+s4(TWq;`je2s|;difGe$dUM21QPFQuTND z``|1{hn5>pUDBas_d61D==S@jYUYeJ{6F^DovFHcCl3eM1X?SYk5#4bF8-=-=gWma zM{_-*{4yTruONRAgc?QMKr^7-IVPg(=ryFl*Lapeu+! zKboP(#|}NMc!bRJ1fb5d%3ePQ-M(?F_mUzAJJp8F^X}SQ1)Ez$zr~a_M;$J>?93P! zT;U8l`y4l6_q3R_@;$X!>Q$c{_mewnG$DWS+ZK8auxZB7SEgvif_>gzk)M{S(b1vv zI;-w8(+d}w7iqwj>k&p!l+^Y0_VPCCWXYPHZp(x({p-5*1)WXnSvVo~+9B+p+v+xW zhj~TG-X7KGs(TFeer=lbFf@6ls%bwDuCxY~Q2uy|JLY{d)#$>WZHocR(YLEfAQ^V+ ZEDj7KYLh9qL literal 0 HcmV?d00001 diff --git a/html/images/os/powertek-dark_2x.png b/html/images/os/powertek-dark_2x.png new file mode 100644 index 0000000000000000000000000000000000000000..777e8ff9a125c8e30c0a37a69bfa3b89bc552360 GIT binary patch literal 2996 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 DovQ9D literal 0 HcmV?d00001 diff --git a/html/images/os/powertek_2x.png b/html/images/os/powertek_2x.png new file mode 100644 index 0000000000000000000000000000000000000000..f03efa4d440462c36f940bbe66a8df9042155a9b GIT binary patch literal 3138 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 literal 0 HcmV?d00001 diff --git a/html/images/os/seagate.png b/html/images/os/seagate.png new file mode 100644 index 0000000000000000000000000000000000000000..0357b1425da656fc20545099ba559a03b76bfa20 GIT binary patch literal 1386 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabRA=0U}gyL332twER0@nowU_Caffr} zKCiUBo*DZ*6Sg^~?RLxD@0GH{HDkYL`d*Lh!#)}Nywdl&Cv11j*y|Cq(IH@tN&Hr) z$Q3pTTkZ2s`xl)Lj9TSTd?6s~fOqa8zwCoPnftu+j|LW>46eEs0km`!jE2A<41v49 zuI>XmOS&Y;FPMQl#OuxU(;D@6&GJ?+{MMbkXPcNqt?c#-S6%HGfz&uoAEN0cav=_$LoS}rzX?oRhtqQ1wGw4*XQ2Se%WWo|CHFgP+6(R zX=0hlFkudDx{8Rp3uyUy8ZesXZ6u$9H{d|!$HDd#w=m&LXr!G273>7W^`EkVr z(Vdl_SlAiXcSLwRN`F(ezjdlu)Ld(GmCffIFRNE^XiScZ?Zb$d6xbaa-s&U*LWM)b*%Db9H+GXO6q4=edks(zsu5#b_(AdTC?}wW;i3f#E!A! z<1w}+JPxK`)5Y#4-Lhxs$@+Wq(vq{OCsO0@9SG(1B1*Q{zHX3a%Y)up2+E*{p#=6Q?bhNE7b42fABZVU*io^;7hah{k1yY zC7T0!-tn)0&d;?pSyubdvoq6Kx^}Kg%zm}q<-pPp9R`N8a$ c`&aEhtS2q+-zopr0J63~qW}N^ literal 0 HcmV?d00001 diff --git a/html/images/os/seagate_2x.png b/html/images/os/seagate_2x.png new file mode 100644 index 0000000000000000000000000000000000000000..64d0eb749bc06af5709b3d732f5abcb63b2c9b15 GIT binary patch literal 2000 zcmeHH`#aMM9R9k6p{-G=qhn4bNmHljX2-^C*4CKI=;S)rZAgbo5i=AzBJHGuv(!m9 z%q2F%hMj|wTW%2&%Pn(Rt{HatGtTq$d7k(4exB!jp7;4Y?+>Vo_5>#z0`-z-8PcQF(wk@P%|-hQv3^w;w_*!w^;!Q)EUlX0 zS&9m8b#g021PaemYw>|X98HAx<73MUOmG*-Yx_E%GzeZp{FsB4kzH(GM*rltF&WL-uFhi@HI|XJicF;elo^%1LP<@f|Agymn2a9f?cvM0|R>mRmZijI3B7^-6W^v>nVC zZdtunuN^bjif&K>J6ca9rLi-1>$yc4TS?s4=wrDo}mdaB=Ze@C+~SfnTxfI29kj zNHFwJi+<`q1HkbFyM>>xqwXPJxkK$`xU> z^77Cz5kQ}1MBD|zgp2ykiSN%r_x{xUk$ySNSo9|^C*1?}`7e7vHnCFXtm!X+ED<8(iy_&RWd|c3vnP@** zid>@q$&w!XR-;{u79(_R$<{}cO#x!8$o-#h>E$K$?*E7s( z&_wb>uypXygA&uRuA$ojLcN1PymV&Gs>zbXpqch~=;y^SYY1SHFjfxtl?m$Pm{l$R Vld@7%85gnHr8pZRnq%d~{0}gfbu$0} literal 0 HcmV?d00001 diff --git a/html/images/os/sigur.png b/html/images/os/sigur.png new file mode 100644 index 0000000000000000000000000000000000000000..251881e9c53a1098cd193db1ed855def3d2fddc5 GIT binary patch literal 1306 zcmeAS@N?(olHy`uVBq!ia0vp^20*O9!3-pmxw!Qh7?>FXd_r7(G7EqF{`~9V`F~X| z|MN`#+&J{->E*v~AOAVC{{QX0e_!4F{r17%0@J_Gum0-{{a5Ysw>Rq3=huH8p8q=| z{m=2$zxK@eyJOm~Rqg-tP5+$R^z*>te@%YBw@&_-XY&8R!Y`}Z{}mYjO;!K7b<&@i zxqllx|IIJ{-{AGXKjDA0%)fZ0e_7i9B1HZszSnnUn|Saoaz1 zu76mX78U#9!j8wkfx0*gJR*x37`TN&n2}-D90>*n#x746#}JF&yO(_PZ#f9CJt!Bi z-Z(v>Am{RqR+U~Q*T291+b;>y*Oj^Drt&X=`Ph!M^1ZeynU7_(p11v)_syWcEY0LV zy?~kO-R_f9E`CnlZe4I(E>`2Nti0i7R72%#RvEs$pffMF8_jyDvzB3o zh})_sc|x22UY7Jb!T;boR|D(3sUG&`{{X4!135ZP4hg6UiGYpY7@S(PLrBZ^~k0{j<<#FjsI;){z*k9OJ1LKzrAY86}2yI z$=lbp%{w>y?bAH|8~Z947SG=G>@|z%=_R|QFRZ~<1Vek9@ zj|Ju({rf=X!Pb2;F}L=`14S5PmMV_vHF6*2U FngFp^RI~s9 literal 0 HcmV?d00001 diff --git a/html/images/os/sigur_2x.png b/html/images/os/sigur_2x.png new file mode 100644 index 0000000000000000000000000000000000000000..1fc75309f05160eb3e7938d6d9c35f9a486a8c97 GIT binary patch literal 1818 zcmeAS@N?(olHy`uVBq!ia0vp^2|(Ay0YUmu_REin08@AYSX z+3&9}|2F&mytMn*=a;`X_WoJb^6SZ!ANv>lu5|b_CH2>~X@5GS{x|vm?G61mG3oc3 z_CE<~|BKE4M~eJ+WccsH^fyi8zX!wL6xIKMoc}`k{>I7tcVYN%&G26b=*Cen8UpkP zfeAOn<$?C7l?3?(Gc+#@oA1r&(qNWxLoj^mclm$UcXK|;)tj)^e%kBP)dyxK&rjc1 z_IzgaQJ@aa0*}aI1_o|n5N2eUHAe!NrTRQw977^n-_FR+f8-|8Hed2~vcf~DL#CZq z?rMZy{r}&8Ro?1c^VJ$A4hj|t&H2gSn?0L3z4f(%wm+J>;dt@6IqAoWO$)9By30m+ zU0CJv?}fs_+pOAqCQtFxKHYrexW;r_mBXnra%={dPp3agOiG{h`o3c+iwtADwSz3% zf>~$hZh0nN@q2aRvs&k>!>i7ECa^eYymFLbNH9-|R{6}quzI_}afUB7X^LO|Fl2bJ zxor>^NSZg7J7d{Peo=O*@RP^SymjO9_}V;!;cnGqMQwv!dYzg*>socg4)lIBxxv1m zUP^VfWA#1zopo%BrgAatntI|_$cOvK&#E3+BDB6-U?!Wf!?KspZ?py3+KRrEbI6Sp zZD!@Sn4Ix(R_@%1eK!5kn`85OZ>m;)JK1btJ8|oYvyw%5_gL-q8IJi&f6SEHXv`+I zF_(S!?<-ymcMeYJR`EY)D;gw~&uyOUwlV0yqko3?cs;H+hPhNa{}cVGwDIcfzh})2 zuD`EYb?7d`#&zYPdAxuAEUwk>ejBuL*$Mvl9{ZO#l?i_lW_T)Pm4BW&M%(c8`+J?M zbT;3dz;fUfhoaS@ZJSx!a=kS5_j`W5b5e3!+e?Yi{x{nDm#5Eu9Le+|Zz|K-e(QdY z!rw}&Q|vw`-rK;MaOcm)_bwkruiiYCsK%I4*^B$1xbyJ(G^Sns-VQsj|uqgUh^yLKU3;R#K+gr8j-;9rm zlCtS1+db3+`X;yW7Th{MgHd1D_tcW2S^YEKr(EROwN7yMsce^H+1ut{Iy`Upf;vr6 zW|{AMHA0=Ygm%wa6TZ1~+m5|&>Tj^W_~EwqPe#YKFYk|LGs|9`^t{K>QimbyyZGH7 z6W2-hcIRwqxX`10EyZ|AW8~D$(>q_aE!6ctm#bM9q<3LOT6K?m#HO|BCZ{eO*ktuW zMg6P^-zCL{jFlg`6h20^KFyLnyGnht3crGmuiLU}bKkoH3xwJy@;_MFIB&6I`nuC1 z#ufi^?pHH}EYIp=TPodI*0M03F<{5fbIEeIS z^T)X#5d3hTQHC$bV?v47r~5xH+*r74N-2-1mC~pGcYe#Cc;G1iB7ef-zF)h(ShL!v V&B(Z|TmdRbJYD@<);T3K0RR?pN>2a) literal 0 HcmV?d00001 diff --git a/html/images/os/snr.png b/html/images/os/snr.png index 4c9d511751c1ff6832a760d106e467861d884071..1bf0de5ddac3d50f3fee004bff19de8e56addd6b 100644 GIT binary patch literal 1309 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 MJ!9P(ltb))09Jk(yZ`_I diff --git a/html/images/os/tfortis.png b/html/images/os/tfortis.png new file mode 100644 index 0000000000000000000000000000000000000000..8c04f6cd8dfc12bf9cb6ef52194559a65a893290 GIT binary patch literal 1301 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabRA=0U}gyL332twEKJVm&d#4uP%_!p zrO-R5+S#itAgtEar`$KVCNjR|_MON6p|$RQ6+S`L;jv9w`TcRp?Pj*Q#TC;7!|Q?~ z>VdL8K~klPm zba(VDmsSp|s++T5@s6iYUl*58zjE!~?_a-xZW{%oAwbU%SQITG3$#z9B*-tA!MuS_ z>1^(&Rh@TP9)#@u5PS0cf!p0KJAv|?1s;*b3=G`DAk4@xYmNj11LJH@7sn8b-qOh@ zi=+*CT;v(fy$g)l_hiQZ|68X7_ldi1p5VY|JW(X_7^})|#|4Z*J0gPCrr%N78!tOA zeDRifUcFj!sb>vj)^mJ{$W&y0=i&Y9rS*oL?=IS|+tle&d3w5t)w)0Ff>F|v1`L1Y z4)(U1ht=#}xV!iM<&bFy9p?QI*>Cu3j^T^lMdIx5S7{oS{p6M@Q8eV0RN3jmd}j9S zMbX6@>a1tl3OVc$_O{!7Lat#-icrE?iQ;o?Ymeq!d0TC>*C$|8=-y_d>1sPy^&P(! z9ed~UByJBCGtSbg%WM^I?_aeO(R{>^9dvVcL5FBXMeK)%E4lOu->-jsi z2ixr#GJq;L3p^r=85p>QL70(Y)*K077RvW@aSVxQog03#=(dGGZH5lZR)eQ0M(dVj z@qPaP-&D_k%@U<35zmGfGCMj}yS|#+u~c4qFB6mZjp~HyOchqli+H$iANjN8>8IVy z8VoLH6nl48&3`&Wc|%Mca|q`ZC6)tILPT1lofGcfpL*0Kl}qJFca{dn+g~#dWF<>* zv#UL>`B`nD-hMdwr)$xJ%E}G>3qnm7$Y)GAAyMb}gYE0${H}d$zg=2X^S>rMTC+T< z|A3r_j^_P#bG6yZ9*a7{|HwBiZ#m7`xHWL=v;PbS4lYW~;QVHNIQuE%iH?``;XpF*WOgK{Qj};xBH3t`JGJFceVHweoHa-3O?4KEz0nK|KL@r z8x_res}t^K95|WutA<1KfkT67)?#+43SS1sKP&g_uM~4+Vwv*Hkd>iNSmVZ$Vn)Hv z0M^&H7{ueA9~Sg{bb&2*g1EqsG)AtAFC9KyJip?YW>LZ4iHin!&d zseG$5qCD0L2d?fpX_#ZYc!r>s&+Q4`yAI{wPd9nd&iVFF&WemRvOb*{tv4 z3=8dB&wBR%Y2bPhOy!I_ODG}ysQOU_HSvv zSG{n;M=eo}KlAT@+WtRD^HcMLa?2%^Z?9iGxLDg_y?vU3(tO^kixy4atm5h{1Oq+v z_l9yA^ZUQu&uaWRL@l^eYis`Empdh{9h6aRzx`T-C0Hmvvj6CGx%<<4?eDGQ6Pa+! p`0TcwM|C#UcyCN*+Za;upJ8Th`Lmv`ZK~4U_-? literal 0 HcmV?d00001 diff --git a/html/images/os/unitrends-dark.png b/html/images/os/unitrends-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..d9c822bbf3f64df84b76f3f6be2f449b5c8302c7 GIT binary patch literal 1039 zcmeAS@N?(olHy`uVBq!ia0vp^20*O9!3-pmxw!Qh7?>FXd_r7(G7JBc2!JMxg3%Bd z9wD&Y=-x!2PXtPW{DK*(j|jR3w#VnqGgRIF*7|;Rina|jYTBK$?LKbX6ZRq%u7wq z(tN&m)32S8!LI@~X|k+{CK+hxvXaMI literal 0 HcmV?d00001 diff --git a/html/images/os/unitrends-dark_2x.png b/html/images/os/unitrends-dark_2x.png new file mode 100644 index 0000000000000000000000000000000000000000..47fd40496294e5ec4eabb367682b99dff6983cca GIT binary patch literal 1269 zcmeAS@N?(olHy`uVBq!ia0vp^2|(3%=ZGyEk%e!&bMo+{l~RK1rk>vYJ*J`0!dkOZIEaj?(fan{*-5t*1l%rqe^yqBtor|aihFaH*ee~InJGEzH~AEX1(lR_pP9UT!_I?KfcBcH^$Su#enS(*P| z5utU*?d#5m*BTbyfBTAO3D@r^gP^SsJgs6}!@?i2KB~Fb#Umb2w9;}b*9C`ZpP5`z zcdF-io>tx6zFazYP4b=nYF>r6SVYq1?RS^^Mdrv&d*oY{?XafBl3+!RP$4bmnQ3+@A-DGFXd_r7(G7EtsqhK@yMrH`q z<)m*0`dqLi$S;`T-wPw3!k%#3)P|KZI|NkcE?o8C7EqG2z$3Dlfq`2Xgc%uT&5-~K zPV#hd46*2ad*NW=Q3W1`i=rOWY@bc|_dBJj*}-y2`=9Q3R%`C{*V{vmgmZu2@n~96 z{LE`oxg;QG`KFmC@ard->QEoN!)|&mhbQH zECvDL+c|O-e$}k~8J^skb}%h6Hz4ga%M1ZQ|BhqAi>!||y_$D?LuXya-Pd2`>lib> VIxK7aGcOzzlb)`AF6*2UngAlpWsU#< literal 0 HcmV?d00001 diff --git a/html/images/os/unitrends_2x.png b/html/images/os/unitrends_2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d4dfe5c034fb400bdddbe9b982e72cd63b1cebef GIT binary patch literal 1254 zcmeAS@N?(olHy`uVBq!ia0vp^2|(XyoPzHZwPFK4sKi?kuEZ!LTrM)-oXaX)jUY zC;H;-i)QAzOb>F!pYIdV4|Q?b^8S@=wTsN1VDp(A4U?@>)(HnQD7%G*vUl$eE|pm# zDPiVu`J9Tmr62R$Y}R-B&r#jTbZ(#e2DwM)CwO0xv=TFui_&ATJL~qF6w*Q7fw1imIOm47!nb^N7j$bp>+qAP5bxojD3 zpL=7Ur*Pw3)a)QxP0eR>oa5dfPy74Jvw^AknSc{Vkj~24AO7s}TI?`o-+v#5G**G& zd**$MPv5_F<@eq>k9RPw2>$i>*X+&vviPOd>x+)8j(?t=xni-n;KhemPZ{%j&E3<{ saYcF3{N3O0dH?g)2ZtRf_{{e+9+#axb8S)jb5Jt%boFyt=akR{02%Stx&QzG literal 0 HcmV?d00001 diff --git a/html/images/os/waveos.png b/html/images/os/waveos.png new file mode 100644 index 0000000000000000000000000000000000000000..2e0b6571cc0ea4573cbcc14a0fc98e5dd35a73bb GIT binary patch literal 1311 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabRA=0U}gyL32{}5n=2DOlhbFSR@!2< z#07FOv&BND0R@3bIes3C#{}M>sSHj%3|94QUR{aZI~jCKg#7yFY`@Im+`9MNqosSV zZ#s6@uW7S_|3s(K)%jER|NHm9Z_SzJB_~ebe8Il(o@L&0(X6GnpS)+T+GacPFn9m? z^wUl?_Z+v<8>6>uJam^#&MHA+< z{QnPh^C%b%0a}DWtdgw&&|Hp^Aiv;G|H0tEy+>IVKrzk&kH}&M25w;xW@MN(M}mQY zaigb;V~9oX)Zo*7!iEBCw%sfO94J0ev>w{t4oNpmT`sZhfAfz>hQOZtPQOk!VW>zIrDxiYo(VypOr zo=In)dcB(e-i$rfa^r$49X;==|NPj&dwhlZ-;4fIYxn7W*clV{`tZSvO3d*yoM(C7 z_J18)BreAC;9}@~V>db3f-e>t6NFA{y~#=WyWZ3KMXJZ-qZnOMasotO94tLhcDc?&QEvi1fI=fDUVd1ItiYIHgC*Oa$ zPfkgfZ)0QO;rPbn%I3jfz>>qG;K8yv)zOo!Vne}%ApU%H4!2pG`ArNI zctjp=(EXNyLSpZcpUb6J+xMKZ|E`N4Sg{hf^08y=J{e)~`F%AM#F&BFX?x7_CS z{jD?6|1Ej_(azn{J2*sh_f{AzoNXz#sZyduIQA^}dh0;bAW{Bf0Z*@r{Og>&ZrS1a zIj_Ipf7Ug}s?cc#gQiMAzDiDGQM_~e1BTE|Kkm;i4PCFN9~b?9|Ea6Tm1gD{iGxy) Mr>mdKI;Vst0K4BkAOHXW literal 0 HcmV?d00001 diff --git a/html/images/os/waveos_2x.png b/html/images/os/waveos_2x.png new file mode 100644 index 0000000000000000000000000000000000000000..5358ee6a6241393e5ed9eaef58a98ce66bb9fb43 GIT binary patch literal 2065 zcmeHHi8s`H6#veQWh^~vEYr{=l6@cEh?uN1q~=(%%OD|JnQ3I5OxaaK3U8EDct}G% zQc3ow$uf!(5<*!rlu7mUd((OU!21LGoV%R!x%ZrV?&sWdv&aV>w@6|n0RUS_L_1eu zCj4|HLKsV%&MX0d0OSMi_NoD;=)*-Ser#o*5{;niy92L55F{l3Gdb5nm<6{OP+DWO%LW>xJ72)>ks_T z8&O}NvIoPQu5nGbg1F+g;JgPBE09>lv}$a%c<3$K4~WPa&FAzpZ~pDsyM!2kEQ@<| z66)27SrW_+@d6&)s|a0t8|jKhbwZQ7O(>Ysh6>QjQxZS{D)ksyG;DwGOA%IBXI zF04hcyM){Q!Y>8>zXFxL@Hk<&1l9GR+nVt7g_!ZZ-|rOilZ+*L3yV^ypAG{hY%~BO zeLkn(p*a{!Z|A(ON0K z;Z5j1hN`|R_Nre@x5@~>XAGF4uD=Xu3NBg)()XI~-ZtBTqb!5p@vOMzxj4&A#Pu<~ z=-hUlv?+4|YGG_^w8-1L9sIVONbC2FYU8pFAd%5?@0EF0ZFqeEmjHW$>NHQ8pti?B zm1x;4P;_omEhB4k?Xe)>PI~PYf+&G8K~@X47CYMfun1@kb`D3O(T=&!J7;N#1KcG_ zX&C|g^;{agCLnC}Oh@n++EuI=A}Ay^g1S#%*Xx4qhKE6IXnEYbVS4sLB$ZuhG1J>d zF*Efr*t1zZEtpA)VreJjn>IWuY3`J)U~mVZXf)r`#(Ys@4|ogS0XT1*q3rv?L7zOq>w#^GS>Qn^#w zSjEYY6uew2CVy7Sd%2C7EhaFNCMQTlRUJxp;rJD>$jS;Tdj7HU1abpKP3?Jw0I856 zg=@@T9e|fRd%88-vvlRZ`&YEk>^Lzr=2==zc`aqJ5EqsQ=krpot-+za~bG%(52bq8|h7*U(et$fVxGP+0`ZV~(;mX*?V`=Mznxp1xAPi~pkW zeau7c#C_z6_K?Cw+qGlzcVO50h+L>-L{O)xs&Ol0YY9`15f&z@SX**8#krEg?6RQ7@||*EQs3vZTTHZ2Xt&h+c02#eV( zWANnyD&-M7Wi^z4(%iYv1|Fa4I-DrJ;eIYKZsw`vjZCK}I5tsB?M08w{l@WSs|&j6 z<55vjytTCH2FwR{$7A8^z1+l74*grK5!Uc!ES8IRK45toZ`}RIl+tTkeG5|XD9OeY zpxTj`$SKMJck=dT3JkCsj%x4~7v!+MsVcsNJ z>EoNQMVFSDquLL?zS(VY^@2u1cQsvN70Y-Ky@IT_-e29c^pS_{p7$;|f5g#bQJz%1 zyNwq+WPT&p=(u*|p$U(jV2^%7;`7tp#U=JGsoPMYb7M2V;cl5)i(eEtoWO|#DqcxX zXFXdr-1|F6tVq<+OTv{^#!6<(c3M>u#?94Fh4 a#KaXi%acfeE;>NSB&-qu&c}x zXrIaXv1E`vg>iYbxdHWmrh|Vz(T{yxiQj|}9&l~aJeu3_^9z#f58Bn)1g8B9RW85c literal 0 HcmV?d00001 diff --git a/html/images/os/wisi.png b/html/images/os/wisi.png new file mode 100644 index 0000000000000000000000000000000000000000..2d9686052a57acc07af699e1285b7afb77f3fc24 GIT binary patch literal 1176 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabRA=0U}gyL32|kZ_vrus{|vJpGE9GP zl|No_XpEPafpV6kW#rP_> zocFEN>q39D(>!O%uiIk1%1HN$@rm_km6osw1l^HdT)SCyxt6D%o8NiXBGItxCnLYj z@U*U(GKpjBj7eT5lUNK&x9*N~k?`1cLv-q^um#;Un@=S%-%#n^{(6z`GM&@bM_sn+ ztY@9{YGH1WY10Si&$fGhF1^gz8MvT_obnc#M zU1a|0e9eSh0h3z|;%#xWR|zuBXI3!IVVyqz()vAzd}l9z8oEs3>i!dJ z-!Ix=;}N#+`Ejk%e6iewv}sz`%?u77I?Hpt;IE;I%(ovKCrWfad-f`2YE?bgYWuY< z7k^)TBB^@HYO`_Sg4OZjZO@NZM(q{4V*IfAYV_HOOFnsjn*M{Wd|TOBW5o;3pcLcj L>gTe~DWM4fg9*!1 literal 0 HcmV?d00001 diff --git a/html/images/os/wisi_2x.png b/html/images/os/wisi_2x.png new file mode 100644 index 0000000000000000000000000000000000000000..543349b2c0594cacdf37274582b7a33d7014e15c GIT binary patch literal 1919 zcmeHH`#;kQ0R8U4Fz+m;hI!p0k1XXPcihFsFxvXuS}VFEi4xVPRb1v7K9MVIYi=cF zP1q;9DzcJEvc@F~ZA0A<>tU8hw5xyPembAeIp?SI!#O{k3#5n;BLiy#004{#cwF?F zW4}`mvZmaYtXTkn03;%L7f?N+)oKCOIKZ9wy!;Krf7ku*LP7UOR^R9QO3fbO^zet} zTSIfFJJu3E@MD4h+X90NGQnCW{S^G(Gys6F`%VzRzG4LcP$vQ|ko;S&r1BY?A8eSb zbwJ!@Xkpq(^x^ON;Ush@epw4*{2^JUr6# zc?|2K$VBq-s8#LTu~kX_&?7%ycA`&NW!5&P92NtIkfTdPMV%A{&r#XAJI<9)btAN% zP7me9l#Ubrc_w&wmT|bR(S^vI+^Br(^wiNQ!4_4=9SDEIHQMn99Zk+d1|6L`k~Jv7 z>bpf*T70RxPovacAx%pM`^zc6AKEM+62Jwq<~QtfYAs>9F35*;XTs`k8$*IuWezWQ zGO6L>OLJ$QuyV`o_fC|0>tO411&@95^`v;vS=l48L3!b$BImhZO_K|qsaOtVivo;w z#s%i&<}rxsc;C{(P9=@YZZS`v zDzWhKOi!0{ubfyMeLS>;`LtT}8JWP2A2~G?1Ja+El$j=>FmjYwS}5bphz9#Q*~K5C zl5NHjR*L@K#J9r}ODmfeU#c`cdk)yGn&clFtU|hm2JDZ`td=VK>I)_XRjVGJBsCwc zqIn-Dj|gE#b5P~BeCtpKVIJB}N$G0*xnfwSQ2Zv>_*FI>!QhN$zSe2IbXpVAD94XH z=#qC)PR98{s&B`+z0h?BnxSzD>BKxrMcz&% zpAr0$3kATZ4i5`0=}4(YM8Zm0+YfT#9xn82&bE!!=r%nu_~ph~1U*=cU1@r}8p5c6 zic3AdTI)Kc-uYeZ z+-D`+F4`EMhTR%*k}7=NbDXyx;lVd@{ffHqDue&*`qH$jD8vgtW9*#-uV7HZqU9W; zGV(we$~{&SuKsq@p4`<)Ro;_g`RX1LG{=%T+H-!gehVA{t@D?>cbO~?6f?2sB40ZY zEKE!s_UX}r{D{KB{WB2-m@YCC}|sp(L~d9DNngdXYa?d1phqR!WDOhAw6YPGwUVTS@p@iPB%@b_mV M?25oO1jR7_1M%%UrvLx| literal 0 HcmV?d00001 diff --git a/html/images/os/zyxel-dark.png b/html/images/os/zyxel-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..c09d84b0d6e84df3a1b3e15854e74afed19a1d7c GIT binary patch literal 1215 zcmeAS@N?(olHy`uVBq!ia0vp^20*O9!3-pmxw!Qh7?>FXd_r7(G7JCz|M&m@|Fkm< zKoS=K$|jy>NI4Bv6LyRps1hg$L2z9_Zrn+zn$$But46_S2n^~FXf9TH4s@VwNswPK zgRshqb| z85kICJY5_^EPCHwxaoI0K%n8_`EMIDeVoJ&go@t#e{|N02}{&gJ72rZ9jy4SI=J(H zmYebZw1?gL|9EF6@+gYze60``eJ5>tU}0ZDd2v9SPD?<{on?kf8LPtnuAHSPJM+c& z*{nAcPJU#cq2ab)nx(z-kBv^eE`R9W;>CTX>e|(_kDt?;aAQqJ&MwU*M=IV-KOfEK zHcu|idReus`*o>nhR6D)gCt6xtH)%|ZB;z8{QjofY0SSWC0Nfm0KEHz$28}d6RKU^%tYF$84naJKrS=RlhUzZ;5gF&+1xa wxp;b>e!$+fQ$Nm{u;=K(hqEqxKl=Zqd|h+>9YZn2eV}CG>FVdQ&MBb@0QNry_5c6? literal 0 HcmV?d00001 diff --git a/html/images/os/zyxel-dark_2x.png b/html/images/os/zyxel-dark_2x.png new file mode 100644 index 0000000000000000000000000000000000000000..e7426b2803f88e53d26adf835e4b12a2f2d07f42 GIT binary patch literal 1515 zcmeAS@N?(olHy`uVBq!ia0vp^2|(U6T^K!^ylP9($MXmtXyCk)Pa=dQGrlLttyef);_${M?pUhfAUdv`Txs#lo02 z{*rTA@AoNDarSvI-&#j2Cp&a9on{s9A7o?c@ZF z6TzlF45yc~Y+?KH>AOomyMRl9HlxKdPL+ofb2+B`T)o+HNo};}gEeOzm}g5o;tlvJ zRmu8;Q?4Q7zfr@7#@8ECqL{PKN8et;a87kXUO(%T>Gq%Pr)hLNFsZv5{)Mr61FMl` z1=o`4)wvIUEa;TJVPljg!Bpd=P!^))z@yK+mMx@eZ`F<*#t!bY+O?h6ge-Q?UvRc< z%j%>ZA`T@9WsHX`7ji1FoMV!BC4C@Q>yDa3Wy-9^hhb+qehaY*PC5U1>fg;3LMw{R z-lj}WD{ri>iZ=?7UQiu&LEXal^eioh?U_?l))qm#4R^(nEb$0$|O&P*ciJDxav`RI55!R)f1Yk&Q{b>rCA zp34vC>|=Kj?NWcdYx;tk%NM>cUi>>hEc->lviGHj3%6Z+THqUb(sS3}FISfuzkFD- zV^7hPw0%cpZP&evSYgPRnm21+d(rzRpY>L7HB>+D6)R;=S*kqqwH!AD{8!%3pqP8V UEbd0t15i%$boFyt=akR{06JQHP5=M^ literal 0 HcmV?d00001 diff --git a/html/images/os/zyxel.png b/html/images/os/zyxel.png index 003b4fe42a73f3fccfa2474547d7e3ac20f8dee6..12f5dfc9a9ff0a8cec709759bc1aadc839a12753 100644 GIT binary patch literal 1227 zcmeAS@N?(olHy`uVBq!ia0vp^20*O9!3-pmxw!Qh7?>FXd_r7(G7Ets=ztL_kah;d z$h?G*hlmtDVnCAuazW@UL;hVb1FR9E2BCSBH5vkgECl+_@V@~%Nvw##0Pt6on3E+8Y9@%-o7uPKGAWOHYy2|oJyOs!T%cz1usjn@e-mrbVsj6HsE zYQ@HNl{s&1XZ~$UzHIXUUc=LpyH56*zm?v6kcoAF{6nm8VZXC`N#uM%fnHw0(uf__ zO{;YJ@3)`Xw59I0fWc;4{@7QkGt4iS#IrxvE2CZ`Ph07a|?MRwuR2 zpSY>iWt;P~@BTC1U*=jiZ|TRME|<)mKYr(z|C9fRF@0)n_V$|D!0=^Y@O1TaS?83{ F1OO!j-t+(f delta 1006 zcmV$~5=3IZ50zm-64gT#k``NT=No74jyY|;T8bzCE6m6nWw24O1CK^SXDBv=FH0JsFVua_~ zxVt!F9zU+l8Gm-tU2`Jax~Adzx~4ai*YSA0(oA$c-cCL@<)?K$-k4oLdI*RYVvG+c zqQIa&f#|Tz0ckIa%2Y=boA?pVW&S>w`Ns;+{EW0xyg@bNO%~7r&86v`&{cs(;fL#u&dS$tx|eCdDS7!Wx;xFz9fHA#d^xbB%q=t zTZ+ZxG|!$z^_ONUQa$p~9BfWoPjFBqFd?4I)Z&P~Ye`=^bsh6$n2z!z>G;jXnorFj z4V%3wynl?pNVMJ&-KFBtKZSW1AB#2kF2xiaFIW@C4MamulF#-0brW^ zA@?A4;yHHqp%~p1UqXc|)0w%2WFp}R{!lH9HFwTN7w=0yu3}~r`3bmQ?OLt@`ufBh zf?7?RL@ex~oV(E!59#p^QRH7ipEoRgOlRA`5gD4=7=K}TFLwP*Egs!@RIxQ%DlJnR zSnPgzY+teXH@LIss&`#3m*M{l(|>q2(J0zPqi7S2qD?f4Hqj{BM5AaEjiOC7iZ;_Ga>;M1& diff --git a/html/images/os/zyxel_2x.png b/html/images/os/zyxel_2x.png index a62a071bde709d7a06ac3d757428f544cdbcc078..c5d3bd7a25e8998ed944a1d38352be80a62e1dd3 100644 GIT binary patch literal 1511 zcmeAS@N?(olHy`uVBq!ia0vp^2|(O=u<5X=vX$A(SY)==*kcif|Q*TdNWFX+cuNZUn>cyG={~wS% zD!8*l@eMOeNAuIXtNU{OL|I>cX)UpmEwGT`Yd`$(hj>LxoN(Xawgrs;5A2joJ^bSC z`Y)wMHQc9V7;d&rGkmkus<%q$8D-LtT0OmWoucbKJkFPnfn`i#q@7w`WCJK|e&d9Uz zZgJ4@Vki>~=;J&c!s4*nwBKV{O7(%IY_l%QwOnYp!Jy6>lgjc?js2S7g(r&_zI!F# z$D1Vfy7}L8$Jhi>29^V^j0Jj)CG4+71MbiJRU>rZKJ(8Fa=bah9@~;HBpOw@Gfma` zZ`9Di7|Of&8B50&{|hsYtYQ0ecqdEklk*>rua`}^8zH=_m%~-(LGiZ2*U=2-`zNej z$>YML$f5Wopx=?D*XZ-5J>MJ5qqmt&J{ZC(bKN26^IRkMZ|R9;7V2#xkQJzaFpm`X{T z6HDIP%e@?{3mj^o4=0o!>^R2LKdMww9!G*FENQ!xB}U zwBGkTQt&?ePs9GFIrUf9Y~priLp3{NTN25spJ`K{XcrCQ{49}f^59N8#W*1W?p~0Nh!Ix~|EOaJ(v~gi`GAxok$vrw1 zw0QT>{no6gT_%1;3Yyi~v5Kyr-pFGnJ!93_&q>MnuLbY47#)|C{773Uuug4{UXSFPKdBki#|R6<2# z&RX_g$k)#$nRvt#-CNbo!FSxBW(eptg4a>{#-D*w3-R^W#+eldD)YL7w6Dup7Sk0x zuX4!PT9?Rdnmtuoekd~w^;xgE=nau;j#M_jvTVVh z$U;$Ytolojn!i{MHj;g``EqSh>`Xf;{r*&_KpasvOh`>v2y!hL+eeBR_G$(5120wi z8ukUlTb%O|>yDTG_os;RMr+oS&s5&C8Mx7Tke_ctLJW;A=951Vn$?RmpImUBpZMc~ zuYge?LY`-iS_2n9O5`+Xd*F20uMVKisw8(DOlZiQbA32`ZQt*3)KSpY<1V{nS^~XZ z;kqa8urxmo5Rx3ClxrpquOJgJaB)QKc(oIp!J_ac+Ge>;6J(agLAQvSGZg7$8*=4x#lE1%FEn=l1PTUQ@katy3q8o7+d4nj2{uAtf2Qc6`mLuiIIs z%E9Y>Q;c{ZmBn^TKve<8edVxM{$1X)Bc@6q3J<))@8o#$k`qy|o~vt;a{JzvQC@m< zbJXp*3UMRhu|w^jU=R0W$OweZb$0*k)b?99e<};*>k7lODd$wp`CakG2R#miIjP3@0jWyGiPQJK0=qNPbsj%c4`1~RKdEQxReHYL zLy7Slx)F9l-E0JO-uY~4hq#7iX|NP>^Ti2VbnQRz@zPi-b0(k^lbv6<8^PcM1=Rup zN=?sxV7eDlul+1+v-rw)|BfSJoY9PtZ;z^LO_oj2x%bpV>?d1wgBALMTcHz}aIB@s zMi9PCb{ao&tH^Fa>$q^1#{Y7zr%05p(>49RvAe*Z-;U8&Kc1m!9V)IFdK`LM;JB1x zz6;~Cu3_^5^)buMVWEoi5Jo;}Cny9L1>*A_>lBK_n7w^$Xs}O+!w(Ie+tl$n28~&K zNp*A-zW$mgG+JJrK^zDxtUQyUuQ$E2`=Y#=ZX$EP8R2T3|m#`lws4w#zCuWoVF7+41*j4xeb;cZy}{!4*EB-8fY;kx}d?NJnHriBx6;vUXMl)I2quh|COln%RULTh;n{>Kx#<*_jGjXLmf>a{g2|?y zuM!bA(l_Nm;LQ>|L9Tw!OZ48Lc`goj2`cxtM zZCY`?rYx3i{_w;HrE3O*kfrvn`oc$HOGgd(NXe(+uNhM1$y|q2)Zq}ZYa3Mo=ZMBU z*)nVXGmq->b5?Ec;mRo(K=}utufjfI+TScP6iHc(K`zBNIK`qNHF6rE$V!Fh00Qs; zcOwN4GxbcmT)~;A%s%hgT_y57je}RzIjV6M1Rc5W0En9yZs{mUo Ld&^3)XUx9=YsR9= diff --git a/html/img/router.png b/html/img/router.png new file mode 100644 index 0000000000000000000000000000000000000000..7eb6753d78c9dfbfb4bba9fc34f5dbc704e2008c GIT binary patch literal 9672 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 KMNUMnLSTYsnK=^x literal 0 HcmV?d00001 diff --git a/html/includes/actions/role_add.inc.php b/html/includes/actions/role_add.inc.php index 836fc6f4..2a4c9b3c 100644 --- a/html/includes/actions/role_add.inc.php +++ b/html/includes/actions/role_add.inc.php @@ -1,25 +1,28 @@ $vars['role_descr'], - 'role_name' => $vars['role_name']) +if ($_SESSION['userlevel'] == 10 && request_token_valid($vars)) { // Only valid forms from level 10 users + if (!safe_empty($vars['role_name']) && + !safe_empty($vars['role_descr'])) { + $oid_id = dbInsert('roles', [ 'role_descr' => $vars['role_descr'], + 'role_name' => $vars['role_name'] ] ); - if ($oid_id) - { + if ($oid_id) { print_success("SUCCESS: Added role"); - } - else - { + } else { print_warning("WARNING: Role not added"); } - } - else - { + } else { print_error("ERROR: All fields must be completed to add a new role."); } } \ No newline at end of file diff --git a/html/includes/actions/role_entity_add.inc.php b/html/includes/actions/role_entity_add.inc.php index 97ca4d9c..30615e37 100644 --- a/html/includes/actions/role_entity_add.inc.php +++ b/html/includes/actions/role_entity_add.inc.php @@ -6,42 +6,45 @@ * * @package observium * @subpackage web - * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2020 Observium Limited + * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2022 Observium Limited * */ -if ($_SESSION['userlevel'] == 10 && request_token_valid($vars)) // Only valid forms from level 10 users -{ +if ($_SESSION['userlevel'] == 10 && request_token_valid($vars)) { // Only valid forms from level 10 users - if (isset($vars['entity_id'])) - { - } // use entity_id - elseif (isset($vars[$vars['entity_type'] . '_entity_id'])) // use type_entity_id - { + if (isset($vars['entity_id'])) { + // use entity_id + } elseif (isset($vars[$vars['entity_type'] . '_entity_id'])) { + // use type_entity_id $vars['entity_id'] = $vars[$vars['entity_type'] . '_entity_id']; } - if (!is_array($vars['entity_id'])) - { - $vars['entity_id'] = array($vars['entity_id']); + + if (!is_array($vars['entity_id'])) { + $vars['entity_id'] = [ $vars['entity_id'] ]; } - foreach ($vars['entity_id'] as $entity_id) - { - if (get_entity_by_id_cache($vars['entity_type'], $entity_id)) // Skip not exist entities - { + $changed = 0; + foreach ($vars['entity_id'] as $entity_id) { + if (get_entity_by_id_cache($vars['entity_type'], $entity_id)) { // Skip not exist entities if (!dbExist('roles_entity_permissions', '`role_id` = ? AND `entity_type` = ? AND `entity_id` = ?', - array($vars['role_id'], $vars['entity_type'], $entity_id) - )) - { + [ $vars['role_id'], $vars['entity_type'], $entity_id ])) { - if(!in_array($vars['access'], array('ro', 'rw'))) { $vars['access'] = 'ro'; } + if (!in_array($vars['access'], [ 'ro', 'rw' ])) { + $vars['access'] = 'ro'; + } - dbInsert(array('entity_id' => $entity_id, 'entity_type' => $vars['entity_type'], 'role_id' => $vars['role_id'], 'access' => $vars['access']), - 'roles_entity_permissions' - ); + dbInsert([ 'entity_id' => $entity_id, 'entity_type' => $vars['entity_type'], 'role_id' => $vars['role_id'], 'access' => $vars['access'] ], + 'roles_entity_permissions'); + $changed++; } - } else { print_error('Error: Invalid Entity.'); } + } else { + print_error('Error: Invalid Entity.'); + } } + + // Reset permissions cache + if ($changed) { set_cache_clear('wui'); } + unset($changed); } // EOF diff --git a/html/includes/actions/role_entity_del.inc.php b/html/includes/actions/role_entity_del.inc.php index a1c3b32d..9a5c5060 100644 --- a/html/includes/actions/role_entity_del.inc.php +++ b/html/includes/actions/role_entity_del.inc.php @@ -6,32 +6,31 @@ * * @package observium * @subpackage web - * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2020 Observium Limited + * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2022 Observium Limited * */ -if ($_SESSION['userlevel'] == 10 && request_token_valid($vars)) // Only valid forms from level 10 users -{ +if ($_SESSION['userlevel'] == 10 && request_token_valid($vars)) { // Only valid forms from level 10 users - if (isset($vars['entity_id'])) - { - } // use entity_id - elseif (isset($vars[$vars['entity_type'] . '_entity_id'])) // use type_entity_id - { + if (isset($vars['entity_id'])) { + // use entity_id + } elseif (isset($vars[$vars['entity_type'] . '_entity_id'])) { + // use type_entity_id $vars['entity_id'] = $vars[$vars['entity_type'] . '_entity_id']; } - $where = '`role_id` = ? AND `entity_type` = ?' . generate_query_values($vars['entity_id'], 'entity_id'); + $where = '`role_id` = ? AND `entity_type` = ?' . generate_query_values_and($vars['entity_id'], 'entity_id'); //if (@dbFetchCell("SELECT COUNT(*) FROM `entity_permissions` WHERE " . $where, array($vars['user_id'], $vars['entity_type']))) - if (dbExist('roles_entity_permissions', $where, array($vars['role_id'], $vars['entity_type']))) - { + if (dbExist('roles_entity_permissions', $where, [ $vars['role_id'], $vars['entity_type'] ])) { dbDelete('roles_entity_permissions', $where, array($vars['role_id'], $vars['entity_type'])); //print_vars(dbError()); - } else { } + // Reset permissions cache + set_cache_clear('wui'); + } } -echo ("nope"); // Hrm? +//echo ("nope"); // Hrm? // EOF diff --git a/html/includes/alerting-navbar.inc.php b/html/includes/alerting-navbar.inc.php index e08b4760..a7d57d92 100644 --- a/html/includes/alerting-navbar.inc.php +++ b/html/includes/alerting-navbar.inc.php @@ -6,11 +6,10 @@ * * @package observium * @subpackage web - * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2021 Observium Limited + * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2022 Observium Limited * */ - /// CONTACTS ACTIONS $readonly = $_SESSION['userlevel'] < 10; @@ -60,7 +59,7 @@ if (!$readonly) $exist_contacts = dbFetchColumn('SELECT `contact_id` FROM `alert_contacts_assoc` WHERE `aca_type` = ? AND `alert_checker_id` = ?', array('alert', $vars['alert_test_id'])); //print_vars($exist_contacts); $sql = "SELECT `contact_id` FROM `alert_contacts` WHERE `contact_disabled` = 0 AND `contact_method` != 'syscontact'" . - generate_query_values($exist_contacts, 'contact_id', '!='); // exclude exist contacts + generate_query_values_and($exist_contacts, 'contact_id', '!='); // exclude exist contacts //print_vars($sql); foreach (dbFetchColumn($sql) as $contact_id) { diff --git a/html/includes/authenticate-functions.inc.php b/html/includes/authenticate-functions.inc.php index ff945a3b..a305f6ed 100644 --- a/html/includes/authenticate-functions.inc.php +++ b/html/includes/authenticate-functions.inc.php @@ -262,4 +262,12 @@ function auth_user_info($username) } } +// Create placeholder user for users logged in via non-MySQL mechanisms to enable user list +function create_mysql_user($username, $userid, $level = '1', $type = 'mysql') +{ + if(isset($username) && isset($userid) && is_numeric($userid)) { + dbInsert(array('username' => $username, 'user_id' => $userid, 'level' => $level, 'type' => $type), 'users'); + } +} + // EOF diff --git a/html/includes/authenticate.inc.php b/html/includes/authenticate.inc.php index be101699..74c52777 100644 --- a/html/includes/authenticate.inc.php +++ b/html/includes/authenticate.inc.php @@ -5,8 +5,8 @@ * This file is part of Observium. * * @package observium - * @subpackage authentication - * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2021 Observium Limited + * @subpackage web + * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2022 Observium Limited * */ @@ -23,15 +23,19 @@ define('OBS_AJAX', (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SE $debug_auth = FALSE; // Do not use this debug unless you Observium Developer ;) -if (PHP_VERSION_ID < 70100) -{ +if (PHP_VERSION_ID < 70100) { // Use sha1 to generate the session ID (option removed in php 7.1) // session.sid_length (Number of session ID characters - 22 to 256. // session.sid_bits_per_character (Bits used per character - 4 to 6. @ini_set('session.hash_function', '1'); } + @ini_set('session.referer_check', ''); // This config was causing so much trouble with Chrome -@ini_set('session.name', 'OBSID'); // Session name +if (OBS_API) { + @ini_set('session.name', 'OBSAPI'); // Session name for API +} else { + @ini_set('session.name', 'OBSID'); // Session name for common Web UI +} @ini_set('session.use_cookies', '1'); // Use cookies to store the session id on the client side @ini_set('session.use_only_cookies', '1'); // This prevents attacks involved passing session ids in URLs @ini_set('session.use_trans_sid', '0'); // Disable SID (no session id in url) @@ -48,13 +52,13 @@ $cookie_httponly = FALSE; //$cookie_httponly = TRUE; // Use custom session lifetime -if (is_numeric($GLOBALS['config']['web_session_lifetime']) && $GLOBALS['config']['web_session_lifetime'] >= 0) { +if (is_intnum($GLOBALS['config']['web_session_lifetime']) && $GLOBALS['config']['web_session_lifetime'] >= 0) { $lifetime = (int)$GLOBALS['config']['web_session_lifetime']; } @ini_set('session.gc_maxlifetime', $lifetime); // Session lifetime (for non "remember me" sessions) -if (PHP_VERSION_ID >= 70300) -{ + +if (PHP_VERSION_ID >= 70300) { // Allows servers to assert that a cookie ought not to be sent along with cross-site requests. // Lax will sent the cookie for cross-domain GET requests, while Strict will not //@ini_set('session.cookie_samesite', 'Strict'); @@ -64,7 +68,7 @@ if (PHP_VERSION_ID >= 70300) 'domain' => $cookie_domain, 'secure' => $cookie_https, 'httponly' => $cookie_httponly, - 'samesite' => 'Strict' + 'samesite' => 'Lax' // 'Strict' /// FIXME. Set this configurable? See: https://jira.observium.org/browse/OBS-4214 ]; session_set_cookie_params($cookie_params); } else { @@ -77,28 +81,24 @@ if (!session_is_active()) { session_regenerate(); } -if ($debug_auth && empty($_SESSION['authenticated'])) -{ +if ($debug_auth && empty($_SESSION['authenticated'])) { logfile('debug_auth.log', __LINE__ . " NOT Authenticated!!!. IP=[" . get_remote_addr($config['web_session_ip_by_header']) . "]. URL=[" . $_SERVER['REQUEST_URI'] . "]"); logfile('debug_auth.log', __LINE__ . ' ' . json_encode($_SESSION)); } // Fallback to MySQL auth as default - FIXME do this in sqlconfig file? -if (!isset($config['auth_mechanism'])) -{ +if (!isset($config['auth_mechanism'])) { $config['auth_mechanism'] = "mysql"; } // Trust Apache authenticated user, if configured to do so and username is available -if ($config['auth']['remote_user'] && $_SERVER['REMOTE_USER'] != '') -{ +if ($config['auth']['remote_user'] && is_valid_param($_SERVER['REMOTE_USER'], 'username')) { session_set_var('username', $_SERVER['REMOTE_USER']); } $auth_file = $config['html_dir'].'/includes/authentication/' . $config['auth_mechanism'] . '.inc.php'; if (is_file($auth_file)) { - if (isset($_SESSION['auth_mechanism']) && $_SESSION['auth_mechanism'] != $config['auth_mechanism']) - { + if (isset($_SESSION['auth_mechanism']) && $_SESSION['auth_mechanism'] != $config['auth_mechanism']) { // Logout if AUTH mechanism changed session_logout(); reauth_with_message('Authentication mechanism changed, please log in again!'); @@ -123,14 +123,12 @@ if (is_file($auth_file)) { if ($_SESSION['authenticated'] && str_starts(ltrim($_SERVER['REQUEST_URI'], '/'), 'logout')) { // Do not use $vars and get_vars here! //print_vars($_SERVER['REQUEST_URI']); - if (auth_can_logout()) - { + if (auth_can_logout()) { // No need for a feedback message if user requested a logout session_logout(function_exists('auth_require_login')); $redirect = auth_logout_url(); - if ($redirect) - { + if ($redirect) { redirect_to_url($redirect); exit(); } @@ -144,8 +142,7 @@ $user_unique_id = session_unique_id(); // Get unique user id and check if IP cha // Store logged remote IP with real proxied IP (if configured and available) $remote_addr = get_remote_addr(); $remote_addr_header = get_remote_addr(TRUE); // Remote addr by http header -if ($remote_addr_header && $remote_addr != $remote_addr_header) -{ +if ($remote_addr_header && $remote_addr != $remote_addr_header) { $remote_addr = $remote_addr_header . ' (' . $remote_addr . ')'; } @@ -156,15 +153,16 @@ if (isset($config['web_session_cidr']) && count($config['web_session_cidr'])) { } if (!$_SESSION['authenticated']) { - if (isset($_GET['username']) && isset($_GET['password']) && - is_string($_GET['username']) && is_string($_GET['password'])) { + if (isset($_GET['username'], $_GET['password']) && + is_valid_param($_GET['username'], 'username') && is_valid_param($_GET['password'], 'password')) { + session_set_var('username', $_GET['username']); $auth_password = $_GET['password']; //r($_GET); //r($_SESSION); - } elseif (isset($_POST['username']) && isset($_POST['password']) && - is_string($_POST['username']) && is_string($_POST['password'])) - { + } elseif (isset($_POST['username'], $_POST['password']) && + is_valid_param($_POST['username'], 'username') && is_valid_param($_POST['password'], 'password')) { + session_set_var('username', $_POST['username']); $auth_password = $_POST['password']; } elseif (isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])) { @@ -256,8 +254,7 @@ if (isset($_SESSION['username'])) { 'user_agent' => $_SERVER['HTTP_USER_AGENT'], 'result' => 'Logged In'), 'authlog'); // Generate keys for cookie auth - if (isset($_POST['remember']) && OBS_ENCRYPT) - { + if (isset($_POST['remember']) && OBS_ENCRYPT) { $ckey = md5(strgen()); $dkey = md5(strgen()); $encpass = encrypt($auth_password, $dkey); @@ -307,28 +304,15 @@ if (isset($_SESSION['username'])) { session_commit(); // Hardcoded level permissions + /// FIXME. It's seems unused?.. - $user_perms = array(); + $user_perms = []; - $perms[0] = []; - $perms[1] = ['LOGIN']; - $perms[2] = []; - $perms[3] = []; - $perms[5] = ['GLOBAL_READ']; - $perms[6] = []; - $perms[7] = []; - $perms[8] = []; - $perms[9] = []; - $perms[10] = ['ADMIN']; - - foreach($perms as $level => $array) - { - if($_SESSION['userlevel'] >= $level) - { - foreach($array AS $entry) { $user_perms[$entry] = $entry; } + foreach ($config['user_level'] as $level => $array) { + if ($_SESSION['userlevel'] >= $level) { + foreach($array['roles'] as $entry) { $user_perms[$entry] = $entry; } } } - //print_vars($user_perms); //print_vars($_SESSION); diff --git a/html/includes/authentication/ldap.inc.php b/html/includes/authentication/ldap.inc.php index 42032d8f..57e5c80e 100644 --- a/html/includes/authentication/ldap.inc.php +++ b/html/includes/authentication/ldap.inc.php @@ -6,7 +6,7 @@ * * @package observium * @subpackage authentication - * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2021 Observium Limited + * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2022 Observium Limited * */ @@ -67,7 +67,7 @@ function ldap_search_user($ldap_group, $userdn, $depth = -1) { $ldap_search = ldap_search($ds, trim($config['auth_ldap_groupbase'], ', '), $filter, array($config['auth_ldap_attr']['dn'])); //r($filter); - if (is_resource($ldap_search)) { + if (ldap_internal_is_valid($ldap_search)) { $ldap_results = ldap_get_entries($ds, $ldap_search); //r($ldap_results); @@ -101,30 +101,30 @@ function ldap_search_user($ldap_group, $userdn, $depth = -1) { * Initializes the LDAP connection to the specified server(s). Cycles through all servers, throws error when no server can be reached. * Private function for this LDAP module only. */ -function ldap_init() -{ +function ldap_init() { global $ds, $config; - if (!is_resource($ds)) - { + if (!ldap_internal_is_valid($ds)) { print_debug('LDAP[Connecting to ' . implode(' ',$config['auth_ldap_server']) . ']'); - $ds = @ldap_connect(implode(' ',$config['auth_ldap_server']), $config['auth_ldap_port']); + if ($config['auth_ldap_port'] === 636) { + print_debug('LDAP[Port 636. Prepending ldaps:// to server URI]'); + $ds = @ldap_connect(implode(' ',preg_filter('/^(ldaps:\/\/)?/', 'ldaps://', $config['auth_ldap_server'])), $config['auth_ldap_port']); + } else { + $ds = @ldap_connect(implode(' ',$config['auth_ldap_server']), $config['auth_ldap_port']); + } print_debug("LDAP[Connected]"); if ($config['auth_ldap_starttls'] && - (in_array($config['auth_ldap_starttls'], [ 'optional', 'require', '1', 1, TRUE ], TRUE))) - { + (in_array($config['auth_ldap_starttls'], [ 'optional', 'require', '1', 1, TRUE ], TRUE))) { $tls = ldap_start_tls($ds); - if ($config['auth_ldap_starttls'] === 'require' && !$tls) - { + if ($config['auth_ldap_starttls'] === 'require' && !$tls) { session_logout(); print_error("Fatal error: LDAP TLS required but not successfully negotiated [" . ldap_error($ds) . "]"); exit; } } - if ($config['auth_ldap_referrals']) - { + if ($config['auth_ldap_referrals']) { ldap_set_option($ds, LDAP_OPT_REFERRALS, $config['auth_ldap_referrals']); print_debug("LDAP[Referrals][Set to " . $config['auth_ldap_referrals'] . "]"); } else { @@ -132,8 +132,7 @@ function ldap_init() print_debug("LDAP[Referrals][Disabled]"); } - if ($config['auth_ldap_version']) - { + if ($config['auth_ldap_version']) { ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, $config['auth_ldap_version']); print_debug("LDAP[Version][Set to " . $config['auth_ldap_version'] . "]"); } @@ -385,11 +384,12 @@ function ldap_auth_user_id($username) $filter_params[] = ldap_filter_create('objectClass', $config['auth_ldap_objectclass']); $filter_params[] = ldap_filter_create($config['auth_ldap_attr']['uid'], $username); $filter = ldap_filter_combine($filter_params); - + print_debug("LDAP[Filter][$filter][" . trim($config['auth_ldap_suffix'], ', ') . "]"); $search = ldap_search($ds, trim($config['auth_ldap_suffix'], ', '), $filter); - $entries = is_resource($search) ? ldap_get_entries($ds, $search) : []; - //print_vars($entries); + //r($search); + $entries = ldap_internal_is_valid($search) ? ldap_get_entries($ds, $search) : []; + //r($entries); if ($entries['count']) { @@ -500,7 +500,7 @@ function ldap_auth_user_list($username = NULL) { //$group_filter .= '(memberof='.$group.')'; $group_params[] = ldap_filter_create($config['auth_ldap_attr']['memberOf'], $group); } - + $filter_params[] = ldap_filter_combine($group_params, '|'); //$filter = '(&'.$filter.'(|'.$group_filter.'))'; @@ -566,7 +566,7 @@ function ldap_internal_user_entries($entries, &$userlist) { $compare = ldap_search_user($ldap_group, $userdn); //print_warning("$username, $realname, "); - //print_vars($compare); + //r($compare); if ($compare === -1) { print_debug("LDAP[UserList][Compare LDAP error: " . ldap_error($ds) . "]"); @@ -606,9 +606,9 @@ function ldap_internal_paged_entries($filter, $attributes) do { $search = ldap_search( $ds, trim($config['auth_ldap_suffix'], ', '), $filter, $attributes, 0, 0, 0, LDAP_DEREF_NEVER, - [['oid' => LDAP_CONTROL_PAGEDRESULTS, 'value' => ['size' => $page_size, 'cookie' => $cookie]]] + [['oid' => LDAP_CONTROL_PAGEDRESULTS, 'value' => [ 'size' => $page_size, 'cookie' => $cookie ]]] ); - if (is_resource($search)) { + if (ldap_internal_is_valid($search)) { ldap_parse_result($ds, $search, $errcode, $matcheddn, $errmsg, $referrals, $controls); print_debug(ldap_error($ds)); $entries = array_merge($entries, ldap_get_entries($ds, $search)); @@ -642,7 +642,7 @@ function ldap_internal_paged_entries($filter, $attributes) $search = ldap_search($ds, trim($config['auth_ldap_suffix'], ', '), $filter, $attributes); print_debug(ldap_error($ds)); - if (is_resource($search)) { + if (ldap_internal_is_valid($search)) { $entries = array_merge($entries, ldap_get_entries($ds, $search)); //print_vars($filter); //print_vars($search); @@ -665,7 +665,7 @@ function ldap_internal_paged_entries($filter, $attributes) $search = ldap_search($ds, trim($config['auth_ldap_suffix'], ', '), $filter, $attributes); print_debug(ldap_error($ds)); - if (is_resource($search)) { + if (ldap_internal_is_valid($search)) { $entries = ldap_get_entries($ds, $search); //print_vars($filter); //print_vars($search); @@ -800,6 +800,9 @@ function ldap_bind_dn($username = "", $password = "") */ function ldap_internal_dn_from_username($username) { + + //r(debug_backtrace()); + global $config, $ds, $cache; if (!isset($cache['ldap']['dn'][$username])) @@ -813,7 +816,11 @@ function ldap_internal_dn_from_username($username) print_debug("LDAP[Filter][$filter][" . trim($config['auth_ldap_suffix'], ', ') . "]"); $search = ldap_search($ds, trim($config['auth_ldap_suffix'], ', '), $filter); - if (is_resource($search)) { + + //r($search); + //r(ldap_get_entries($ds, $search)); + + if (ldap_internal_is_valid($search)) { $entries = ldap_get_entries($ds, $search); if ($entries['count']) { @@ -1110,6 +1117,16 @@ function ldap_unescape_filter_value($values = array()) return $values; } +function ldap_internal_is_valid($obj) { + if (PHP_VERSION_ID >= 80100) { + // ldap_bind() returns an LDAP\Connection instance in 8.1; previously, a resource was returned + // ldap_search() returns an LDAP\Result instance in 8.1; previously, a resource was returned. + return is_object($obj); + } + + return is_resource($obj); +} + /** * Converts all ASCII chars < 32 to "\HEX" * diff --git a/html/includes/authentication/mysql.inc.php b/html/includes/authentication/mysql.inc.php index 009599cf..1ed1f5b0 100644 --- a/html/includes/authentication/mysql.inc.php +++ b/html/includes/authentication/mysql.inc.php @@ -1,5 +1,4 @@ $hash), 'users', '`username` = ?', array($username)); // FIXME should return BOOL + return dbUpdate([ 'password' => $hash ], 'users', '`username` = ? AND `type` = ?', [ $username, 'mysql' ]); // FIXME should return BOOL } /** @@ -124,16 +122,22 @@ function mysql_auth_usermanagement() * @param string $description User's description * @return bool TRUE if user addition is successful, FALSE if it is not */ -function mysql_adduser($username, $password, $level, $email = "", $realname = "", $can_modify_passwd='1', $description = "") +function mysql_adduser($username, $password, $level, $email = "", $realname = "", $can_modify_passwd = '1', $description = "") { if (!mysql_auth_user_exists($username)) { // $hash = crypt($password, '$1$' . strgen(8).'$'); // This is old hash, do not used anymore (keep for history) $hash = password_hash($password, PASSWORD_DEFAULT); - return dbInsert(array('username' => $username, 'password' => $hash, 'level' => $level, 'email' => $email, 'realname' => $realname, 'can_modify_passwd' => $can_modify_passwd, 'descr' => $description), 'users'); - } else { - return FALSE; + return dbInsert([ 'username' => $username, + 'password' => $hash, + 'level' => $level, + 'email' => $email, + 'realname' => $realname, + 'can_modify_passwd' => $can_modify_passwd, + 'descr' => $description ], 'users'); } + + return FALSE; } /** @@ -145,7 +149,7 @@ function mysql_adduser($username, $password, $level, $email = "", $realname = "" function mysql_auth_user_exists($username) { //return @dbFetchCell("SELECT COUNT(*) FROM `users` WHERE `username` = ?", array($username)); // FIXME should return BOOL - return dbExist('users', '`username` = ?', array($username)); + return dbExist('users', '`username` = ? AND `type` = ?', [ $username, 'mysql' ]); } /** @@ -156,7 +160,7 @@ function mysql_auth_user_exists($username) */ function mysql_auth_username_by_id($user_id) { - return dbFetchCell("SELECT `username` FROM `users` WHERE `user_id` = ?", array($user_id)); // FIXME should return FALSE if not found + return dbFetchCell("SELECT `username` FROM `users` WHERE `user_id` = ? AND `type` = ?", [ $user_id, 'mysql' ]); // FIXME should return FALSE if not found } /** @@ -167,7 +171,7 @@ function mysql_auth_username_by_id($user_id) */ function mysql_auth_user_level($username) { - return dbFetchCell("SELECT `level` FROM `users` WHERE `username` = ?", array($username)); + return dbFetchCell("SELECT `level` FROM `users` WHERE `username` = ? AND `type` = ?", [ $username, 'mysql' ]); } /** @@ -178,7 +182,7 @@ function mysql_auth_user_level($username) */ function mysql_auth_user_id($username) { - return dbFetchCell("SELECT `user_id` FROM `users` WHERE `username` = ?", array($username)); + return dbFetchCell("SELECT `user_id` FROM `users` WHERE `username` = ? AND `type` = ?", [ $username, 'mysql' ]); } /** @@ -196,7 +200,7 @@ function mysql_deluser($username) dbDelete('users_prefs', "`user_id` = ?", array($user_id)); dbDelete('users_ckeys', "`username` = ?", array($username)); - return dbDelete('users', "`username` = ?", array($username)); // FIXME should return BOOL + return dbDelete('users', "`username` = ? AND `type` = ?", [ $username, 'mysql' ]); // FIXME should return BOOL } /** @@ -206,7 +210,7 @@ function mysql_deluser($username) */ function mysql_auth_user_list() { - return dbFetchRows("SELECT * FROM `users`"); // FIXME hardcode list of returned fields as in all other backends; array content should not depend on db changes/column names. + return dbFetchRows("SELECT * FROM `users` WHERE `type` = ?", [ 'mysql' ]); // FIXME hardcode list of returned fields as in all other backends; array content should not depend on db changes/column names. } /** @@ -217,7 +221,7 @@ function mysql_auth_user_list() */ function mysql_auth_user_info($username) { - return dbFetchRow("SELECT * FROM `users` WHERE `username` = ?", array($username)); + return dbFetchRow("SELECT * FROM `users` WHERE `username` = ? AND `type` = ?", [ $username, 'mysql' ]); } // EOF diff --git a/html/includes/authentication/radius.inc.php b/html/includes/authentication/radius.inc.php index b7d95aac..a087200c 100644 --- a/html/includes/authentication/radius.inc.php +++ b/html/includes/authentication/radius.inc.php @@ -1,5 +1,4 @@ $rad_userlevel, 'user_id' => $user_id ], 'users', '`username` = ? AND `type` = ?', [ $username, 'radius' ]); + } + } return $rad_userlevel; } @@ -324,8 +334,8 @@ function radius_deluser($username) */ function radius_auth_user_list() { - $userlist = array(); - return $userlist; + // Send list of users from MySQL + return dbFetchRows("SELECT * FROM `users` WHERE `type` = ?", [ 'radius' ]); } // EOF diff --git a/html/includes/cache-data.inc.php b/html/includes/cache-data.inc.php index 298925e7..732a711e 100644 --- a/html/includes/cache-data.inc.php +++ b/html/includes/cache-data.inc.php @@ -6,10 +6,11 @@ * * @package observium * @subpackage web - * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2020 Observium Limited + * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2022 Observium Limited * */ +$cache_data_start = microtime(TRUE); $cache_item = get_cache_item('data'); //print_vars($cache_item->isHit()); @@ -33,17 +34,22 @@ if (!ishit_cache_item($cache_item)) // This means device_by_id_cache actually never has to do any queries by itself, it'll always get the // cached version when running from the web interface. From the commandline obviously we'll need to fetch // the data per-device. We pre-fetch the graphs list as well, much faster than a query per device obviously. - $graphs_array = dbFetchRows("SELECT * FROM `device_graphs` FORCE INDEX (`graph`) ORDER BY `graph`;"); + //$graphs_array = dbFetchRows("SELECT * FROM `device_graphs` FORCE INDEX (`graph`) ORDER BY `graph`;"); - foreach ($graphs_array as $graph) + $cache['graphs'] = []; + foreach(dbFetchRows("SELECT `graph` FROM `device_graphs` GROUP BY `graph` ORDER BY `graph`;") as $entry) + { + $cache['graphs'][$entry['graph']] = $entry['graph']; + } + + /*foreach ($graphs_array as $graph) { // Cache this per device_id so we can assign it to the correct (cached) device in the for loop below if ($graph['enabled']) { $device_graphs[$graph['device_id']][$graph['graph']] = $graph; } - } - $cache['graphs'] = array(); // All permitted graphs + }*/ // Cache scheduled maintenance currently active $cache['maint'] = cache_alert_maintenance(); @@ -54,16 +60,18 @@ if (!ishit_cache_item($cache_item)) } else { $devices_array = dbFetchRows("SELECT * FROM `devices` ORDER BY `hostname`;"); } + foreach ($devices_array as $device) { if (device_permitted($device['device_id'])) { // Process device and add all the human-readable stuff. - humanize_device($device); + // Very slow on larger systems (3s with 2000 devices) + //humanize_device($device); // Assign device graphs from array created above - $device['graphs'] = (array)$device_graphs[$device['device_id']]; - $cache['graphs'] = array_unique(array_merge($cache['graphs'], array_keys($device['graphs']))); // Add to global array cache + //$device['graphs'] = (array)$device_graphs[$device['device_id']]; + //$cache['graphs'] = array_unique(array_merge($cache['graphs'], array_keys($device['graphs']))); // Add to global array cache $cache['devices']['permitted'][] = (int)$device['device_id']; // Collect IDs for permitted $cache['devices']['hostname'][$device['hostname']] = $device['device_id']; @@ -184,10 +192,10 @@ if (!ishit_cache_item($cache_item)) // Devices disabled if (isset($cache['devices']['disabled']) && count($cache['devices']['disabled']) > 0) { - $cache['ports']['device_disabled'] = dbFetchColumn("SELECT `port_id` FROM `ports` WHERE 1 " . $where_permitted . generate_query_values($cache['devices']['disabled'], 'device_id')); + $cache['ports']['device_disabled'] = dbFetchColumn("SELECT `port_id` FROM `ports` WHERE 1 " . $where_permitted . generate_query_values_and($cache['devices']['disabled'], 'device_id')); if (!$config['web_show_disabled']) { - $where_hide .= generate_query_values($cache['devices']['disabled'], 'device_id', '!='); + $where_hide .= generate_query_values_and($cache['devices']['disabled'], 'device_id', '!='); } } @@ -195,9 +203,9 @@ if (!ishit_cache_item($cache_item)) $where_devices_ignored = ''; if (isset($cache['devices']['ignored']) && count($cache['devices']['ignored']) > 0) { - $cache['ports']['device_ignored'] = dbFetchColumn("SELECT `port_id` FROM `ports` WHERE 1 " . $where_permitted . $where_hide . generate_query_values($cache['devices']['ignored'], 'device_id')); - $where_hide .= generate_query_values($cache['devices']['ignored'], 'device_id', '!='); - $where_devices_ignored = generate_query_values($cache['devices']['ignored'], 'device_id'); + $cache['ports']['device_ignored'] = dbFetchColumn("SELECT `port_id` FROM `ports` WHERE 1 " . $where_permitted . $where_hide . generate_query_values_and($cache['devices']['ignored'], 'device_id')); + $where_hide .= generate_query_values_and($cache['devices']['ignored'], 'device_id', '!='); + $where_devices_ignored = generate_query_values_and($cache['devices']['ignored'], 'device_id'); } $cache['ports']['stat']['device_ignored'] = count($cache['ports']['device_ignored']); @@ -650,6 +658,8 @@ unset($cache_item); //print_vars(get_cache_items('__wui')); //print_vars(get_cache_stats()); +$cache_data_time = microtime(TRUE) - $cache_data_start; + // EOF diff --git a/html/includes/contacts-navbar.inc.php b/html/includes/contacts-navbar.inc.php index be8c9dd7..c5ddc382 100644 --- a/html/includes/contacts-navbar.inc.php +++ b/html/includes/contacts-navbar.inc.php @@ -341,10 +341,10 @@ $("#contact_method").change(function() { } else { $script .= PHP_EOL . " } else if (select === '" . $transport . "') {" . PHP_EOL; } - $script .= " \$('div[id^=\"contact_${transport}_\"]').show();" . PHP_EOL . " "; + $script .= " \$('div[id^=\"contact_{$transport}_\"]').show();" . PHP_EOL . " "; foreach (array_keys($config['transports']) as $ltransport) { if ($transport != $ltransport) { - $script .= " \$('div[id^=\"contact_${ltransport}_\"]').hide();"; + $script .= " \$('div[id^=\"contact_{$ltransport}_\"]').hide();"; } } diff --git a/html/includes/entities/cbqos.inc.php b/html/includes/entities/cbqos.inc.php index 90d595d6..c4b091da 100644 --- a/html/includes/entities/cbqos.inc.php +++ b/html/includes/entities/cbqos.inc.php @@ -6,7 +6,7 @@ * * @package observium * @subpackage web - * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2021 Observium Limited + * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2022 Observium Limited * */ @@ -28,21 +28,21 @@ function build_cbqos_query($vars) switch ($var) { case "policy_name": case "object_name": - $sql .= generate_query_values($value, $var); + $sql .= generate_query_values_and($value, $var); break; case "group": case "group_id": $values = get_group_entities($value); - $sql .= generate_query_values($values, 'cbqos_id'); + $sql .= generate_query_values_and($values, 'cbqos_id'); break; case 'device_group_id': case 'device_group': $values = get_group_entities($value, 'device'); - $sql .= generate_query_values($values, 'ports_cbqos.device_id'); + $sql .= generate_query_values_and($values, 'ports_cbqos.device_id'); break; case "device": case "device_id": - $sql .= generate_query_values($value, 'ports_cbqos.device_id'); + $sql .= generate_query_values_and($value, 'ports_cbqos.device_id'); break; } } diff --git a/html/includes/entities/counter.inc.php b/html/includes/entities/counter.inc.php index ff13d402..880308c7 100644 --- a/html/includes/entities/counter.inc.php +++ b/html/includes/entities/counter.inc.php @@ -6,14 +6,14 @@ * * @package observium * @subpackage web - * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2021 Observium Limited + * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2022 Observium Limited * */ /** * Humanize counter. * - * Returns a the $counter array with processed information: + * Returns a $counter array with processed information: * counter_state (TRUE: state counter, FALSE: normal counter) * human_value, counter_symbol, state_name, state_event, state_class * @@ -164,42 +164,42 @@ function build_counter_query($vars, $query_count = FALSE) case "group": case "group_id": $values = get_group_entities($value); - $sql .= generate_query_values($values, 'counters.counter_id'); + $sql .= generate_query_values_and($values, 'counters.counter_id'); break; case 'device_group_id': case 'device_group': $values = get_group_entities($value, 'device'); - $sql .= generate_query_values($values, 'counters.device_id'); + $sql .= generate_query_values_and($values, 'counters.device_id'); break; case "device": case "device_id": - $sql .= generate_query_values($value, 'counters.device_id'); + $sql .= generate_query_values_and($value, 'counters.device_id'); break; case "id": case "counter_id": - $sql .= generate_query_values($value, 'counters.counter_id'); + $sql .= generate_query_values_and($value, 'counters.counter_id'); break; case "entity_id": - $sql .= generate_query_values($value, 'counters.measured_entity'); + $sql .= generate_query_values_and($value, 'counters.measured_entity'); break; case "entity_type": - $sql .= generate_query_values($value, 'counters.measured_class'); + $sql .= generate_query_values_and($value, 'counters.measured_class'); break; case 'entity_state': case "measured_state": - $sql .= build_entity_measured_where('counter', ['measured_state' => $value]); + $sql .= build_entity_measured_where('counter', [ 'measured_state' => $value ]); break; case 'class': case "counter_class": - $sql .= generate_query_values($value, 'counter_class'); + $sql .= generate_query_values_and($value, 'counter_class'); break; case "descr": case "counter_descr": - $sql .= generate_query_values($value, 'counters.counter_descr', '%LIKE%'); + $sql .= generate_query_values_and($value, 'counters.counter_descr', '%LIKE%'); break; case "event": case "counter_event": - $sql .= generate_query_values($value, 'counter_event'); + $sql .= generate_query_values_and($value, 'counter_event'); break; } } @@ -443,7 +443,7 @@ function generate_counter_row($counter, $vars) $counter['counter_class'], $config['counter_types'][$counter['counter_class']]['alt_units']) as $unit => $unit_value) { - if (is_numeric($unit_value)) { $counter_tooltip .= "
${unit_value}${unit}"; } + if (is_numeric($unit_value)) { $counter_tooltip .= "
{$unit_value}{$unit}"; } } } diff --git a/html/includes/entities/device.inc.php b/html/includes/entities/device.inc.php index 6d7d1b7b..08493603 100644 --- a/html/includes/entities/device.inc.php +++ b/html/includes/entities/device.inc.php @@ -6,7 +6,7 @@ * * @package observium * @subpackage web - * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2021 Observium Limited + * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2022 Observium Limited * */ @@ -14,29 +14,25 @@ * Build devices where array * * This function returns an array of "WHERE" statements from a $vars array. - * The returned array can be implode()d and used on the devices table. + * The returned array can be imploded and used on the devices table. * Originally extracted from the /devices/ page * * @param array $vars * @return array */ -function build_devices_where_array($vars) -{ +function build_devices_where_array($vars) { $where_array = array(); - foreach ($vars as $var => $value) - { - if ($value != '') - { - switch ($var) - { + foreach ($vars as $var => $value) { + if (!safe_empty($value)) { + switch ($var) { case 'group': case 'group_id': $values = get_group_entities($value); - $where_array[$var] = generate_query_values($values, 'device_id'); + $where_array[$var] = generate_query_values_and($values, 'device_id'); break; case 'device': case 'device_id': - $where_array[$var] = generate_query_values($value, 'device_id'); + $where_array[$var] = generate_query_values_and($value, 'device_id'); break; case 'hostname': case 'sysname': @@ -44,13 +40,15 @@ function build_devices_where_array($vars) case 'sysDescr': case 'serial': case 'purpose': - $where_array[$var] = generate_query_values($value, $var, '%LIKE%'); + $condition = str_contains_array($value, [ '*', '?' ]) ? 'LIKE' : '%LIKE%'; + $where_array[$var] = generate_query_values_and($value, $var, $condition); break; case 'location_text': - $where_array[$var] = generate_query_values($value, 'devices.location', '%LIKE%'); + $condition = str_contains_array($value, [ '*', '?' ]) ? 'LIKE' : '%LIKE%'; + $where_array[$var] = generate_query_values_and($value, 'devices.location', $condition); break; case 'location': - $where_array[$var] = generate_query_values($value, 'devices.location'); + $where_array[$var] = generate_query_values_and($value, 'devices.location'); break; case 'location_lat': case 'location_lon': @@ -60,7 +58,7 @@ function build_devices_where_array($vars) case 'location_city': if ($GLOBALS['config']['geocoding']['enable']) { - $where_array[$var] = generate_query_values($value, 'devices_locations.' . $var); + $where_array[$var] = generate_query_values_and($value, 'devices_locations.' . $var); } break; case 'os': @@ -74,10 +72,10 @@ function build_devices_where_array($vars) case 'distro': case 'ignore': case 'disabled': - $where_array[$var] = generate_query_values($value, $var); + $where_array[$var] = generate_query_values_and($value, $var); break; case 'graph': - $where_array[$var] = generate_query_values(devices_with_graph($value), "devices.device_id"); + $where_array[$var] = generate_query_values_and(devices_with_graph($value), "devices.device_id"); } } } @@ -103,7 +101,6 @@ function devices_with_graph($graph) function build_devices_sort($vars) { $order = ''; - $desc_order = isset($vars['sort_desc']) && $vars['sort_desc']; switch ($vars['sort']) { case 'uptime': @@ -112,13 +109,12 @@ function build_devices_sort($vars) case 'features': case 'type': case 'os': + case 'sysName': case 'device_id': $order = ' ORDER BY `devices`.`'.$vars['sort'].'`'; - if ($desc_order) - { - $order .= " DESC"; - } + if ($vars['sort_order'] == "desc") { $order .= " DESC";} break; + case 'domain': // Special order hostnames in Domain Order // SELECT `hostname`, @@ -126,19 +122,18 @@ function build_devices_sort($vars) // SUBSTRING_INDEX(SUBSTRING_INDEX(`hostname`,'.',-2),'.',1) AS `middle`, // SUBSTRING_INDEX(`hostname`,'.',-1) AS `rightmost` // FROM `devices` ORDER by `middle`, `rightmost`, `leftmost`; - if ($desc_order) + if ($vars['sort_order'] == "desc") { $order = ' ORDER BY `middle` DESC, `rightmost` DESC, `leftmost` DESC'; } else { $order = ' ORDER BY `middle`, `rightmost`, `leftmost`'; } break; + + case 'hostname': default: $order = ' ORDER BY `devices`.`hostname`'; - if ($desc_order) - { - $order .= " DESC"; - } + if ($vars['sort_order'] == "desc") { $order .= " DESC"; } break; } return $order; @@ -150,27 +145,9 @@ function print_device_header($device, $args = array()) { if (!is_array($device)) { print_error("Invalid device passed to print_device_header()!"); } - /* FIXME. Unused? - if ($device['status'] == '0') { $class = "div-alert"; } else { $class = "div-normal"; } - if ($device['ignore'] == '1') - { - $class = "div-ignore-alert"; - if ($device['status'] == '1') - { - $class = "div-ignore"; - } - } - - if ($device['disabled'] == '1') - { - $class = "div-disabled"; - } - - $type = strtolower($device['os']); - */ $div_class = 'box box-solid'; if (!safe_empty($args['div-class'])) { - $div_class .= " ${args['div-class']}"; + $div_class .= " " . $args['div-class']; } echo '
@@ -200,12 +177,12 @@ function print_device_header($device, $args = array()) { } $graph_array = []; - $graph_array['height'] = "100"; - $graph_array['width'] = "310"; - $graph_array['to'] = $config['time']['now']; + //$graph_array['height'] = "100"; + //$graph_array['width'] = "310"; + $graph_array['to'] = get_time(); $graph_array['device'] = $device['device_id']; $graph_array['type'] = "device_bits"; - $graph_array['from'] = $config['time']['day']; + $graph_array['from'] = get_time('day'); $graph_array['legend'] = "no"; $graph_array['height'] = "45"; @@ -398,7 +375,7 @@ function print_device_row($device, $vars = array('view' => 'basic'), $link_vars // Preprocess device graphs array $graphs_enabled = []; - foreach ($GLOBALS['cache']['devices']['id'][$device['device_id']]['graphs'] as $graph) + foreach ($device['graphs'] as $graph) { $graphs_enabled[] = $graph['graph']; } @@ -515,50 +492,41 @@ function get_device_icon($device, $base_icon = FALSE, $dark = FALSE) { } // Icon by vendor name - if ($icon === 'generic' && ($config['os'][$device['os']]['vendor'] || $device['vendor'])) - { - if ($device['vendor']) - { + if ($icon === 'generic' && ($config['os'][$device['os']]['vendor'] || $device['vendor'])) { + if ($device['vendor']) { $vendor = $device['vendor']; } else { $vendor = rewrite_vendor($config['os'][$device['os']]['vendor']); // Compatibility, if device not polled for long time } $vendor_safe = safename(strtolower($vendor)); - if (isset($config['vendors'][$vendor_safe]['icon'])) - { + if (isset($config['vendors'][$vendor_safe]['icon'])) { $icon = $config['vendors'][$vendor_safe]['icon']; - } - elseif (is_file($config['html_dir'] . '/images/os/' . $vendor_safe . '.png')) - { + } elseif (is_file($config['html_dir'] . '/images/os/' . $vendor_safe . '.png')) { $icon = $vendor_safe; - } - elseif (isset($config['os'][$device['os']]['icons'])) - { + } elseif (isset($config['os'][$device['os']]['icons'])) { // Fallback to os alternative icon $icon = array_values($config['os'][$device['os']]['icons'])[0]; } } // Set dark mode by session - if (isset($_SESSION['theme'])) - { + if (isset($_SESSION['theme'])) { $dark = str_contains($_SESSION['theme'], 'dark'); } // Prefer dark variant of icon in dark mode - if ($dark && is_file($config['html_dir'] . '/images/os/' . $icon . '-dark.png')) - { + if ($dark && is_file($config['html_dir'] . '/images/os/' . $icon . '-dark.png')) { $icon .= '-dark'; } - if ($base_icon) - { + if ($base_icon) { // return base name for os icon return $icon; } // return image html tag + $base_url = rtrim($config['base_url'], '/'); $srcset = ''; // Now we always have 2x icon variant! //if (is_file($config['html_dir'] . '/images/os/' . $icon . '_2x.png')) // HiDPI image exist? @@ -566,14 +534,13 @@ function get_device_icon($device, $base_icon = FALSE, $dark = FALSE) { // Detect allowed screen ratio for current browser $ua_info = detect_browser(); - if ($ua_info['screen_ratio'] > 1) - { - $srcset = ' srcset="' .$config['base_url'] . '/images/os/' . $icon . '_2x.png'.' 2x"'; + if ($ua_info['screen_ratio'] > 1) { + $srcset = ' srcset="' . $base_url . '/images/os/' . $icon . '_2x.png'.' 2x"'; } //} // Image tag -- FIXME re-engineer this code to do this properly. This is messy. - return ''; + return ''; } // TESTME needs unit testing @@ -638,8 +605,11 @@ function generate_device_popup($device, $vars = []) { } } + $count = 0; foreach ($graphs as $entry) { + if($count == 3) { break; } + if ($entry && in_array(str_replace('device_', '', $entry), $graphs_enabled, TRUE)) { // No text provided for the minigraph, fetch from array if (preg_match(OBS_PATTERN_GRAPH_TYPE, $entry, $graphtype)) { @@ -664,17 +634,13 @@ function generate_device_popup($device, $vars = []) { $content .= '
'; $content .= "

" . $text . "

"; - /* - $content .= generate_box_open(array('title' => $text, - 'body-style' => 'white-space: nowrap;')); - */ $content .= generate_graph_tag($graph_array); - $graph_array['from'] = get_time('week'); $content .= generate_graph_tag($graph_array); - $content .= '
'; - //$content .= generate_box_close(); + + $count++; + } } @@ -722,41 +688,6 @@ function generate_device_link_short($device, $vars = [], $short = TRUE) { return generate_device_link($device, NULL, $vars, TRUE, $short); } -function device_name($device, $max_len = FALSE) { - global $config; - - switch (strtolower($config['web_device_name'])) { - case 'sysname': - $name_field = 'sysName'; - break; - case 'purpose': - case 'descr': - case 'description': - $name_field = 'purpose'; - break; - default: - $name_field = 'hostname'; - } - - if ($max_len && !is_intnum($max_len)) { - $max_len = $config['short_hostname']['length']; - } - - if ($name_field !== 'hostname' && !safe_empty($device[$name_field])) { - if ($name_field === 'sysName' && $max_len && $max_len > 3) { - // short sysname when is valid hostname (do not escape here) - return short_hostname($device[$name_field], $max_len, FALSE); - } - return $device[$name_field]; - } - - if ($max_len && $max_len > 3) { - // short hostname (do not escape here) - return short_hostname($device['hostname'], $max_len, FALSE); - } - return $device['hostname']; -} - function generate_device_form_values($form_filter = FALSE, $column = 'device_id', $options = array()) { global $cache; diff --git a/html/includes/entities/generic.inc.php b/html/includes/entities/generic.inc.php index 78dd5d11..4a3aa353 100644 --- a/html/includes/entities/generic.inc.php +++ b/html/includes/entities/generic.inc.php @@ -54,6 +54,30 @@ function get_customoid_by_id($oid_id) { } // end function get_customoid_by_id() +// DOCME needs phpdoc block +// TESTME needs unit testing +function get_application_by_id($application_id) +{ + if (is_numeric($application_id)) + { + $application = dbFetchRow("SELECT * FROM `applications` WHERE `app_id` = ?", array($application_id)); + } + if (is_array($application)) + { + return $application; + } else { + return FALSE; + } +} + +// DOCME needs phpdoc block +// TESTME needs unit testing +function accesspoint_by_id($ap_id, $refresh = '0') +{ + $ap = dbFetchRow("SELECT * FROM `accesspoints` WHERE `accesspoint_id` = ?", array($ap_id)); + + return $ap; +} function generate_entity_popup_graphs($entity, $vars) { @@ -376,8 +400,8 @@ function build_entity_measured_where($entity_type, $vars) { case 'port': case 'printersupply': - $measure_sql = generate_query_values($measured_type, $column_measured_type, NULL, OBS_DB_NO_LEADING_AND); - $measure_sql .= generate_query_values($entities, $column_measured_id); + $measure_sql = generate_query_values_ng($measured_type, $column_measured_type); + $measure_sql .= generate_query_values_and($entities, $column_measured_id); break; } if ($measure_sql) { $measure_array[] = $measure_sql; } @@ -388,7 +412,7 @@ function build_entity_measured_where($entity_type, $vars) //$value = (array)$value; // Select all without measured entities if (in_array('none', $value)) { - $measure_array[] = generate_query_values(1, $column_measured_id, 'NULL', OBS_DB_NO_LEADING_AND); + $measure_array[] = generate_query_values_ng(1, $column_measured_id); $value = array_diff($value, [ 'none' ]); } if (count($value)) @@ -410,8 +434,8 @@ function build_entity_measured_where($entity_type, $vars) $entities = dbFetchColumn($entity_sql); //$entities = dbFetchColumn($entity_sql, NULL, TRUE); //r($entities); - $measure_sql = generate_query_values($measured_type, $column_measured_type, NULL, OBS_DB_NO_LEADING_AND); - $measure_sql .= generate_query_values($entities, $column_measured_id); + $measure_sql = generate_query_values_ng($measured_type, $column_measured_type); + $measure_sql .= generate_query_values_and($entities, $column_measured_id); break; case 'printersupply': break; diff --git a/html/includes/entities/mempool.inc.php b/html/includes/entities/mempool.inc.php index 993fee43..0dd274f6 100644 --- a/html/includes/entities/mempool.inc.php +++ b/html/includes/entities/mempool.inc.php @@ -6,7 +6,7 @@ * * @package observium * @subpackage web - * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2021 Observium Limited + * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2022 Observium Limited * */ @@ -32,20 +32,20 @@ function build_mempool_query($vars) case "group": case "group_id": $values = get_group_entities($value); - $sql .= generate_query_values($values, 'mempools.mempool_id'); + $sql .= generate_query_values_and($values, 'mempools.mempool_id'); break; case 'device_group_id': case 'device_group': $values = get_group_entities($value, 'device'); - $sql .= generate_query_values($values, 'mempools.device_id'); + $sql .= generate_query_values_and($values, 'mempools.device_id'); break; case "device": case "device_id": - $sql .= generate_query_values($value, 'mempools.device_id'); + $sql .= generate_query_values_and($value, 'mempools.device_id'); break; case "descr": case "mempool_descr"; - $sql .= generate_query_values($value, 'mempool_descr', '%LIKE%'); + $sql .= generate_query_values_and($value, 'mempool_descr', '%LIKE%'); break; } } diff --git a/html/includes/entities/oid_entry.inc.php b/html/includes/entities/oid_entry.inc.php index 41228af8..194170e8 100644 --- a/html/includes/entities/oid_entry.inc.php +++ b/html/includes/entities/oid_entry.inc.php @@ -6,15 +6,14 @@ * * @package observium * @subpackage web - * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2021 Observium Limited + * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2022 Observium Limited * */ function generate_oid_template_link($entry) { $url = generate_url(array('page' => 'customoid', 'oid_id' => $entry['oid_id'])); - $link = ''.$entry['oid_descr'].''; - return $link; + return ''.$entry['oid_descr'].''; } function build_oid_query($vars) @@ -33,21 +32,21 @@ function build_oid_query($vars) case "oid_descr": case "oid": case "oid_name": - $sql .= generate_query_values($value, $var); + $sql .= generate_query_values_and($value, $var); break; case "group": case "group_id": $values = get_group_entities($value); - $sql .= generate_query_values($values, 'oid_entry_id'); + $sql .= generate_query_values_and($values, 'oid_entry_id'); break; case 'device_group_id': case 'device_group': $values = get_group_entities($value, 'device'); - $sql .= generate_query_values($values, 'oids_entries.device_id'); + $sql .= generate_query_values_and($values, 'oids_entries.device_id'); break; case "device": case "device_id": - $sql .= generate_query_values($value, 'oids_entries.device_id'); + $sql .= generate_query_values_and($value, 'oids_entries.device_id'); break; } } @@ -97,7 +96,7 @@ function print_oid_table_header($vars, $entries) $cols['event'] = array('Event', 'style="width: 60px;"'); if ($entries[0]['oid_autodiscover'] == '0' && $vars['page'] === "customoid") { - $cols['actions'] = array('', 'style="width: 40px;"'); echo "derp"; + $cols['actions'] = array('', 'style="width: 40px;"'); } echo get_table_header($cols, $vars); @@ -112,7 +111,6 @@ function print_oid_table($vars) $entries = dbFetchRows($sql); $count = count($entries); - if (count($entries)) { echo generate_box_open(); diff --git a/html/includes/entities/p2pradio.inc.php b/html/includes/entities/p2pradio.inc.php index b6dcdb8c..dc1b9da5 100644 --- a/html/includes/entities/p2pradio.inc.php +++ b/html/includes/entities/p2pradio.inc.php @@ -6,7 +6,7 @@ * * @package observium * @subpackage web - * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2021 Observium Limited + * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2022 Observium Limited * */ @@ -23,16 +23,16 @@ function generate_p2pradio_query($vars) case "group": case "group_id": $values = get_group_entities($value); - $sql .= generate_query_values($values, 'radio_id'); + $sql .= generate_query_values_and($values, 'radio_id'); break; case 'device_group_id': case 'device_group': $values = get_group_entities($value, 'device'); - $sql .= generate_query_values($values, 'p2p_radios.device_id'); + $sql .= generate_query_values_and($values, 'p2p_radios.device_id'); break; case "device": case "device_id": - $sql .= generate_query_values($value, 'device_id'); + $sql .= generate_query_values_and($value, 'device_id'); break; } } diff --git a/html/includes/entities/port.inc.php b/html/includes/entities/port.inc.php index bbbb2818..f91eb7d6 100644 --- a/html/includes/entities/port.inc.php +++ b/html/includes/entities/port.inc.php @@ -6,7 +6,7 @@ * * @package observium * @subpackage web - * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2021 Observium Limited + * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2022 Observium Limited * */ @@ -14,7 +14,7 @@ * Build ports WHERE array * * This function returns an array of "WHERE" statements from a $vars array. - * The returned array can be implode()d and used on the ports table. + * The returned array can be imploded and used on the ports table. * Originally extracted from the /ports/ page * * @param array $vars @@ -27,20 +27,20 @@ function build_ports_where_array($vars) { if (!safe_empty($value)) { switch ($var) { case 'location': - $where[] = generate_query_values($value, $var); + $where[] = generate_query_values_and($value, $var); break; case 'device_id': - $where[] = generate_query_values($value, 'ports.device_id'); + $where[] = generate_query_values_and($value, 'ports.device_id'); break; case 'group': case 'group_id': $values = get_group_entities($value); - $where[] = generate_query_values($values, 'ports.port_id'); + $where[] = generate_query_values_and($values, 'ports.port_id'); break; case 'device_group_id': case 'device_group': $values = get_group_entities($value, 'device'); - $where[] = generate_query_values($values, 'ports.device_id'); + $where[] = generate_query_values_and($values, 'ports.device_id'); break; case 'disable': $var = 'disabled'; @@ -49,25 +49,26 @@ function build_ports_where_array($vars) { case 'ignore': case 'ifSpeed': case 'ifType': + case 'ifVlan': case 'port_id': - $where[] = generate_query_values($value, 'ports.'.$var); + $where[] = generate_query_values_and($value, 'ports.'.$var); break; case 'hostname': case 'ifAlias': case 'ifDescr': // FIXME, probably better always use port_label instead ifDescr for search - $where[] = generate_query_values($value, $var, '%LIKE%'); + $where[] = generate_query_values_and($value, $var, '%LIKE%'); break; case 'label': case 'port_label': - $where[] = generate_query_values($value, 'port_label', '%LIKE%'); + $where[] = generate_query_values_and($value, 'port_label', '%LIKE%'); break; case 'mac': case 'ifPhysAddress': $value = str_replace([ '.', '-', ':' ], '', $value); - $where[] = generate_query_values($value, 'ifPhysAddress', '%LIKE%'); + $where[] = generate_query_values_and($value, 'ifPhysAddress', '%LIKE%'); break; case 'port_descr_type': - $where[] = generate_query_values($value, $var, 'LIKE'); + $where[] = generate_query_values_and($value, $var, 'LIKE'); break; case 'errors': if (get_var_true($value)) { @@ -88,13 +89,13 @@ function build_ports_where_array($vars) { foreach ((array)$value as $state) { if ($state === "down") { $state_where[] = '`ifAdminStatus` = "up" AND `ifOperStatus` IN ("lowerLayerDown", "down")'; - //$state_where[] = generate_query_values('up', 'ifAdminStatus', NULL, FALSE) . generate_query_values(['down', 'lowerLayerDown'], 'ifOperStatus'); + //$state_where[] = generate_query_values_ng('up', 'ifAdminStatus') . generate_query_values_and(['down', 'lowerLayerDown'], 'ifOperStatus'); } elseif ($state === "up") { $state_where[] = '`ifAdminStatus` = "up" AND `ifOperStatus` IN ("up", "testing", "monitoring")'; - //$state_where[] = generate_query_values('up', 'ifAdminStatus', NULL, FALSE) . generate_query_values(['up', 'testing', 'monitoring'], 'ifOperStatus'); + //$state_where[] = generate_query_values_ng('up', 'ifAdminStatus') . generate_query_values_and(['up', 'testing', 'monitoring'], 'ifOperStatus'); } elseif ($state === "admindown" || $state === "shutdown") { $state_where[] = '`ifAdminStatus` = "down"'; - //$state_where[] = generate_query_values('down', 'ifAdminStatus', NULL, FALSE); + //$state_where[] = generate_query_values_ng('down', 'ifAdminStatus'); } } switch (count($state_where)) { @@ -110,12 +111,12 @@ function build_ports_where_array($vars) { break; case 'cbqos': if ($value && $value !== 'no') { - $where[] = generate_query_values($GLOBALS['cache']['ports']['cbqos'], 'ports.port_id'); + $where[] = generate_query_values_and($GLOBALS['cache']['ports']['cbqos'], 'ports.port_id'); } break; case 'mac_accounting': if ($value && $value !== 'no') { - $where[] = generate_query_values($GLOBALS['cache']['ports']['mac_accounting'], 'ports.port_id'); + $where[] = generate_query_values_and($GLOBALS['cache']['ports']['mac_accounting'], 'ports.port_id'); } break; } @@ -194,24 +195,26 @@ function generate_port_popup($port, $text = NULL, $type = NULL) $content = generate_device_popup_header($port); $content .= generate_port_popup_header($port); - $content .= '
'; - //$content .= generate_box_open(array('body-style' => 'width: 700px;')); - $graph_array['type'] = $port['graph_type']; - $graph_array['legend'] = "yes"; - $graph_array['height'] = "100"; - $graph_array['width'] = "275"; - $graph_array['to'] = $time['now']; - $graph_array['from'] = $time['day']; - $graph_array['id'] = $port['port_id']; - $content .= generate_graph_tag($graph_array); - $graph_array['from'] = $time['week']; - $content .= generate_graph_tag($graph_array); - $graph_array['from'] = $time['month']; - $content .= generate_graph_tag($graph_array); - $graph_array['from'] = $time['year']; - $content .= generate_graph_tag($graph_array); - $content .= "
"; - //$content .= generate_box_close(); + if($type != "none") { + $content .= '
'; + //$content .= generate_box_open(array('body-style' => 'width: 700px;')); + $graph_array['type'] = $port['graph_type']; + $graph_array['legend'] = "yes"; + $graph_array['height'] = "100"; + $graph_array['width'] = "275"; + $graph_array['to'] = $time['now']; + $graph_array['from'] = $time['day']; + $graph_array['id'] = $port['port_id']; + $content .= generate_graph_tag($graph_array); + $graph_array['from'] = $time['week']; + $content .= generate_graph_tag($graph_array); + $graph_array['from'] = $time['month']; + $content .= generate_graph_tag($graph_array); + $graph_array['from'] = $time['year']; + $content .= generate_graph_tag($graph_array); + $content .= "
"; + //$content .= generate_box_close(); + } return $content; } @@ -454,7 +457,7 @@ function generate_port_row($port, $vars = array()) if (!isset($cache['ports_option']['ipv4_addresses']) || in_array($port['port_id'], $cache['ports_option']['ipv4_addresses'])) { $sql = "SELECT * FROM `ipv4_addresses` WHERE `port_id` = ?"; // Do not exclude IPv4 link-local - $sql .= generate_query_values(array_diff($ignore_type, [ 'link-local' ]), 'ipv4_type', '!='); // Do not show ignored ip types + $sql .= generate_query_values_and(array_diff($ignore_type, [ 'link-local' ]), 'ipv4_type', '!='); // Do not show ignored ip types foreach (dbFetchRows($sql, array($port['port_id'])) as $ip) { $string .= $break . generate_popup_link('ip', $ip['ipv4_address'].'/'.$ip['ipv4_prefixlen'], NULL, 'small'); @@ -464,7 +467,7 @@ function generate_port_row($port, $vars = array()) if (!isset($cache['ports_option']['ipv6_addresses']) || in_array($port['port_id'], $cache['ports_option']['ipv6_addresses'])) { $sql = "SELECT * FROM `ipv6_addresses` WHERE `port_id` = ?"; - $sql .= generate_query_values($ignore_type, 'ipv6_type', '!='); // Do not show ignored ip types + $sql .= generate_query_values_and($ignore_type, 'ipv6_type', '!='); // Do not show ignored ip types foreach (dbFetchRows($sql, array($port['port_id'])) as $ip6) { $string .= $break . generate_popup_link('ip', $ip6['ipv6_address'].'/'.$ip6['ipv6_prefixlen'], NULL, 'small'); diff --git a/html/includes/entities/printersupply.inc.php b/html/includes/entities/printersupply.inc.php index 34ed790e..da767f67 100644 --- a/html/includes/entities/printersupply.inc.php +++ b/html/includes/entities/printersupply.inc.php @@ -6,7 +6,7 @@ * * @package observium * @subpackage web - * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2021 Observium Limited + * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2022 Observium Limited * */ @@ -23,28 +23,28 @@ function build_printersupplies_query($vars) case "group": case "group_id": $values = get_group_entities($value); - $sql .= generate_query_values($values, 'printersupplies.supply_id'); + $sql .= generate_query_values_and($values, 'printersupplies.supply_id'); break; case 'device_group_id': case 'device_group': $values = get_group_entities($value, 'device'); - $sql .= generate_query_values($values, 'printersupplies.device_id'); + $sql .= generate_query_values_and($values, 'printersupplies.device_id'); break; case "device": case "device_id": - $sql .= generate_query_values($value, 'printersupplies.device_id'); + $sql .= generate_query_values_and($value, 'printersupplies.device_id'); break; case "supply": case "supply_type"; - $sql .= generate_query_values($value, 'printersupplies.supply_type'); + $sql .= generate_query_values_and($value, 'printersupplies.supply_type'); break; case "colour": case "supply_colour"; - $sql .= generate_query_values($value, 'supply_colour'); + $sql .= generate_query_values_and($value, 'supply_colour'); break; case "descr": case "supply_descr"; - $sql .= generate_query_values($value, 'supply_descr', '%LIKE%'); + $sql .= generate_query_values_and($value, 'supply_descr', '%LIKE%'); break; } } diff --git a/html/includes/entities/processor.inc.php b/html/includes/entities/processor.inc.php index 284ec001..843e920e 100644 --- a/html/includes/entities/processor.inc.php +++ b/html/includes/entities/processor.inc.php @@ -6,7 +6,7 @@ * * @package observium * @subpackage web - * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2021 Observium Limited + * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2022 Observium Limited * */ @@ -27,20 +27,20 @@ function generate_processor_query($vars) case "group": case "group_id": $values = get_group_entities($value); - $sql .= generate_query_values($values, 'processor_id'); + $sql .= generate_query_values_and($values, 'processor_id'); break; case 'device_group_id': case 'device_group': $values = get_group_entities($value, 'device'); - $sql .= generate_query_values($values, 'processors.device_id'); + $sql .= generate_query_values_and($values, 'processors.device_id'); break; case "device": case "device_id": - $sql .= generate_query_values($value, 'processors.device_id'); + $sql .= generate_query_values_and($value, 'processors.device_id'); break; case "descr": case "processor_descr"; - $sql .= generate_query_values($value, 'processor_descr', '%LIKE%'); + $sql .= generate_query_values_and($value, 'processor_descr', '%LIKE%'); break; } } diff --git a/html/includes/entities/pseudowire.inc.php b/html/includes/entities/pseudowire.inc.php index a62575ac..ea80ae50 100644 --- a/html/includes/entities/pseudowire.inc.php +++ b/html/includes/entities/pseudowire.inc.php @@ -1,13 +1,12 @@ $value]); + $sql .= build_entity_measured_where('sensor', [ 'measured_state' => $value ]); break; case "metric": // old metric param not allow array @@ -153,19 +153,19 @@ function build_sensor_query($vars, $query_count = FALSE) { } case 'class': case "sensor_class": - $sql .= generate_query_values($value, 'sensor_class'); + $sql .= generate_query_values_and($value, 'sensor_class'); break; case "descr": case "sensor_descr": - $sql .= generate_query_values($value, 'sensors.sensor_descr', '%LIKE%'); + $sql .= generate_query_values_and($value, 'sensors.sensor_descr', '%LIKE%'); break; case "type": case "sensor_type": - $sql .= generate_query_values($value, 'sensor_type', '%LIKE%'); + $sql .= generate_query_values_and($value, 'sensor_type', '%LIKE%'); break; case "event": case "sensor_event": - $sql .= generate_query_values($value, 'sensor_event'); + $sql .= generate_query_values_and($value, 'sensor_event'); break; } } @@ -304,6 +304,126 @@ function print_sensor_table_header($vars) { echo('' . PHP_EOL); } +function generate_sensor_line($sensor, $vars) { + global $config; + + humanize_sensor($sensor); + + $graph_array = []; + $graph_array['to'] = get_time(); + $graph_array['id'] = $sensor['sensor_id']; + $graph_array['type'] = "sensor_graph"; + $graph_array['width'] = 80; + $graph_array['height'] = 20; + $graph_array['bg'] = 'ffffff00'; + $graph_array['from'] = get_time('day'); + $graph_array['style'] = 'margin-top: 5px'; + + if ($sensor['sensor_event'] && is_numeric($sensor['sensor_value'])) { + $mini_graph = generate_graph_tag($graph_array); + } else { + // Do not show "Draw Error" minigraph + $mini_graph = ''; + } + + /* + $sensor_tooltip = $sensor['event_descr']; + // Append value in alternative units to tooltip + if (isset($config['sensor_types'][$sensor['sensor_class']]['alt_units'])) { + foreach (value_to_units($sensor['sensor_value'], + $config['sensor_types'][$sensor['sensor_class']]['symbol'], + $sensor['sensor_class'], + $config['sensor_types'][$sensor['sensor_class']]['alt_units']) as $unit => $unit_value) { + if (is_numeric($unit_value)) { $sensor_tooltip .= "
{$unit_value}{$unit}"; } + } + } + */ + + //r($sensor); + $text = '' . $sensor['human_value'] . $sensor['sensor_symbol'] . ''; + + //$line = ''; + $line = ''; + //$btn_class = str_replace('label', 'btn', $sensor['event_class']); // FIXME Need button-outline-* class from bs4+ + if (get_var_true($vars['compact'])) { + $line .= ''; + $line .= ''; + + //r($line); + return $line; +} + +function get_compact_sensors_line($measured_class, $entry, $vars) { + + // order dom sensors always by temperature, voltage, current, dbm, power + $order = []; + if (safe_count($entry) > 0) { + $classes = array_keys($entry); + //r($types); + if ($measured_class === 'port') { + // always display all classes for dom (also if not exist) + $order = [ 'temperature', 'voltage', 'current', /* 'dbm', 'power' */ ]; + // or dbm or power + if (in_array('dbm', $classes, TRUE)) { + $order[] = 'dbm'; + } elseif (in_array('power', $classes, TRUE)) { + $order[] = 'power'; + } else { + $order[] = 'dbm'; + } + } else { + $order = array_intersect([ 'temperature', 'voltage', 'current', 'dbm', 'power' ], $classes); + } + $order = array_merge($order, array_diff($classes, $order)); + //r($order); + } + $line = ''; + foreach ($order as $class) { + if (!isset($entry[$class])) { + // Add empty columns for port entities (for correct align) + $line .= ''; + } + + foreach ($entry[$class] as $sensor) { + /* + $sensor['sensor_descr'] = trim(str_ireplace($rename_from, '', $sensor['sensor_descr']), ":- \t\n\r\0\x0B"); + if (empty($sensor['sensor_descr'])) { + // Some time sensor descriptions equals to entity name + $sensor['sensor_descr'] = nicecase($sensor['sensor_class']); + } + */ + + // Compact view per entity/lane + $line .= generate_sensor_line($sensor, $vars); + } + } + + return $line; +} + function print_sensor_row($sensor, $vars) { echo generate_sensor_row($sensor, $vars); @@ -404,7 +524,7 @@ function generate_sensor_row($sensor, $vars) $sensor['sensor_class'], $config['sensor_types'][$sensor['sensor_class']]['alt_units']) as $unit => $unit_value) { - if (is_numeric($unit_value)) { $sensor_tooltip .= "
${unit_value}${unit}"; } + if (is_numeric($unit_value)) { $sensor_tooltip .= "
{$unit_value}{$unit}"; } } } diff --git a/html/includes/entities/sla.inc.php b/html/includes/entities/sla.inc.php index 0a6cd1b5..408dc585 100644 --- a/html/includes/entities/sla.inc.php +++ b/html/includes/entities/sla.inc.php @@ -6,7 +6,7 @@ * * @package observium * @subpackage web - * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2020 Observium Limited + * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2022 Observium Limited * */ @@ -23,38 +23,38 @@ function generate_sla_query($vars) case "group": case "group_id": $values = get_group_entities($value); - $sql .= generate_query_values($values, 'slas.sla_id'); + $sql .= generate_query_values_and($values, 'slas.sla_id'); break; case 'device_group_id': case 'device_group': $values = get_group_entities($value, 'device'); - $sql .= generate_query_values($values, 'storage.device_id'); + $sql .= generate_query_values_and($values, 'storage.device_id'); break; case "device": case "device_id": - $sql .= generate_query_values($value, 'slas.device_id'); + $sql .= generate_query_values_and($value, 'slas.device_id'); break; case "id": case "sla_id": - $sql .= generate_query_values($value, 'slas.sla_id'); + $sql .= generate_query_values_and($value, 'slas.sla_id'); break; case "owner": - $sql .= generate_query_values($value, 'slas.sla_owner'); + $sql .= generate_query_values_and($value, 'slas.sla_owner'); break; case "target": case "sla_target": - $sql .= generate_query_values($value, 'slas.sla_target', '%LIKE%'); + $sql .= generate_query_values_and($value, 'slas.sla_target', '%LIKE%'); break; case "sla_tag": - $sql .= generate_query_values($value, 'slas.sla_tag'); + $sql .= generate_query_values_and($value, 'slas.sla_tag'); break; case "rtt_type": case "rtt_sense": - $sql .= generate_query_values($value, 'slas.'.$var); + $sql .= generate_query_values_and($value, 'slas.'.$var); break; case "event": case "rtt_event": - $sql .= generate_query_values($value, 'slas.rtt_event'); + $sql .= generate_query_values_and($value, 'slas.rtt_event'); break; } } diff --git a/html/includes/entities/status.inc.php b/html/includes/entities/status.inc.php index fff4f060..b373688d 100644 --- a/html/includes/entities/status.inc.php +++ b/html/includes/entities/status.inc.php @@ -6,7 +6,7 @@ * * @package observium * @subpackage web - * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2021 Observium Limited + * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2022 Observium Limited * */ @@ -74,26 +74,26 @@ function generate_status_query($vars, $query_count = FALSE) { case "group": case "group_id": $values = get_group_entities($value, 'status'); - $sql .= generate_query_values($values, 'status.status_id'); + $sql .= generate_query_values_and($values, 'status.status_id'); break; case 'device_group_id': case 'device_group': $values = get_group_entities($value, 'device'); - $sql .= generate_query_values($values, 'status.device_id'); + $sql .= generate_query_values_and($values, 'status.device_id'); break; case "device": case "device_id": - $sql .= generate_query_values($value, 'status.device_id'); + $sql .= generate_query_values_and($value, 'status.device_id'); break; case "id": case 'status_id': - $sql .= generate_query_values($value, 'status.status_id'); + $sql .= generate_query_values_and($value, 'status.status_id'); break; case "entity_id": - $sql .= generate_query_values($value, 'measured_entity'); + $sql .= generate_query_values_and($value, 'measured_entity'); break; case "entity_type": - $sql .= generate_query_values($value, 'measured_class'); + $sql .= generate_query_values_and($value, 'measured_class'); break; case 'entity_state': case "measured_state": @@ -101,23 +101,23 @@ function generate_status_query($vars, $query_count = FALSE) { break; case "class": case 'entPhysicalClass': - $sql .= generate_query_values($value, 'entPhysicalClass'); + $sql .= generate_query_values_and($value, 'entPhysicalClass'); break; case "event": case "status_event": - $sql .= generate_query_values($value, 'status_event'); + $sql .= generate_query_values_and($value, 'status_event'); break; case "status": case "status_name": - $sql .= generate_query_values($value, 'status_name'); + $sql .= generate_query_values_and($value, 'status_name'); break; case "descr": case "status_descr": - $sql .= generate_query_values($value, 'status_descr', '%LIKE%'); + $sql .= generate_query_values_and($value, 'status_descr', '%LIKE%'); break; case 'type': case "status_type": - $sql .= generate_query_values($value, 'status_type', '%LIKE%'); + $sql .= generate_query_values_and($value, 'status_type', '%LIKE%'); break; } } @@ -323,7 +323,7 @@ function generate_status_row($status, $vars) { $row .= '' . generate_entity_link('status', $status, $mini_graph, NULL, FALSE) . ''; if ($vars['tab'] !== "overview") { - $row .= '' . generate_tooltip_link('', format_uptime((get_time() - $status['status_last_change']), 'short-2') . ' ago', format_unixtime($status['status_last_change'])) . ' + $row .= '' . generate_tooltip_time($status['status_last_change'], 'ago') . ' ' . generate_tooltip_link('', $status['status_event'], $status['event_descr'], $status['event_class']) . ''; $table_cols++; $table_cols++; diff --git a/html/includes/entities/storage.inc.php b/html/includes/entities/storage.inc.php index 8c5aa73c..5621acbc 100644 --- a/html/includes/entities/storage.inc.php +++ b/html/includes/entities/storage.inc.php @@ -6,7 +6,7 @@ * * @package observium * @subpackage web - * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2021 Observium Limited + * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2022 Observium Limited * */ @@ -30,23 +30,23 @@ function generate_storage_query($vars) case "group": case "group_id": $values = get_group_entities($value); - $sql .= generate_query_values($values, 'storage.storage_id'); + $sql .= generate_query_values_and($values, 'storage.storage_id'); break; case 'device_group_id': case 'device_group': $values = get_group_entities($value, 'device'); - $sql .= generate_query_values($values, 'storage.device_id'); + $sql .= generate_query_values_and($values, 'storage.device_id'); break; case "device": case "device_id": - $sql .= generate_query_values($value, 'storage.device_id'); + $sql .= generate_query_values_and($value, 'storage.device_id'); break; case "descr": case "storage_descr"; - $sql .= generate_query_values($value, 'storage_descr', '%LIKE%'); + $sql .= generate_query_values_and($value, 'storage_descr', '%LIKE%'); break; case 'ignored': - $sql .= generate_query_values($value, 'storage.storage_ignore'); + $sql .= generate_query_values_and($value, 'storage.storage_ignore'); break; } } @@ -92,8 +92,7 @@ function generate_storage_query($vars) } -function print_storage_table($vars) -{ +function print_storage_table($vars) { global $cache, $config; @@ -101,13 +100,16 @@ function print_storage_table($vars) $sql = generate_storage_query($vars); - $storages = array(); + $storages = []; foreach (dbFetchRows($sql) as $storage) { if (isset($cache['devices']['id'][$storage['device_id']])) { $storage['hostname'] = $cache['devices']['id'][$storage['device_id']]['hostname']; $storage['html_row_class'] = $cache['devices']['id'][$storage['device_id']]['html_row_class']; + + // FIXME. Should be part of humanize_storage() + $storage['human_type'] = array_preg_replace($config['rewrites']['storage_type_regexp'], $storage['storage_type']); $storages[] = $storage; } } @@ -152,19 +154,19 @@ function print_storage_table_header($vars) } echo('' . PHP_EOL); - $cols = array( - array(NULL, 'class="state-marker"'), - 'device' => array('Device', 'style="width: 250px;"'), - 'mountpoint' => array('Mountpoint'), - 'size' => array('Size', 'style="width: 100px;"'), - 'used' => array('Used', 'style="width: 100px;"'), - 'free' => array('Free', 'style="width: 100px;"'), - array('', 'style="width: 100px;"'), - 'usage' => array('Usage %', 'style="width: 200px;"'), - ); + $cols = [ + [ NULL, 'class="state-marker"' ], + 'device' => [ 'Device', 'style="width: 250px;"' ], + 'mountpoint' => [ 'Mountpoint' ], + 'fstype' => [ 'FS Type', 'style="width: 90px;"' ], + 'size' => [ 'Size', 'style="width: 100px;"' ], + 'used' => [ 'Used', 'style="width: 100px;"' ], + 'free' => [ 'Free', 'style="width: 100px;"' ], + [ '', 'style="width: 100px;"' ], + 'usage' => [ 'Usage %', 'style="width: 200px;"' ], + ]; - if ($vars['page'] === "device") - { + if ($vars['page'] === "device") { unset($cols['device']); } @@ -182,10 +184,10 @@ function generate_storage_row($storage, $vars) { global $config; - $table_cols = 8; + $table_cols = 9; if ($vars['page'] !== "device" && $vars['popup'] != TRUE) { $table_cols++; } // Add a column for device. - if(isset($vars['graph_type']) && $vars['graph_type'] == "perc") + if(isset($vars['graph_type']) && $vars['graph_type'] === "perc") $graph_array = array(); $graph_array['to'] = $config['time']['now']; @@ -225,6 +227,7 @@ function generate_storage_row($storage, $vars) { if ($vars['page'] !== "device" && $vars['popup'] != TRUE) { $row .= ''; } $row .= ' + diff --git a/html/includes/entities/virtualmachine.inc.php b/html/includes/entities/virtualmachine.inc.php index fb1aff92..a7c4f567 100644 --- a/html/includes/entities/virtualmachine.inc.php +++ b/html/includes/entities/virtualmachine.inc.php @@ -1,13 +1,12 @@ < / s c r i p t > // javascript:alert("Hello world");/ // + //
' . PHP_EOL . '
' . PHP_EOL . @@ -744,32 +735,23 @@ function pagination(&$vars, $total, $return_vars = FALSE) '
' . PHP_EOL . '
"; //$values = array('' => array('name')) - foreach ($pagesizes as $pagesize) - { - $value = generate_url($vars, array('pagesize' => $pagesize, 'pageno' => floor($start / $pagesize))); - $name = ($pagesize == $GLOBALS['config']['web_pagesize'] ? "[ $pagesize ]" : $pagesize); - $values[$value] = array('name' => $name, 'class' => 'text-center'); + foreach ($pagesizes as $pagesize) { + $value = generate_url($vars, [ 'pagesize' => $pagesize, 'pageno' => floor(fdiv($start, $pagesize)) ]); + $name = $pagesize == $GLOBALS['config']['web_pagesize'] ? "[ $pagesize ]" : $pagesize; + $values[$value] = [ 'name' => $name, 'class' => 'text-center' ]; } - $element = array('type' => 'select', - 'class' => 'pagination', - 'id' => 'pagesize', - 'name' => '# '.$per_page, - 'width' => '90px', - 'onchange' => "window.open(this.options[this.selectedIndex].value,'_top')", - 'value' => $per_page, - 'data-style' => 'box', - 'values' => $values); + $element = [ + 'type' => 'select', + 'class' => 'pagination', + 'id' => 'pagesize', + 'name' => '# '.$per_page, + 'width' => '90px', + 'onchange' => "window.open(this.options[this.selectedIndex].value,'_top')", + 'value' => $per_page, + 'data-style' => 'box', + 'values' => $values + ]; $pagination.= '
@@ -1130,6 +1104,28 @@ function generate_popup_link($type, $text = NULL, $vars = array(), $class = NULL return ''.$text.''; } +function generate_tooltip_time($timestamp, $text = '') { + if (is_numeric($timestamp) && $timestamp > OBS_MIN_UNIXTIME) { + // Unixtime + $timediff = get_time() - $timestamp; + $timetext = format_uptime($timediff, "short-3"); + if (!safe_empty($text)) { + $timetext .= " $text"; + } + + return generate_tooltip_link('', $timetext, format_unixtime($timestamp), NULL); + } + + // Timestamp + $timediff = get_time() - strtotime($timestamp); + $timetext = format_uptime($timediff, "short-3"); + if (!safe_empty($text)) { + $timetext .= " $text"; + } + + return generate_tooltip_link('', $timetext, format_timestamp($timestamp), NULL); +} + /** * Generate mouseover links with static tooltip from URL, link text, contents and a class. * @@ -1146,21 +1142,19 @@ function generate_popup_link($type, $text = NULL, $vars = array(), $class = NULL * @return string */ // TESTME needs unit testing -function generate_tooltip_link($url, $text, $contents = '', $class = NULL, $attribs = [], $escape = FALSE) -{ +function generate_tooltip_link($url, $text, $contents = '', $class = NULL, $attribs = [], $escape = FALSE) { global $config, $link_iter; $link_iter++; - $href = (strlen($url) ? 'href="' . $url . '"' : ''); + $href = !safe_empty($url) ? 'href="' . $url . '"' : ''; if ($escape) { $text = escape_html($text); } $attribs['class'] = array_merge((array)$class, (array)$attribs['class']); // Allow the Grinch to disable popups and destroy Christmas. - $allow_mobile = (in_array(detect_browser_type(), array('mobile', 'tablet')) ? $config['web_mouseover_mobile'] : TRUE); - if ($config['web_mouseover'] && strlen($contents) && $allow_mobile) - { + $allow_mobile = !in_array(detect_browser_type(), [ 'mobile', 'tablet' ]) || $config['web_mouseover_mobile']; + if ($config['web_mouseover'] && $allow_mobile && !safe_empty($contents)) { $attribs['style'] = 'cursor: pointer;'; $attribs['data-rel'] = 'tooltip'; $attribs['data-tooltip'] = $contents; @@ -1387,26 +1381,29 @@ function print_graph_popup($graph_array) // TESTME needs unit testing // DOCME needs phpdoc block -function permissions_cache($user_id) -{ - $permissions = array(); +function permissions_cache($user_id) { + + $cache_key = 'permissions_'.$GLOBALS['config']['auth_mechanism'].$user_id; + $cache_item = get_cache_item($cache_key); + if (ishit_cache_item($cache_item)) { + return get_cache_data($cache_item); + } + + $permissions = []; // Get permissions from user-specific and role tables. $permission_where = '`user_id` = ? AND `auth_mechanism` = ?'; $permission_params = [ $user_id, $GLOBALS['config']['auth_mechanism'] ]; $entity_permissions = dbFetchRows("SELECT * FROM `entity_permissions` WHERE " . $permission_where, $permission_params); $roles_entity_permissions = dbFetchRows("SELECT * FROM `roles_entity_permissions` LEFT JOIN `roles_users` USING (`role_id`) WHERE " . $permission_where, $permission_params); - foreach (array_merge((array)$entity_permissions, (array)$roles_entity_permissions) as $entity) - { + foreach (array_merge((array)$entity_permissions, (array)$roles_entity_permissions) as $entity) { // Set access to ro if it's not in the defined list. $access = (in_array($entity['access'], array('ro', 'rw')) ? $entity['access'] : 'ro'); - switch ($entity['entity_type']) - { + switch ($entity['entity_type']) { case "group": // this is a group, so expand its members into an array $group = get_group_by_id($entity['entity_id']); - foreach (get_group_entities($entity['entity_id']) as $group_entity_id) - { + foreach (get_group_entities($entity['entity_id']) as $group_entity_id) { $permissions[$group['entity_type']][$group_entity_id] = $access; } //break; // And also store self group permission in cache @@ -1425,20 +1422,22 @@ function permissions_cache($user_id) // Alerts // FIXME - this seems like it would be slow on very large installs - $alert = array(); - foreach (dbFetchRows('SELECT `alert_table_id`, `device_id`, `entity_id`, `entity_type` FROM `alert_table`') as $alert_table_entry) - { + $alert = []; + foreach (dbFetchRows('SELECT `alert_table_id`, `device_id`, `entity_id`, `entity_type` FROM `alert_table`') as $alert_table_entry) { //r($alert_table_entry); - if (is_entity_permitted($alert_table_entry['entity_id'], $alert_table_entry['entity_type'], $alert_table_entry['device_id'], $permissions)) - { + if (is_entity_permitted($alert_table_entry['entity_id'], $alert_table_entry['entity_type'], $alert_table_entry['device_id'], $permissions)) { $alert[$alert_table_entry['alert_table_id']] = TRUE; } } - if (count($alert)) - { + if (count($alert)) { $permissions['alert'] = $alert; } + set_cache_item($cache_item, $permissions); + + // Clear expired cache + del_cache_expired(); + return $permissions; } @@ -1882,11 +1881,11 @@ function get_locations($filter = array()) { case 'location_city': // Check geo params only when GEO enabled globally if ($GLOBALS['config']['geocoding']['enable']) { - $where_array[$var] = generate_query_values($value, $var); + $where_array[$var] = generate_query_values_and($value, $var); } break; case 'location': - $where_array[$var] = generate_query_values($value, $var); + $where_array[$var] = generate_query_values_and($value, $var); break; } } @@ -2209,13 +2208,16 @@ function generate_query_permitted($type_array = [ 'device' ], $options = []) { if (!isset($options['port_null']) || !$options['port_null']) { //$query_permitted[] = "($column != '' AND $column IS NOT NULL)"; $query_permitted[] = "$column IS NOT NULL"; - } elseif (!$user_limited) { + } elseif (!$user_limited && safe_count($query_permitted)) { // FIXME. derp code, need rewrite - $query_permitted[] = safe_count($query_permitted) ? "OR $column IS NULL" : "$column IS NULL"; + //$query_permitted[] = safe_count($query_permitted) ? "OR $column IS NULL" : "$column IS NULL"; + $query_permitted[] = "OR $column IS NULL"; } $query_permitted = implode(" AND ", (array)$query_permitted); - $query_part[] = str_replace(" AND OR ", ' OR ', $query_permitted); + if (!safe_empty($query_permitted)) { + $query_part[] = str_replace(" AND OR ", ' OR ', $query_permitted); + } unset($query_permitted); break; @@ -2384,7 +2386,7 @@ function load_user_config(&$load_config, $user_id) { if (!isset($config_variable[$item['pref']]['useredit']) || !$config_variable[$item['pref']]['useredit']) { // Load only permitted settings - print_debug("User [$user_id] setting '${item['pref']}' not permitted by definitions."); + print_debug("User [$user_id] setting '{$item['pref']}' not permitted by definitions."); continue; } @@ -2628,11 +2630,10 @@ function get_smokeping_files($rdebug = 0) if ($rdebug) { echo('- Recursing through ' . $config['smokeping']['dir'] . '
'); } - if (isset($config['smokeping']['master_hostname'])) - { + if (isset($config['smokeping']['master_hostname'])) { $master_hostname = $config['smokeping']['master_hostname']; } else { - $master_hostname = $config['own_hostname']; + $master_hostname = $config['own_hostname'] ?: get_localhost(); } if (is_dir($config['smokeping']['dir'])) diff --git a/html/includes/graphs/bgp/auth.inc.php b/html/includes/graphs/bgp/auth.inc.php index 4e51557a..9f803839 100644 --- a/html/includes/graphs/bgp/auth.inc.php +++ b/html/includes/graphs/bgp/auth.inc.php @@ -1,5 +1,4 @@ $rrd_filename, 'descr' => $port['hostname'] ."-". $port['ifDescr'], 'descr_in' => device_name($port, TRUE), - 'descr_out' => short_ifname($port['ifDescr'], NULL, FALSE) ]; // Options sets for skip htmlentities + 'descr_out' => short_ifname($port['port_label'], NULL, FALSE) ]; // Options sets for skip htmlentities } } diff --git a/html/includes/graphs/device/bits.inc.php b/html/includes/graphs/device/bits.inc.php index 4ea8cd34..123c0911 100644 --- a/html/includes/graphs/device/bits.inc.php +++ b/html/includes/graphs/device/bits.inc.php @@ -15,7 +15,7 @@ $ds_in = "INOCTETS"; $ds_out = "OUTOCTETS"; -$graph_return = array('descr' => 'Device total traffic in bits/sec.'); +$graph_return['descr'] = 'Device total traffic in bits/sec.'; // init $i = 0; diff --git a/html/includes/graphs/device/processor.inc.php b/html/includes/graphs/device/processor.inc.php index 225a0cac..0660ca58 100644 --- a/html/includes/graphs/device/processor.inc.php +++ b/html/includes/graphs/device/processor.inc.php @@ -1,5 +1,4 @@ +// EOF diff --git a/html/includes/graphs/device/storage.inc.php b/html/includes/graphs/device/storage.inc.php index 243fd0c9..986b5d6a 100644 --- a/html/includes/graphs/device/storage.inc.php +++ b/html/includes/graphs/device/storage.inc.php @@ -15,18 +15,22 @@ $scale_max = "100"; include_once($config['html_dir']."/includes/graphs/common.inc.php"); -if($width > 500) +if($width > 350) { - $descr_len = 22; + $padding = 45; + $text_width = 7; } else { - $descr_len = 12; + $padding = 39; + $text_width = 6; } -$descr_len += round(($width - 250) / 8); +$legend_len = ($vars['width'] + $padding) / $text_width; +$descr_len = $legend_len - 31; $iter = 0; $colours = 'mixed-10c'; -$rrd_options .= " COMMENT:'".str_pad('Size Used %used', $descr_len+31, ' ', STR_PAD_LEFT)."\\l'"; -//$rrd_options .= " COMMENT:' Size Used %age\\l'"; +$rrd_options .= " COMMENT:'".str_pad('Size Used % Used', $legend_len, ' ', STR_PAD_LEFT)."\\l'"; +$graph_return['legend_lines'] = 1; + foreach (dbFetchRows("SELECT * FROM storage where device_id = ?", array($device['device_id'])) as $storage) { @@ -47,7 +51,14 @@ foreach (dbFetchRows("SELECT * FROM storage where device_id = ?", array($device[ $rrd_options .= " GPRINT:".$storage['storage_id']."used:LAST:%6.2lf%sB"; $rrd_options .= " GPRINT:".$storage['storage_id']."perc:LAST:%5.2lf%%\\l"; $iter++; + $graph_return['legend_lines']++; + $graph_return['rrds'][] = $rrd; + } else { + $graph_return['missing_rrds'][] = $rrd; } } + + + // EOF diff --git a/html/includes/graphs/generic_data.inc.php b/html/includes/graphs/generic_data.inc.php index 1a9d7669..9bf03737 100644 --- a/html/includes/graphs/generic_data.inc.php +++ b/html/includes/graphs/generic_data.inc.php @@ -1,5 +1,4 @@ $ds) $cmd_def .= " CDEF:".$ds_name."_max=".$ds_name; } + //$graph_return['rrds'][$ds['file']][] = $ds_name; + if (!empty($ds['cdef'])) { $ds_name = $ds_name."_c"; @@ -217,6 +225,7 @@ foreach ($graph_def['ds'] as $ds_name => $ds) } } $cmd_graph .= " COMMENT:'\\l'"; + $graph_return['legend_lines']++; if ($ds['line']) { if (is_numeric($ds['line'])) { diff --git a/html/includes/graphs/generic_duplex.inc.php b/html/includes/graphs/generic_duplex.inc.php index 66dbaca8..26db2629 100644 --- a/html/includes/graphs/generic_duplex.inc.php +++ b/html/includes/graphs/generic_duplex.inc.php @@ -184,7 +184,7 @@ $rrd_options .= " HRULE:0#999999"; // $midnight = strtotime('today midnight'); // for ($i = 1; $i <= 2; $i++) // { -// $rrd_options .= " VRULE:${midnight}#FF0000"; +// $rrd_options .= " VRULE:{$midnight}#FF0000"; // $midnight -= 86400; // } //} diff --git a/html/includes/graphs/generic_multi_bits.inc.php b/html/includes/graphs/generic_multi_bits.inc.php index ea65aaba..5d186bff 100644 --- a/html/includes/graphs/generic_multi_bits.inc.php +++ b/html/includes/graphs/generic_multi_bits.inc.php @@ -117,7 +117,7 @@ if ($i) if ($vars['previous'] == "yes") { - $rrd_options .= " AREA:inbitsX#9999966:"; + $rrd_options .= " AREA:inbitsX#99999966:"; $rrd_options .= " AREA:doutbitsX#99999966:"; } diff --git a/html/includes/graphs/generic_multi_separated.inc.php b/html/includes/graphs/generic_multi_separated.inc.php index 5780339f..672d0324 100644 --- a/html/includes/graphs/generic_multi_separated.inc.php +++ b/html/includes/graphs/generic_multi_separated.inc.php @@ -16,6 +16,8 @@ $graph_return['valid_options'][] = "previous"; $graph_return['valid_options'][] = "total"; $graph_return['valid_options'][] = "trend"; +$graph_return['legend_lines'] = 0; + // Here we scale the number of numerical columns shown to make sure we keep the text. if ($width > 600) { @@ -74,6 +76,9 @@ if ($legend != 'no') if (in_array("max", $data_show)) { $rrd_options .= " COMMENT:' Max'"; } if (in_array("tot", $data_show)) { $rrd_options .= " COMMENT:' Total'"; } $rrd_options .= " COMMENT:'\\l'"; + + $graph_return['legend_lines']++; + } $i = 0; @@ -150,18 +155,20 @@ foreach ($rrd_list as $rrd) if (in_array("min", $data_show)) { $rrd_options .= " GPRINT:inbits".$i.":MIN:%6.2lf%s"; } if (in_array("max", $data_show)) { $rrd_options .= " GPRINT:inbits".$i.":MAX:%6.2lf%s"; } if (in_array("tot", $data_show)) { $rrd_options .= " GPRINT:totinB".$i.":%6.2lf%s".$total_units; } - $rrd_options .= " COMMENT:'\\l'"; + $graph_return['legend_lines']++; + + $rrd_optionsb .= " AREA:outbits".$i."_neg#" . $colour_out . ":".$stack; $rrd_options .= " HRULE:999999999999999#" . $colour_out . ":'" . rrdtool_escape($rrd['descr_out'], $descr_len - 3) . " Tx'"; - if (in_array("lst", $data_show)) { $rrd_options .= " GPRINT:outbits".$i.":LAST:%6.2lf%s"; } if (in_array("avg", $data_show)) { $rrd_options .= " GPRINT:outbits".$i.":AVERAGE:%6.2lf%s"; } if (in_array("min", $data_show)) { $rrd_options .= " GPRINT:outbits".$i.":MIN:%6.2lf%s"; } if (in_array("max", $data_show)) { $rrd_options .= " GPRINT:outbits".$i.":MAX:%6.2lf%s"; } if (in_array("tot", $data_show)) { $rrd_options .= " GPRINT:totoutB".$i.":%6.2lf%s".$total_units; } - $rrd_options .= " COMMENT:'\\l'"; + $graph_return['legend_lines']++; + $i++; $iter++; } @@ -208,6 +215,7 @@ if (in_array("tot", $data_show)) $rrd_options .= " VDEF:tot=octets,TOTAL"; $rrd_options .= " COMMENT:' \\l'"; + $graph_return['legend_lines']++; $rrd_options .= " HRULE:999999999999999#FFFFFF:'" . rrdtool_escape("Total", $descr_len - 3) . " Rx'"; if (in_array("lst", $data_show)) { $rrd_options .= " GPRINT:inbits:LAST:%6.2lf%s"; } @@ -216,6 +224,7 @@ if (in_array("tot", $data_show)) if (in_array("max", $data_show)) { $rrd_options .= " GPRINT:inbits:MAX:%6.2lf%s"; } if (in_array("tot", $data_show)) { $rrd_options .= " GPRINT:totin:%6.2lf%s".$total_units; } $rrd_options .= " COMMENT:'\\l'"; + $graph_return['legend_lines']++; $rrd_options .= " HRULE:999999999999999#FFFFFF:'" . rrdtool_escape("", $descr_len - 3) . " Tx'"; if (in_array("lst", $data_show)) { $rrd_options .= " GPRINT:outbits:LAST:%6.2lf%s"; } @@ -224,6 +233,7 @@ if (in_array("tot", $data_show)) if (in_array("max", $data_show)) { $rrd_options .= " GPRINT:outbits:MAX:%6.2lf%s"; } if (in_array("tot", $data_show)) { $rrd_options .= " GPRINT:totout:%6.2lf%s".$total_units; } $rrd_options .= " COMMENT:'\\l'"; + $graph_return['legend_lines']++; $rrd_options .= " HRULE:999999999999999#FFFFFF:'" . rrdtool_escape("", $descr_len - 4) . " Agg'"; if (in_array("lst", $data_show)) { $rrd_options .= " GPRINT:bits:LAST:%6.2lf%s"; } @@ -232,6 +242,7 @@ if (in_array("tot", $data_show)) if (in_array("max", $data_show)) { $rrd_options .= " GPRINT:bits:MAX:%6.2lf%s"; } if (in_array("tot", $data_show)) { $rrd_options .= " GPRINT:tot:%6.2lf%s".$total_units; } $rrd_options .= " COMMENT:'\\l'"; + $graph_return['legend_lines']++; if ($vars['trend']) { @@ -251,6 +262,7 @@ if (in_array("tot", $data_show) && $vars['previous'] == "yes") $rrd_options .= " VDEF:totoutX=outBX,TOTAL"; $rrd_options .= " VDEF:totX=octetsX,TOTAL"; $rrd_options .= " COMMENT:' \\l'"; + $graph_return['legend_lines']++; $rrd_options .= " HRULE:999999999999999#AAAAAA:'" . rrdtool_escape("Prev Total", $descr_len - 3) . " Rx'"; if (in_array("lst", $data_show)) { $rrd_options .= " GPRINT:inbitsX:LAST:%6.2lf%s"; } @@ -259,6 +271,7 @@ if (in_array("tot", $data_show) && $vars['previous'] == "yes") if (in_array("max", $data_show)) { $rrd_options .= " GPRINT:inbitsX:MAX:%6.2lf%s"; } if (in_array("tot", $data_show)) { $rrd_options .= " GPRINT:totinX:%6.2lf%s".$total_units; } $rrd_options .= " COMMENT:'\\l'"; + $graph_return['legend_lines']++; $rrd_options .= " HRULE:999999999999999#AAAAAA:'" . rrdtool_escape("", $descr_len - 3) . " Tx'"; if (in_array("lst", $data_show)) { $rrd_options .= " GPRINT:outbitsX:LAST:%6.2lf%s"; } @@ -267,6 +280,7 @@ if (in_array("tot", $data_show) && $vars['previous'] == "yes") if (in_array("max", $data_show)) { $rrd_options .= " GPRINT:outbitsX:MAX:%6.2lf%s"; } if (in_array("tot", $data_show)) { $rrd_options .= " GPRINT:totoutX:%6.2lf%s".$total_units; } $rrd_options .= " COMMENT:'\\l'"; + $graph_return['legend_lines']++; $rrd_options .= " HRULE:999999999999999#AAAAAA:'" . rrdtool_escape("", $descr_len - 4) . " Agg'"; if (in_array("lst", $data_show)) { $rrd_options .= " GPRINT:bitsX:LAST:%6.2lf%s"; } @@ -275,6 +289,7 @@ if (in_array("tot", $data_show) && $vars['previous'] == "yes") if (in_array("max", $data_show)) { $rrd_options .= " GPRINT:bitsX:MAX:%6.2lf%s"; } if (in_array("tot", $data_show)) { $rrd_options .= " GPRINT:totX:%6.2lf%s".$total_units; } $rrd_options .= " COMMENT:'\\l'"; + $graph_return['legend_lines']++; } diff --git a/html/includes/graphs/generic_simplex.inc.php b/html/includes/graphs/generic_simplex.inc.php index 15daa372..3ed828b4 100644 --- a/html/includes/graphs/generic_simplex.inc.php +++ b/html/includes/graphs/generic_simplex.inc.php @@ -25,14 +25,15 @@ $graph_return['valid_options'][] = "trend"; include($config['html_dir']."/includes/graphs/common.inc.php"); -$unit_text = str_pad(truncate($unit_text,18,''),18); -$line_text = str_pad(truncate($line_text,12,''),12); +// Fix length before escaping for layout purposes +$unit_text = rrdtool_escape(str_pad(truncate($unit_text,17,''),17)); +$line_text = rrdtool_escape(str_pad(truncate($line_text,11,''),11)); if (isset($unit_integer) && $unit_integer) { - $ds_format = '"%6.0lf "'; //NOTE. max value 999999 + $ds_format = '"%6.0lf"'; //NOTE. max value 999999 } else { - $ds_format = '"%6.2lf%s"'; + $ds_format = '"%5.1lf%s"'; } if ($multiplier) @@ -98,13 +99,13 @@ if ($vars['previous'] == "yes") if ($graph_max) { $rrd_options .= " CDEF:" . $ds . "_minmax=" . $ds . "_max," . $ds . "_min,-"; $rrd_options .= " AREA:" . $ds . "_min#ffffff00"; - $rrd_options .= " AREA:" . $ds . "_minmax#" . $colour_line . "60:STACK"; + $rrd_options .= " AREA:" . $ds . "_minmax#" . $colour_line . "30:STACK"; } else { $rrd_options .= " AREA:".$ds."#".$colour_area.":"; } //} -$rrd_options .= " COMMENT:'".$unit_text."Now Avg Max "; +$rrd_options .= " COMMENT:'".$unit_text."Now Avg Min Max"; if ($percentile) { @@ -119,7 +120,7 @@ $rrd_options .= " GPRINT:".$ds.":AVERAGE:".$ds_format; // if ($print_min || TRUE) // { -// $rrd_options .= " GPRINT:".$ds."_min:MIN:%6.2lf%s"; + $rrd_options .= " GPRINT:".$ds."_min:MIN:".$ds_format; // } $rrd_options .= " GPRINT:".$ds."_max:MAX:".$ds_format; diff --git a/html/includes/graphs/graph.inc.php b/html/includes/graphs/graph.inc.php index 8e928239..77269953 100644 --- a/html/includes/graphs/graph.inc.php +++ b/html/includes/graphs/graph.inc.php @@ -6,7 +6,7 @@ * * @package observium * @subpackage graphs - * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2021 Observium Limited + * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2022 Observium Limited * */ @@ -43,7 +43,7 @@ if (isset($vars['format']) && array_key_exists($vars['format'], $config['graph_f $graphfile = $config['temp_dir'] . "/" . strgen() . "." . $extension; -if (OBS_DEBUG) { print_vars($graphtype); } +if (OBS_DEBUG) { print_vars($vars); print_vars($graphtype); } if (isset($graphtype['type']) && isset($graphtype['subtype'])) { $type = $graphtype['type']; @@ -76,22 +76,26 @@ if (preg_match(OBS_PATTERN_RRDTIME, $vars['to'])) { $to = $vars['to']; }// if (preg_match(OBS_PATTERN_RRDTIME, $vars['from'])) { $from = $vars['from']; }// else { $from = $config['time']['day']; } if (isset($vars['period']) && is_numeric($vars['period'])) { - $to = time(); - $from = time() - $vars['period']; + $to = get_time(); + $from = get_time() - $vars['period']; $period = $vars['period']; -} elseif(preg_match('/[\-]*\d+[s|w|m|d|y|h]/', $vars['from'])) { +} elseif (preg_match('/[\-]*\d+[s|w|m|d|y|h]/', $vars['from'])) { // It seems we have AT-style/timespec. Just pass it through (some features will break because we can't calculate period) $from = $vars['from']; - if(preg_match('/[\-]*\d+[s|w|m|d|y|h]/', $vars['to'])) { $to = $vars['to']; } else { $to = 'NOW'; } + if (preg_match('/[\-]*\d+[s|w|m|d|y|h]/', $vars['to'])) { + $to = $vars['to']; + } else { + $to = 'now'; + } } else { - $from = (isset($vars['from'])) ? $vars['from'] : time() - 86400; - $to = (isset($vars['to'])) ? $vars['to'] : time(); + $from = (isset($vars['from']) && is_numeric($vars['from'])) ? $vars['from'] : get_time() - 86400; + $to = (isset($vars['to']) && is_numeric($vars['to'])) ? $vars['to'] : time(); if ($from < 0) { $from = $to + $from; } $period = $to - $from; } // Set prev_from & prev_to if we have a period -if(isset($period)) { +if (isset($period)) { $prev_from = $from - $period; $prev_to = $from; } @@ -100,41 +104,62 @@ $graph_include = FALSE; $definition_include = FALSE; //print_message("Graph type: $type, subtype: $subtype"); -if (is_file($config['html_dir'] . "/includes/graphs/$type/$subtype.inc.php")) -{ +if (is_file($config['html_dir'] . "/includes/graphs/$type/$subtype.inc.php")) { $graph_include = $config['html_dir'] . "/includes/graphs/$type/$subtype.inc.php"; -} -elseif (is_array($config['graph_types'][$type][$subtype]['ds'])) -{ +} elseif (is_array($config['graph_types'][$type][$subtype]['ds'])) { // Init tags array $tags = []; // Additional include with define some graph variables like $unit_text, $graph_title // Currently only for indexed definitions if ($config['graph_types'][$type][$subtype]['index'] && - is_file($config['html_dir'] . "/includes/graphs/$type/definition.inc.php")) - { + is_file($config['html_dir'] . "/includes/graphs/$type/definition.inc.php")) { $definition_include = $config['html_dir'] . "/includes/graphs/$type/definition.inc.php"; } $graph_include = $config['html_dir'] . "/includes/graphs/generic_definition.inc.php"; -} -elseif (is_file($config['html_dir'] . "/includes/graphs/$type/graph.inc.php")) -{ +} elseif (is_file($config['html_dir'] . "/includes/graphs/$type/graph.inc.php")) { $graph_include = $config['html_dir'] . "/includes/graphs/$type/graph.inc.php"; } -if ($graph_include) -{ +if ($graph_include) { include($config['html_dir'] . "/includes/graphs/$type/auth.inc.php"); - if (isset($auth) && $auth) - { - if ($definition_include) - { + if (isset($auth) && $auth) { + if ($definition_include) { include_once($definition_include); } - include($graph_include); + + // Requested a rigid height graph, probably for the dashboard. + // If we don't know the legend height, turn off legend. + // If we know the height and it won't fit, turn it off. + + if ( !(isset($vars['legend']) && $vars['legend'] == 'no') && + (isset($vars['rigid_height']) && $vars['rigid_height'] == 'yes') ) { + $line_height = ($width > 350 ? 14 : 12); // Set line height based on font size chosen by width + if (!isset($graph_return['legend_lines'])) { // Don't know legend length + print_debug('no legend height'); + $vars['legend'] = 'no'; + } + else if (($graph_return['legend_lines'] * $line_height) > ($height - 100)) { // Legend too long + print_debug('legend too tall: ' . $graph_return['legend_lines'] * $line_height); + $vars['legend'] = 'no'; + } + else { // Legend fits + $height = $height - ($graph_return['legend_lines'] * $line_height); + } + } + + $rrd_options .= ' --start ' . rrdtool_escape($from) . + ' --end ' . rrdtool_escape($to) . + ' --width ' . rrdtool_escape($width) . + ' --height ' . rrdtool_escape($height) . ' '; + + if ($vars['legend'] === 'no') { + $rrd_options .= ' -g'; + $legend = 'no'; + } + } } elseif(!isset($vars['command_only'])) { graph_error('no '. $type.'_'.$subtype.''); // Graph Template Missing diff --git a/html/includes/graphs/legend.inc.php b/html/includes/graphs/legend.inc.php index 748594d7..9be44273 100644 --- a/html/includes/graphs/legend.inc.php +++ b/html/includes/graphs/legend.inc.php @@ -65,6 +65,9 @@ if ($legend != 'no') if (in_array("max", $data_show)) { $rrd_options .= " COMMENT:' Max'"; } if (in_array("tot", $data_show)) { $rrd_options .= " COMMENT:' Total'"; } $rrd_options .= " COMMENT:'\\l'"; + + $graph_return['legend_lines']++; + } // EOF diff --git a/html/includes/graphs/multi-oid-entry/auth.inc.php b/html/includes/graphs/multi-oid-entry/auth.inc.php new file mode 100644 index 00000000..c9114a29 --- /dev/null +++ b/html/includes/graphs/multi-oid-entry/auth.inc.php @@ -0,0 +1,61 @@ +5 +// FIXME - do special handling of descriptions if all oid are identical or all devices are identical +// remove device/oid from descr if all identical +// arrange device/oid into aligned columns if the graph is wide enough + +if (!is_array($vars['id'])) { $vars['id'] = array($vars['id']); } + +$is_permitted = FALSE; + +$oids = []; +$rrd_list = []; + +foreach ($vars['id'] as $oid_entry_id) { + + $sql = "SELECT *"; + $sql .= " FROM `oids_entries`"; + $sql .= " LEFT JOIN `oids` USING(`oid_id`)"; + $sql .= " LEFT JOIN `devices` USING(`device_id`)"; + $sql .= " WHERE `oid_entry_id` = ?"; + + $oid = dbFetchRow($sql, array($oid_entry_id)); + if (is_numeric($oid['device_id']) && ($auth || device_permitted($oid['device_id']))) { + $oids[] = $oid; + $is_permitted = TRUE; + + $rrd_file = get_rrd_path($oid, "oid-" . $oid['oid'] . "-" . $oid['oid_type'] . ".rrd"); + if (rrd_is_file($rrd_file, TRUE)) { + $rrd_list[] = [ 'filename' => $rrd_file, + 'descr' => $oid['hostname'] . ' ' . $oid['oid_name'], + 'ds' => 'value']; + } + + } else { + // Bail on first rejection + $is_permitted = FALSE; + } +} + +if ($auth || $is_permitted || $_SESSION['userlevel'] >= 5) { + $title_array = array(); + $title_array[] = array('text' => 'Multiple OIDs'); + $title_array[] = array('text' => safe_count($vars['id']) . ' Entries'); + $auth = TRUE; +} + +unset($is_permitted); + +// EOF \ No newline at end of file diff --git a/html/includes/graphs/multi-oid-entry/line.inc.php b/html/includes/graphs/multi-oid-entry/line.inc.php new file mode 100644 index 00000000..1fb0f8fb --- /dev/null +++ b/html/includes/graphs/multi-oid-entry/line.inc.php @@ -0,0 +1,25 @@ + escape_html($port['port_label']) . (strlen($port['ifAlias']) ? " (".escape_html($port['ifAlias']).")" : ''), 'url' => generate_url(array('page' => 'device', 'device' => $device['device_id'], 'tab' => 'port', 'port' => $port['port_id']))); $graph_title = device_name($device, TRUE) . " :: " . $port['port_label_short']; + + // check if the port has an alias available and add it to the title + if(!empty($port['ifAlias'])) { + $graph_title = $graph_title . " :: " . $port['ifAlias']; + } + $rrd_filename = get_port_rrdfilename($port, NULL, TRUE); if ($vars['type'] == 'port_bits') diff --git a/html/includes/graphs/sensor/airflow.inc.php b/html/includes/graphs/sensor/airflow.inc.php deleted file mode 100644 index cbaf46cb..00000000 --- a/html/includes/graphs/sensor/airflow.inc.php +++ /dev/null @@ -1,30 +0,0 @@ - $dash['dash_name'], 'url' => generate_url(array('page' => "dashboard", 'dash' => $dash['dash_id'])), 'icon' => $config['icon']['overview']); + } + } + + $entries[] = array('divider' => TRUE); + if ($_SESSION['userlevel'] > 7) { - - $dashboards = dbFetchRows("SELECT * FROM `dashboards`"); - - $entries = array(); - - if (safe_count($dashboards)) - { - //$navbar['observium']['dash']['text'] = "Dashboards"; - foreach ($dashboards as $dash) - { - $entries[] = array('text' => $dash['dash_name'], 'url' => generate_url(array('page' => "dashboard", 'dash' => $dash['dash_id'])), 'icon' => $config['icon']['overview']); - } - } - - $entries[] = array('divider' => TRUE); - - $entries[] = array('title' => 'Create Dashboard', 'url' => generate_url(array('page' => 'dashboard_add')), 'icon' => $config['icon']['plus']); - } $navbar['observium']['entries'][] = array('title' => 'Dashboard', 'url' => generate_url(array('page' => 'dashboard')), 'icon' => $config['icon']['overview'], 'entries' => $entries); @@ -106,7 +103,7 @@ $menu_start = utime(); $ids[] = $group['group_id']; } } - foreach (dbFetchRows("SELECT COUNT(*) AS `count`, `group_id` FROM `group_table` WHERE 1" . generate_query_values($ids, 'group_id') . " GROUP BY `group_id`") as $entry) { + foreach (dbFetchRows("SELECT COUNT(*) AS `count`, `group_id` FROM `group_table` WHERE " . generate_query_values_ng($ids, 'group_id') . " GROUP BY `group_id`") as $entry) { $groups_count[$entry['group_id']] = $entry['count']; } unset($ids); @@ -204,15 +201,14 @@ $menu_start = utime(); } $navbar['observium']['entries'][] = array('title' => 'Network Map', 'url' => generate_url(array('page' => 'map')), 'icon' => $config['icon']['netmap']); - + $navbar['observium']['entries'][] = array('title' => 'Network Traffic Map', 'url' => generate_url(array('page' => 'map_traffic')), 'icon' => $config['icon']['map']); $navbar['observium']['entries'][] = array('title' => 'Event Log', 'url' => generate_url(array('page' => 'eventlog')), 'icon' => $config['icon']['eventlog']); $navbar['observium']['entries'][] = array('divider' => TRUE); - if ($_SESSION['userlevel'] >= 7) - { + if ($_SESSION['userlevel'] >= 7) { // Print Contacts $counts['contacts'] = dbFetchCell("SELECT COUNT(*) FROM `alert_contacts`"); @@ -220,31 +216,30 @@ $menu_start = utime(); $navbar['observium']['entries'][] = array('divider' => TRUE); } - if (OBSERVIUM_EDITION !== 'community' && $_SESSION['userlevel'] >= 5) - { + if (OBSERVIUM_EDITION !== 'community' && $_SESSION['userlevel'] >= 5) { // Custom OIDs $oids = dbFetchRows("SELECT `oids`.*, COUNT(*) AS `count` FROM `oids` JOIN `oids_entries` ON `oids`.`oid_id` = `oids_entries`.`oid_id` WHERE 1 GROUP BY `oids`.`oid_id`"); $oid_count = safe_count($oids); - foreach ($oids AS $oid) - { - $oids_menu[] = array('title' => $oid['oid_descr'], 'url' => generate_url(array('page' => 'customoid', 'oid_id' => $oid['oid_id'])), 'count' => $oid['count'], 'icon' => $config['icon']['customoid']); + $oids_menu = []; + foreach ($oids as $oid) { + $oids_menu[] = [ 'title' => $oid['oid_descr'], 'url' => generate_url([ 'page' => 'customoid', 'oid_id' => $oid['oid_id'] ]), 'count' => $oid['count'], 'icon' => $config['icon']['customoid'] ]; } - $navbar['observium']['entries'][] = array('title' => 'Custom OIDs', 'url' => generate_url(array('page' => 'customoids')), 'count' => $oid_count, 'icon' => $config['icon']['customoid'], 'entries' => $oids_menu); + $navbar['observium']['entries'][] = [ 'title' => 'Custom OIDs', 'url' => generate_url([ 'page' => 'customoids' ]), 'count' => $oid_count, 'icon' => $config['icon']['customoid'], 'entries' => $oids_menu ]; //$navbar['observium']['entries'][] = array('divider' => TRUE); - $navbar['observium']['entries'][] = array('title' => 'Probes', 'url' => generate_url(array('page' => 'probes')), 'icon' => $config['icon']['status']); - $navbar['observium']['entries'][] = array('divider' => TRUE); + // Probes + $navbar['observium']['entries'][] = [ 'title' => 'Probes', 'url' => generate_url([ 'page' => 'probes' ]), 'icon' => $config['icon']['status'] ]; + $navbar['observium']['entries'][] = [ 'divider' => TRUE ]; } $navbar['observium']['entries'][] = array('title' => 'Hardware Inventory', 'url' => generate_url(array('page' => 'inventory')), 'icon' => $config['icon']['inventory']); - if ($cache['packages']['count']) - { + if ($cache['packages']['count']) { $navbar['observium']['entries'][] = array('title' => 'Software Packages', 'url' => generate_url(array('page' => 'packages')), 'icon' => $config['icon']['packages']); } @@ -568,12 +563,12 @@ $menu_start = utime(); //r($cache['sensor_types']); - $menu_items[0] = array('fanspeed', 'humidity', 'temperature', 'airflow'); - $menu_items[1] = array('current', 'voltage', 'power', 'apower', 'rpower', 'frequency'); + $menu_items[0] = [ 'temperature', 'humidity', 'fanspeed', 'airflow' ]; + $menu_items[1] = [ 'current', 'voltage', 'power', 'apower', 'rpower', 'frequency' ]; $menu_items[2] = array_diff(array_keys((array)$cache['sensors']['types']), $menu_items[0], $menu_items[1]); - foreach ($menu_items as $items) { - if (is_array($items)) { sort($items); } + foreach ($menu_items as $key => $items) { + if ($key > 1 && is_array($items)) { sort($items); } // Do not sort first basic health entities foreach ($items as $item) { @@ -753,7 +748,7 @@ $menu_start = utime(); echo(' '); } @@ -992,7 +991,9 @@ $(function() { $('#suggestions').fadeOut(); // Hide the suggestions box } else { key_count_global++; - setTimeout("lookupwait(" + key_count_global + ",\"" + inputString + "\")", 300); // Added timeout 0.3s before send query + // Added timeout 0.3s before send query + // Prevent use quotes in query string, for do not use XSS attacks + setTimeout("lookupwait(" + key_count_global + ",\"" + inputString.replace(/"/g, '\\x22').replace(/'/g, '\\x27') + "\")", 300); } key_press_time = Date.now() } diff --git a/html/includes/notifications.inc.php b/html/includes/notifications.inc.php index 26ec3159..1737488b 100644 --- a/html/includes/notifications.inc.php +++ b/html/includes/notifications.inc.php @@ -23,6 +23,7 @@ $alerts_item = get_cache_item('alerts'); if (!ishit_cache_item($notifications_item) || !ishit_cache_item($alerts_item)) { // Generate/collect notifications/alerts + if ($_SESSION['userlevel'] > 7) { $latest = []; $latest['version'] = get_obs_attrib('latest_ver'); @@ -39,17 +40,6 @@ if (!ishit_cache_item($notifications_item) || !ishit_cache_item($alerts_item)) { ]; } - - if (version_compare(PHP_VERSION, OBS_MIN_PHP_VERSION, '<')) { - $notifications[] = array('text' => '

Your PHP version is too old.

- Your currently installed PHP version ' . PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION . '.' . PHP_RELEASE_VERSION . ' - is older than the required minimum. - Please upgrade your version of PHP to prevent possible incompatibilities and security problems.
- Currently recommended version(s): 7.2.x and newer.', - 'severity' => 'danger', - 'unixtime' => $config['time']['now']); - } - //if (!function_exists('hash_exists')) //{ // $alerts[] = array('text' => 'The PHP `hash` module is missing This will cause editing and other forms to fail. Please install it.', 'severity' => 'danger'); @@ -63,8 +53,7 @@ if (!ishit_cache_item($notifications_item) || !ishit_cache_item($alerts_item)) { } // Warning about web_url config, only for ssl - if (is_ssl() && preg_match('/^http:/', $config['web_url'])) - { + if (is_ssl() && preg_match('/^http:/', $config['web_url'])) { $notifications[] = array('text' => 'Setting \'web_url\' for "External Web URL" not set or incorrect, please update on ' . generate_link('Global Settings Edit', array('page' => 'settings', 'section' => 'wui')) . ' page.', 'severity' => 'warning', 'unixtime' => $config['time']['now']); } @@ -94,15 +83,6 @@ if (!ishit_cache_item($notifications_item) || !ishit_cache_item($alerts_item)) { 'text' => $entry['message'], 'severity' => $entry['severity']); } - - // Warning about obsolete config on some pages - if (OBS_DEBUG || - in_array($vars['tab'], array('data', 'perf', 'edit', 'showtech')) || - in_array($vars['page'], array('pollerlog', 'settings', 'preferences'))) - { - // FIXME move to notification center? - print_obsolete_config(); - } } if (isset($config['alerts']['suppress']) && $config['alerts']['suppress']) { @@ -142,6 +122,53 @@ if (!ishit_cache_item($notifications_item) || !ishit_cache_item($alerts_item)) { $alerts = array_merge(get_cache_data($alerts_item), $alerts); } +// Admin level notifications on settings/perf pages +if ($_SESSION['userlevel'] >= 10 && (in_array($vars['tab'], [ 'data', 'perf', 'edit', 'showtech' ]) || + in_array($vars['page'], [ 'about', 'pollerlog', 'settings', 'preferences' ]))) { + + if (version_compare(PHP_VERSION, OBS_MIN_PHP_VERSION, '<')) { + $notifications[] = array('text' => '

Your PHP version is too old.

+ Your currently installed PHP version ' . PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION . '.' . PHP_RELEASE_VERSION . ' + is older than the required minimum. + Please upgrade your version of PHP to prevent possible incompatibilities and security problems.
+ Currently recommended version(s): 7.2.x and newer.', + 'severity' => 'danger', + 'unixtime' => $config['time']['now']); + $alerts[] = [ + 'title' => 'PHP version is old', + 'text' => 'Your currently installed PHP version ' . PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION . '.' . PHP_RELEASE_VERSION . + " is older than the recommended minimum 7.2.x.", + 'severity' => 'alert' + ]; + } + + // Same for MySQL/MariaDB + $versions = get_versions(); + if ($versions['mysql_old']) { + $mysql_recommented = $versions['mysql_name'] === 'MariaDB' ? OBS_MIN_MARIADB_VERSION : OBS_MIN_MYSQL_VERSION; + + $notifications[] = array('text' => '

Your '.$versions['mysql_name'].' version is old.

+ Your currently installed '.$versions['mysql_name'].' version ' . $versions['mysql_version'] . ' + is older than the recommended minimum. + Please upgrade your version of '.$versions['mysql_name'].' to prevent possible incompatibilities and security problems.
+ Currently recommended version is '.$mysql_recommented.' and newer.', + 'severity' => 'warning', + 'unixtime' => $config['time']['now']); + $alerts[] = [ + 'title' => $versions['mysql_name'].' version is old', + 'text' => 'Your currently installed '.$versions['mysql_name'].' version ' . $versions['mysql_version'] . + " is older than the recommended minimum $mysql_recommented.", + 'severity' => 'warning' + ]; + } + + // Warning about obsolete config on some pages + if (OBS_DEBUG) { + // FIXME move to notification center? + print_obsolete_config(); + } + +} // Sort by unixtime $notifications = array_sort_by($notifications, 'unixtime', SORT_DESC, SORT_NUMERIC); diff --git a/html/includes/port-sort-select.inc.php b/html/includes/port-sort-select.inc.php index c360182d..083857d3 100644 --- a/html/includes/port-sort-select.inc.php +++ b/html/includes/port-sort-select.inc.php @@ -57,6 +57,9 @@ switch ($vars['sort']) { case 'mac': $select .= ',`ifPhysAddress`'; break; + case 'mtu': + $select .= ',`ifMtu`'; + break; case 'device': $select .= ',`devices`.`hostname`'; break; diff --git a/html/includes/port-sort.inc.php b/html/includes/port-sort.inc.php index c743c771..dc0b82d4 100644 --- a/html/includes/port-sort.inc.php +++ b/html/includes/port-sort.inc.php @@ -64,6 +64,9 @@ switch ($vars['sort']) { case 'media': $ports = array_sort_by($ports, 'ifType', $sort_order, SORT_STRING); break; + case 'mtu': + $ports = array_sort_by($ports, 'ifMtu', $sort_order, SORT_NUMERIC); + break; case 'descr': $ports = array_sort_by($ports, 'ifAlias', $sort_order, SORT_STRING); break; diff --git a/html/includes/print/addresses.inc.php b/html/includes/print/addresses.inc.php index 788d2f91..59a3e7af 100644 --- a/html/includes/print/addresses.inc.php +++ b/html/includes/print/addresses.inc.php @@ -1,5 +1,4 @@ ' . PHP_EOL; $string .= '
' . PHP_EOL; - if ($events['short']) - { - $string .= ' ' . PHP_EOL; + if ($events['short']) { + $string .= ' ' . PHP_EOL; } else { $string .= ' ' . PHP_EOL; @@ -217,26 +224,26 @@ function get_alert_log($vars) switch ($var) { // case 'alert_entry': -// $where .= generate_query_values($value, 'alert_table_id'); +// $where .= generate_query_values_and($value, 'alert_table_id'); // break; case 'log_type': - $where .= generate_query_values($value, 'log_type'); + $where .= generate_query_values_and($value, 'log_type'); break; case 'alert_test_id': - $where .= generate_query_values($value, 'alert_test_id'); + $where .= generate_query_values_and($value, 'alert_test_id'); break; case 'device': case 'device_id': - $where .= generate_query_values($value, 'device_id'); + $where .= generate_query_values_and($value, 'device_id'); break; case 'entity_id': - $where .= generate_query_values($value, 'entity_id'); + $where .= generate_query_values_and($value, 'entity_id'); break; case 'entity_type': - $where .= generate_query_values($value, 'entity_type'); + $where .= generate_query_values_and($value, 'entity_type'); break; case 'message': - $where .= generate_query_values($value, 'message', '%LIKE%'); + $where .= generate_query_values_and($value, 'message', '%LIKE%'); break; case 'timestamp_from': $where .= ' AND `timestamp` >= ?'; diff --git a/html/includes/print/arptable.inc.php b/html/includes/print/arptable.inc.php index 88732330..e6d5651b 100644 --- a/html/includes/print/arptable.inc.php +++ b/html/includes/print/arptable.inc.php @@ -6,7 +6,7 @@ * * @package observium * @subpackage web - * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2020 Observium Limited + * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2022 Observium Limited * */ @@ -38,33 +38,32 @@ function print_arptable($vars) { case 'device': case 'device_id': - $where .= generate_query_values($value, 'device_id'); + $where .= generate_query_values_and($value, 'device_id'); break; case 'port': case 'port_id': - $where .= generate_query_values($value, 'I.port_id'); + $where .= generate_query_values_and($value, 'I.port_id'); break; case 'ip_version': - $where .= generate_query_values($value, 'ip_version'); + $where .= generate_query_values_and($value, 'ip_version'); break; case 'address': - if (isset($vars['searchby']) && $vars['searchby'] == 'ip') + if (isset($vars['searchby']) && $vars['searchby'] === 'ip') { $value = trim($value); - if (strpos($value, ':') !== FALSE) - { - if (Net_IPv6::checkIPv6($value)) + if (str_contains($value, ':')) { + if (get_ip_version($value) === 5) { - $value = Net_IPv6::uncompress($value, TRUE); + $value = ip_uncompress($value); } else { // FIXME. Need another conversion ("2001:b08:b08" -> "2001:0b08:0b08") -- mike } } - $where .= generate_query_values($value, 'ip_address', '%LIKE%'); + $where .= generate_query_values_and($value, 'ip_address', '%LIKE%'); } else { // MAC Addresses - $value = str_replace(array(':', ' ', '-', '.', '0x'), '', $value); - $where .= generate_query_values($value, 'mac_address', '%LIKE%'); + $value = str_replace([ ':', ' ', '-', '.', '0x' ], '', $value); + $where .= generate_query_values_and($value, 'mac_address', '%LIKE%'); } break; } diff --git a/html/includes/print/authlog.inc.php b/html/includes/print/authlog.inc.php index 76bd68d2..629e9983 100644 --- a/html/includes/print/authlog.inc.php +++ b/html/includes/print/authlog.inc.php @@ -1,5 +1,4 @@ element based on the contents of the $header array, modified by the current request $vars + * + * @param array $header Array with table header definition including columns and classes. + * @param array $vars Array with current selected column ID and/or variables for generate column link + * @return string $string + */ + +function generate_table_header($header = [], $vars = []) { + + // Store current $vars sort variables + $sort = $vars['sort']; + $sort_order = strtolower($vars['sort_order']); + if (!in_array($sort_order, array('asc', 'desc', 'reset'))) { + $sort_order = 'asc'; + } + + // Reset current $vars sort variables + unset($vars['sort'], $vars['sort_order']); + + $output = ''; + + $output .= ' ' . PHP_EOL; + + //r($header); + + // Loop each column generating a ' . PHP_EOL; + } + + $output .= ' ' . PHP_EOL; + $output .= ' ' . PHP_EOL; + + return $output; + + +} + +function generate_table_header_field($field_id, $field, $vars, $sort, $sort_order) +{ + + if(empty($field)) { // No label, generate empty column header. + $return = ''; + } elseif ( is_numeric($field_id) && !is_array($field)) { // Label without id, generate simple column header + $return = $field; + } else { + if(!is_array($field)) { $field = [ 'label' => $field ]; } + if(!isset($field['label'])) { $field['label'] = $field[0]; } + + if($sort == $field_id) { + $field['label'] = ''.$field['label'].''; + if($sort_order == 'asc') { + $new_vars = [ 'sort' => $field_id, 'sort_order' => 'desc' ]; + $field['caret'] = ' '; + } else { + $new_vars = [ 'sort' => NULL, 'sort_order' => NULL ]; + $field['caret'] = ' '; + } + } else { + $new_vars = [ 'sort' => $field_id ]; + } + $return = ''.$field['label'].$field['caret'].''; + + // Generate slash separated links for subfields + if(isset($field['subfields'])) + { + foreach($field['subfields'] as $subfield_id => $subfield) + { + //r($subfield); r($subfield_id); + $subfields[] = generate_table_header_field($subfield_id, $subfield, $vars, $sort, $sort_order); + } + $return .= ' ['.implode(" / ", $subfields).']'; + } + } + + return $return; + +} + /** * Helper function for generate table header with sort links * This used in other print_* functions diff --git a/html/includes/print/dot1xtable.inc.php b/html/includes/print/dot1xtable.inc.php index 05285f77..43f93190 100644 --- a/html/includes/print/dot1xtable.inc.php +++ b/html/includes/print/dot1xtable.inc.php @@ -1,5 +1,4 @@ ' . PHP_EOL; if ($events['short']) { - $string .= ' ' . PHP_EOL; + $string .= ' ' . PHP_EOL; } else { $string .= ' ' . PHP_EOL; @@ -215,22 +214,22 @@ function get_events_array($vars) { case 'device': case 'device_id': - $where .= generate_query_values($value, 'device_id'); + $where .= generate_query_values_and($value, 'device_id'); break; case 'port': case 'entity': case 'entity_id': - $where .= generate_query_values($value, 'entity_id'); + $where .= generate_query_values_and($value, 'entity_id'); break; case 'severity': - $where .= generate_query_values($value, 'severity'); + $where .= generate_query_values_and($value, 'severity'); break; case 'type': case 'entity_type': - $where .= generate_query_values($value, 'entity_type'); + $where .= generate_query_values_and($value, 'entity_type'); break; case 'message': - $where .= generate_query_values($value, 'message', '%LIKE%'); + $where .= generate_query_values_and($value, 'message', '%LIKE%'); break; case 'timestamp_from': $where .= ' AND `timestamp` >= ?'; @@ -243,8 +242,8 @@ function get_events_array($vars) case "group": case "group_id": $values = get_group_entities($value); - $where .= generate_query_values($values, 'entity_id'); - $where .= generate_query_values(get_group_entity_type($value), 'entity_type'); + $where .= generate_query_values_and($values, 'entity_id'); + $where .= generate_query_values_and(get_group_entity_type($value), 'entity_type'); break; } } diff --git a/html/includes/print/fdbtable.inc.php b/html/includes/print/fdbtable.inc.php index 74866176..902de0c4 100644 --- a/html/includes/print/fdbtable.inc.php +++ b/html/includes/print/fdbtable.inc.php @@ -18,6 +18,7 @@ * */ function print_fdbtable($vars) { + //r($vars); $entries = get_fdbtable_array($vars); if (!$entries['count']) { @@ -27,8 +28,13 @@ function print_fdbtable($vars) { } $list = array('device' => FALSE, 'port' => FALSE); - if (!isset($vars['device']) || empty($vars['device']) || $vars['page'] === 'search') { $list['device'] = TRUE; } - if (!isset($vars['port']) || empty($vars['port']) || $vars['page'] === 'search') { $list['port'] = TRUE; } + + + + if (!isset($vars['device']) || is_array($vars['device']) || empty($vars['device']) || $vars['page'] === 'search') { $list['device'] = TRUE; } + if (!isset($vars['port']) || is_array($vars['port']) || empty($vars['port']) || $vars['page'] === 'search') { $list['port'] = TRUE; } + + //r($list); $string = generate_box_open(); @@ -119,50 +125,54 @@ function get_fdbtable_array($vars) { // Do not show deleted entries by default $vars['deleted'] = 0; } - //r($vars); + foreach ($vars as $var => $value) { - if ($value != '') { - switch ($var) { - case 'device': - case 'device_id': - $where .= generate_query_values($value, 'F.device_id'); - break; - case 'port': - case 'port_id': - $where .= generate_query_values($value, 'F.port_id'); - break; - case 'interface': - case 'port_name': - $where .= generate_query_values($value, 'I.ifDescr', 'LIKE%'); + + // Skip empty variables (and array with empty first entry) when building query + if ($value == '' || (is_array($value) && count($value) == 1 && $value[0] == '')) { continue; } + + switch ($var) { + case 'device': + case 'device_id': + $where .= generate_query_values_and($value, 'F.device_id'); + break; + case 'port': + case 'port_id': + $where .= generate_query_values_and($value, 'F.port_id'); + break; + case 'interface': + case 'port_name': + $where .= generate_query_values_and($value, 'I.ifDescr', 'LIKE%'); + $join_ports = TRUE; + break; + case 'trunk': + if (get_var_true($value)) { + $where .= " AND (`I`.`ifTrunk` IS NOT NULL AND `I`.`ifTrunk` != '')"; $join_ports = TRUE; - break; - case 'trunk': - if (get_var_true($value)) { - $where .= " AND (`I`.`ifTrunk` IS NOT NULL AND `I`.`ifTrunk` != '')"; - $join_ports = TRUE; - } elseif (in_array($value, [ 'none', 'no', '0' ])) { - $where .= " AND (`I`.`ifTrunk` IS NULL OR `I`.`ifTrunk` = '')"; - $join_ports = TRUE; - } - break; - case 'vlan_id': - $where .= generate_query_values($value, 'F.vlan_id'); - break; - case 'vlan_name': - $where .= generate_query_values($value, 'V.vlan_name'); - break; - case 'address': - if (str_contains_array($value, [ '*', '?' ])) { - $like = 'LIKE'; - } else { - $like = '%LIKE%'; - } - $where .= generate_query_values(str_replace([ ':', ' ', '-', '.', '0x' ],'', $value), 'F.mac_address', $like); - break; - case 'deleted': - $where .= ' AND `deleted` = ?'; - $params[] = $value; - } + } + else if (in_array($value, ['none', 'no', '0'])) { + $where .= " AND (`I`.`ifTrunk` IS NULL OR `I`.`ifTrunk` = '')"; + $join_ports = TRUE; + } + break; + case 'vlan_id': + $where .= generate_query_values_and($value, 'F.vlan_id'); + break; + case 'vlan_name': + $where .= generate_query_values_and($value, 'V.vlan_name'); + break; + case 'address': + if (str_contains_array($value, ['*', '?'])) { + $like = 'LIKE'; + } + else { + $like = '%LIKE%'; + } + $where .= generate_query_values_and(str_replace([':', ' ', '-', '.', '0x'], '', $value), 'F.mac_address', $like); + break; + case 'deleted': + $where .= ' AND `deleted` = ?'; + $params[] = $value; } } @@ -206,6 +216,9 @@ function get_fdbtable_array($vars) { $query .= $sort; $query .= " LIMIT $start,$pagesize"; + //r($query); + //r($params); + // Query addresses //$array['entries'] = dbFetchRows($query, $params, TRUE); $array['entries'] = dbFetchRows($query, $params); diff --git a/html/includes/print/inventory.inc.php b/html/includes/print/inventory.inc.php index 514de8f3..4048115f 100644 --- a/html/includes/print/inventory.inc.php +++ b/html/includes/print/inventory.inc.php @@ -6,7 +6,7 @@ * * @package observium * @subpackage web - * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2021 Observium Limited + * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2022 Observium Limited * */ @@ -28,31 +28,31 @@ function generate_inventory_query($vars) { { case 'device': case 'device_id': - $where .= generate_query_values($value, 'device_id'); + $where .= generate_query_values_and($value, 'device_id'); break; case 'os': - $where .= generate_query_values($value, 'os'); + $where .= generate_query_values_and($value, 'os'); $select[] = 'devices.os'; $devices = TRUE; break; case 'parts': case 'entPhysicalModelName': - $where .= generate_query_values($value, 'entPhysicalModelName', 'LIKE'); + $where .= generate_query_values_and($value, 'entPhysicalModelName', 'LIKE'); break; case 'serial': case 'entPhysicalSerialNum': - $where .= generate_query_values($value, 'entPhysicalSerialNum', '%LIKE%'); + $where .= generate_query_values_and($value, 'entPhysicalSerialNum', '%LIKE%'); break; case 'description': case 'entPhysicalDescr': - $where .= generate_query_values($value, 'entPhysicalDescr', '%LIKE%'); + $where .= generate_query_values_and($value, 'entPhysicalDescr', '%LIKE%'); break; case 'class': case 'entPhysicalClass': - $where .= generate_query_values($value, 'entPhysicalClass', '%LIKE%'); + $where .= generate_query_values_and($value, 'entPhysicalClass', '%LIKE%'); break; case 'deleted': - $where .= generate_query_values($value, 'deleted', 'NOT NULL'); + $where .= generate_query_values_and($value, 'deleted', 'NOT NULL'); break; } } @@ -371,15 +371,15 @@ relay // vendor + model + hw $ent_model = ''; - if ($ent['entPhysicalModelName'] && !in_array($ent['entPhysicalModelName'], [ 'N/A' ])) { - if ($ent['entPhysicalMfgName'] && !in_array($ent['entPhysicalMfgName'], [ 'N/A' ])) { + if ($ent['entPhysicalModelName'] && is_valid_param($ent['entPhysicalModelName'], 'hardware')) { + if ($ent['entPhysicalMfgName'] && is_valid_param($ent['entPhysicalMfgName'], 'vendor')) { $ent_model .= $ent['entPhysicalMfgName']; } $ent_model .= ' ' . $ent['entPhysicalModelName']; - if ($ent['entPhysicalHardwareRev'] && !in_array($ent['entPhysicalHardwareRev'], [ 'N/A' ])) { - $ent_model .= " ${ent['entPhysicalHardwareRev']}"; + if ($ent['entPhysicalHardwareRev'] && is_valid_param($ent['entPhysicalHardwareRev'], 'revision')) { + $ent_model .= " " . $ent['entPhysicalHardwareRev']; } $ent_model = trim($ent_model); } diff --git a/html/includes/print/logalert.inc.php b/html/includes/print/logalert.inc.php index b2b88661..7a0e9594 100644 --- a/html/includes/print/logalert.inc.php +++ b/html/includes/print/logalert.inc.php @@ -6,7 +6,7 @@ * * @package observium * @subpackage web - * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2020 Observium Limited + * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2022 Observium Limited * */ @@ -113,17 +113,17 @@ function get_logalert_log($vars) switch ($var) { case 'la_id': - $where .= generate_query_values($value, 'la_id'); + $where .= generate_query_values_and($value, 'la_id'); break; case 'device': case 'device_id': - $where .= generate_query_values($value, 'device_id'); + $where .= generate_query_values_and($value, 'device_id'); break; case 'program': - $where .= generate_query_values($value, 'program', '%LIKE%'); + $where .= generate_query_values_and($value, 'program', '%LIKE%'); break; case 'message': - $where .= generate_query_values($value, 'message', '%LIKE%'); + $where .= generate_query_values_and($value, 'message', '%LIKE%'); break; case 'timestamp_from': $where .= ' AND `timestamp` >= ?'; diff --git a/html/includes/print/mac_addresses.inc.php b/html/includes/print/mac_addresses.inc.php index 876d359a..0b048eea 100644 --- a/html/includes/print/mac_addresses.inc.php +++ b/html/includes/print/mac_addresses.inc.php @@ -1,5 +1,4 @@ ' . generate_menu_link($entry['url'], ' ' . $entry['title'], $entry['count'], 'label', NULL, $entry['alert_count']) . PHP_EOL); + echo(str_pad('',($level-1)*2) . '
' . generate_device_link($storage) . ''.generate_entity_link('storage', $storage).''.$storage['human_type'].' '.$total.' '.$used.' '.$free.''; - $timediff = $GLOBALS['config']['time']['now'] - strtotime($entry['timestamp']); - $string .= generate_tooltip_link('', format_uptime($timediff, "short-3"), format_timestamp($entry['timestamp']), NULL) . ''; + $string .= generate_tooltip_time($entry['timestamp']) . ''; $string .= format_timestamp($entry['timestamp']) . '
element + foreach($header AS $id => $col) + { + + //if (in_array($id, ['class', 'group', 'style'])) { continue; } // skip html metadata + + if ($id === 'class' || $id === 'style') { continue; } // skip html data + + $fields = []; // Empty array for fields + + if(empty($col) || !is_array($col)) { $col = [ $id => $col ]; } // If col is not an array, make it one + + if($id == 'state-marker') { $col['class'] = 'state-marker'; } // Hard code handling of state-marker + + // Loop each field and generate an element + foreach ($col as $field_id => $field) { + + if ($field_id === 'class' || $field_id === 'style' || $field_id == 'subfields') { continue; } // skip html data + + $header_field = generate_table_header_field($field_id, $field, $vars, $sort, $sort_order); + + if(strlen($header_field) > 0) { + $fields[] = $header_field; + } + + } + $output .= ' '; + $output .= implode(' / ', $fields); + $output .= '
'; - $timediff = $GLOBALS['config']['time']['now'] - strtotime($entry['timestamp']); - $string .= generate_tooltip_link('', format_uptime($timediff, "short-3"), format_timestamp($entry['timestamp']), NULL) . ''; + $string .= generate_tooltip_time($entry['timestamp']) . ''; $string .= format_timestamp($entry['timestamp']) . '
' . PHP_EOL; + + $cols = [ + [ NULL, 'class="state-marker"' ], + [ NULL, 'style="width: 1px;"' ], + 'device' => [ 'Local address', 'style="width: 150px;"' ], + 'local_as' => [ 'Local AS / VRF', 'style="width: 110px;"' ], + [ NULL, 'style="width: 20px;"' ], + 'peer_ip' => [ 'Peer address', 'style="width: 150px;"' ], + 'peer_as' => [ 'Remote AS', 'style="width: 90px;"' ], + 'type' => [ 'Type', 'style="width: 50px;"' ], + [ 'Family', 'style="width: 50px;"' ], + 'state' => 'State', + 'uptime' => 'Uptime / Updates', + ]; + //if (!$list['device']) { unset($cols['device']); } + $string .= get_table_header($cols, $vars); + + $string .= ' ' . PHP_EOL; + + foreach ($entries['entries'] as $peer) { + $local_dev = device_by_id_cache($peer['device_id']); + $local_as = ($list['device'] ? ' (AS' . $peer['human_local_as'] . ')' : ''); + $local_name = generate_device_link_short($local_dev, [ 'tab' => 'routing', 'proto' => 'bgp' ], 18); + $local_ip = generate_device_link($local_dev, $peer['human_localip'] . $local_as, [ 'tab' => 'routing', 'proto' => 'bgp' ]); + $peer_as = 'AS' . $peer['human_remote_as']; + if ($peer['peer_device_id']) { + $peer_dev = device_by_id_cache($peer['peer_device_id']); + $peer_name = generate_device_link_short($peer_dev, [ 'tab' => 'routing', 'proto' => 'bgp' ], 18); + } else { + $peer_name = $peer['reverse_dns']; + } + $peer_ip = generate_entity_link("bgp_peer", $peer, $peer['human_remoteip']); + $peer_afis = &$entries['afisafi'][$peer['device_id']][$peer['bgpPeer_id']]; + $peer_afis_html = []; + + // Generate AFI/SAFI labels + foreach ($peer_afis as $peer_afi) { + // $peer_afi_html = ''; + if (isset($GLOBALS['config']['routing_afis_name'][$peer_afi['afi']])) { + $afi_num = $GLOBALS['config']['routing_afis_name'][$peer_afi['afi']]; + $afi_class = $GLOBALS['config']['routing_afis'][$afi_num]['class']; + } else { + $afi_class = 'default'; + } + + if (isset($GLOBALS['config']['routing_safis_name'][$peer_afi['safi']])) { + // Named SAFI + $safi_num = $GLOBALS['config']['routing_safis_name'][$peer_afi['safi']]; + $safi_class = $GLOBALS['config']['routing_safis'][$safi_num]['class']; + + } elseif (isset($GLOBALS['config']['routing_safis'][$peer_afi['safi']])) { + // Numeric SAFI + $safi_num = $peer_afi['safi']; + $peer_afi['safi'] = $GLOBALS['config']['routing_safis'][$safi_num]['name']; + $safi_class = $GLOBALS['config']['routing_safis'][$safi_num]['class']; + } else { + $safi_class = 'default'; + } + + $peer_afi_items = [ + [ 'event' => $afi_class, 'text' => $peer_afi['afi'] ], + [ 'event' => $safi_class, 'text' => $peer_afi['safi'] ], + ]; + $peer_afi_html = get_label_group($peer_afi_items); + //r($peer_afi_html); + $peer_afis_html[] = $peer_afi_html; } - // Entries have been returned. Print the table. - $list = array('device' => FALSE); - if ($vars['page'] !== 'device') { - $list['device'] = TRUE; + $string .= ' ' . PHP_EOL; + $string .= ' ' . PHP_EOL; + $string .= ' ' . PHP_EOL; + $string .= ' ' . PHP_EOL; + $string .= ' ' . PHP_EOL; + $string .= ' ' . PHP_EOL . ' ' . PHP_EOL; + } elseif ($list['graph']) { + // Empty row for correct view class table-striped-two + $string .= ' ' . PHP_EOL; } + } - // Print - echo $string; + $string .= ' ' . PHP_EOL; + $string .= '
' . $local_ip . '
' . $local_name . '
AS' . $peer['human_local_as'] . ''; + if (!safe_empty($peer['virtual_name'])) { + $vitual_type = isset($GLOBALS['config']['os'][$local_dev['os']]['snmp']['virtual_type']) ? nicecase($GLOBALS['config']['os'][$local_dev['os']]['snmp']['virtual_type']) : 'VRF'; + $string .= '
'.$vitual_type.': '.$peer['virtual_name'].''; } - - switch ($vars['graph']) { - case 'prefixes_ipv4unicast': - case 'prefixes_ipv4multicast': - case 'prefixes_ipv4vpn': - case 'prefixes_ipv6unicast': - case 'prefixes_ipv6multicast': - case 'macaccounting_bits': - case 'macaccounting_pkts': - case 'updates': - $table_class = 'table-striped-two'; - $list['graph'] = TRUE; - break; - default: - $table_class = 'table-striped'; - $list['graph'] = FALSE; - } - - $string = generate_box_open(); - - $string .= '' . PHP_EOL; - - $cols = array( - array(NULL, 'class="state-marker"'), - array(NULL, 'style="width: 1px;"'), - 'device' => array('Local address', 'style="width: 150px;"'), - 'local_as' => [ 'Local AS / VRF', 'style="width: 110px;"' ], - array(NULL, 'style="width: 20px;"'), - 'peer_ip' => array('Peer address', 'style="width: 150px;"'), - 'peer_as' => [ 'Remote AS', 'style="width: 90px;"' ], - 'type' => array('Type', 'style="width: 50px;"'), - array('Family', 'style="width: 50px;"'), - 'state' => 'State', - 'uptime' => 'Uptime / Updates', - ); - //if (!$list['device']) { unset($cols['device']); } - $string .= get_table_header($cols, $vars); - - $string .= ' ' . PHP_EOL; - - foreach ($entries['entries'] as $peer) - { - $local_dev = device_by_id_cache($peer['device_id']); - $local_as = ($list['device'] ? ' (AS' . $peer['human_local_as'] . ')' : ''); - $local_name = generate_device_link_short($local_dev, [ 'tab' => 'routing', 'proto' => 'bgp' ], 18); - $local_ip = generate_device_link($local_dev, $peer['human_localip'] . $local_as, array('tab' => 'routing', 'proto' => 'bgp')); - $peer_as = 'AS' . $peer['human_remote_as']; - if ($peer['peer_device_id']) { - $peer_dev = device_by_id_cache($peer['peer_device_id']); - $peer_name = generate_device_link_short($peer_dev, [ 'tab' => 'routing', 'proto' => 'bgp' ], 18); - } else { - $peer_name = $peer['reverse_dns']; - } - $peer_ip = generate_entity_link("bgp_peer", $peer, $peer['human_remoteip']); - $peer_afis = &$entries['afisafi'][$peer['device_id']][$peer['bgpPeer_id']]; - $peer_afis_html = array(); - - // Generate AFI/SAFI labels - foreach ($peer_afis as $peer_afi) - { -// $peer_afi_html = ''; - if (isset($GLOBALS['config']['routing_afis_name'][$peer_afi['afi']])) - { - $afi_num = $GLOBALS['config']['routing_afis_name'][$peer_afi['afi']]; - $afi_class = $GLOBALS['config']['routing_afis'][$afi_num]['class']; - } else { - $afi_class = 'default'; - } - - if (isset($GLOBALS['config']['routing_safis_name'][$peer_afi['safi']])) - { - // Named SAFI - $safi_num = $GLOBALS['config']['routing_safis_name'][$peer_afi['safi']]; - $safi_class = $GLOBALS['config']['routing_safis'][$safi_num]['class']; - } - else if (isset($GLOBALS['config']['routing_safis'][$peer_afi['safi']])) - { - // Numeric SAFI - $safi_num = $peer_afi['safi']; - $peer_afi['safi'] = $GLOBALS['config']['routing_safis'][$safi_num]['name']; - $safi_class = $GLOBALS['config']['routing_safis'][$safi_num]['class']; - } else { - $safi_class = 'default'; - } - -// $peer_afi_html .= '' . $peer_afi['afi'] . ''; -// $peer_afi_html .= '' . $peer_afi['safi'] . ''; -// $peer_afi_html .= ''; - - $peer_afi_items = [ - ['event' => $afi_class, 'text' => $peer_afi['afi']], - ['event' => $safi_class, 'text' => $peer_afi['safi']], - ]; - $peer_afi_html = get_label_group($peer_afi_items); - //r($peer_afi_html); - $peer_afis_html[] = $peer_afi_html; - } - - $string .= ' ' . PHP_EOL; - $string .= ' ' . PHP_EOL; - $string .= ' ' . PHP_EOL; - $string .= ' ' . PHP_EOL; - $string .= ' ' . PHP_EOL; - $string .= ' ' . PHP_EOL; - $string .= ' ' . PHP_EOL; - $string .= ' ' . PHP_EOL; - $string .= ' ' . PHP_EOL; - $string .= ' ' . PHP_EOL; - $string .= ' ' . PHP_EOL; - $string .= ' ' . PHP_EOL; + $string .= ' ' . PHP_EOL; + $string .= ' ' . PHP_EOL; + $string .= ' ' . PHP_EOL; + $string .= ' ' . PHP_EOL; + $string .= ' ' . PHP_EOL; + $string .= ' ' . PHP_EOL; + $string .= ' ' . PHP_EOL; - $string .= ' ' . PHP_EOL; + $string .= ' ' . PHP_EOL; - // Graphs - $peer_graph = FALSE; - switch ($vars['graph']) { - case 'prefixes_ipv4unicast': - case 'prefixes_ipv4multicast': - case 'prefixes_ipv4vpn': - case 'prefixes_ipv6unicast': - case 'prefixes_ipv6multicast': - $afisafi = preg_replace('/prefixes_(ipv[46])(\w+)/', '$1.$2', $vars['graph']); // prefixes_ipv6unicast ->> ipv6.unicast - if (isset($peer_afis[$afisafi]) && $peer['bgpPeer_id']) - { - $graph_array['type'] = 'bgp_' . $vars['graph']; - $graph_array['id'] = $peer['bgpPeer_id']; - $peer_graph = TRUE; - } - break; - case 'updates': - if ($peer['bgpPeer_id']) - { - $graph_array['type'] = 'bgp_updates'; - $graph_array['id'] = $peer['bgpPeer_id']; - $peer_graph = TRUE; - } - break; - case 'macaccounting_bits': - case 'macaccounting_pkts': - //FIXME. I really still not know it works or not? -- mike - // This part copy-pasted from old code as is - $acc = dbFetchRow("SELECT * FROM `mac_accounting` AS M + // Graphs + $peer_graph = FALSE; + switch ($vars['graph']) { + case 'prefixes_ipv4unicast': + case 'prefixes_ipv4multicast': + case 'prefixes_ipv4vpn': + case 'prefixes_ipv6unicast': + case 'prefixes_ipv6multicast': + $afisafi = preg_replace('/prefixes_(ipv[46])(\w+)/', '$1.$2', $vars['graph']); // prefixes_ipv6unicast ->> ipv6.unicast + if (isset($peer_afis[$afisafi]) && $peer['bgpPeer_id']) { + $graph_array['type'] = 'bgp_' . $vars['graph']; + $graph_array['id'] = $peer['bgpPeer_id']; + $peer_graph = TRUE; + } + break; + + case 'updates': + if ($peer['bgpPeer_id']) { + $graph_array['type'] = 'bgp_updates'; + $graph_array['id'] = $peer['bgpPeer_id']; + $peer_graph = TRUE; + } + break; + + case 'macaccounting_bits': + case 'macaccounting_pkts': + // FIXME. I really still not know it works or not? -- mike + // This part copy-pasted from old code as is + $acc = dbFetchRow("SELECT * FROM `mac_accounting` AS M LEFT JOIN `ip_mac` AS I ON M.mac = I.mac_address LEFT JOIN `ports` AS P ON P.port_id = M.port_id LEFT JOIN `devices` AS D ON D.device_id = P.device_id WHERE I.ip_address = ?", array($peer['bgpPeerRemoteAddr'])); - $database = get_rrd_path($device, "cip-" . $acc['ifIndex'] . "-" . $acc['mac'] . ".rrd"); - if (is_array($acc) && is_file($database)) - { - $peer_graph = TRUE; - $graph_array['id'] = $acc['ma_id']; - $graph_array['type'] = $vars['graph']; - } - break; - } - - if ($peer_graph) { - $graph_array['to'] = get_time(); - $string .= ' ' . PHP_EOL; - $string .= ' ' . PHP_EOL . ' ' . PHP_EOL; - } - elseif ($list['graph']) - { - // Empty row for correct view class table-striped-two - $string .= ' ' . PHP_EOL; + $database = get_rrd_path($device, "cip-" . $acc['ifIndex'] . "-" . $acc['mac'] . ".rrd"); + if (is_array($acc) && is_file($database)) { + $peer_graph = TRUE; + $graph_array['id'] = $acc['ma_id']; + $graph_array['type'] = $vars['graph']; } + break; } - $string .= ' ' . PHP_EOL; - $string .= '
' . $local_ip . '
' . $local_name . '
AS' . $peer['human_local_as'] . ''; - if (!safe_empty($peer['virtual_name'])) { - $vitual_type = isset($GLOBALS['config']['os'][$local_dev['os']]['snmp']['virtual_type']) ? nicecase($GLOBALS['config']['os'][$local_dev['os']]['snmp']['virtual_type']) : 'VRF'; - $string .= '
'.$vitual_type.': '.$peer['virtual_name'].''; - } - $string .= '
' . $peer_ip . '
' . $peer_name . '
' . $peer_as . '
' . $peer['astext'] . '
' . $peer['peer_type'] . '' . implode('
', $peer_afis_html) . '
' . $peer['bgpPeerAdminStatus'] . '
' . $peer['bgpPeerState'] . '
' . format_uptime($peer['bgpPeerFsmEstablishedTime']) . '
+ $string .= '
' . $peer_ip . '
' . $peer_name . '
' . $peer_as . '
' . $peer['astext'] . '
' . $peer['peer_type'] . '' . implode('
', $peer_afis_html) . '
' . $peer['bgpPeerAdminStatus'] . '
' . $peer['bgpPeerState'] . '
' . format_uptime($peer['bgpPeerFsmEstablishedTime']) . '
Updates: ' . format_si($peer['bgpPeerInUpdates']) . ' ' . format_si($peer['bgpPeerOutUpdates']) . '
' . PHP_EOL; - - $string .= generate_graph_row($graph_array); - - $string .= '
'; + if ($peer_graph) { + $graph_array['to'] = get_time(); + $string .= '
' . PHP_EOL; - $string .= generate_box_close(); + $string .= generate_graph_row($graph_array); - // Print pagination header - if ($entries['pagination_html']) - { - $string = $entries['pagination_html'] . $string . $entries['pagination_html']; + $string .= '
'; + + $string .= generate_box_close(); + + // Print pagination header + if ($entries['pagination_html']) { + $string = $entries['pagination_html'] . $string . $entries['pagination_html']; + } + + // Print + echo $string; } @@ -240,156 +227,172 @@ function print_bgp_peer_table($vars) { * pagination, pageno, pagesize * device, type, adminstatus, state */ -function get_bgp_array($vars) -{ - $array = []; +function get_bgp_array($vars) { + $array = []; - // With pagination? (display page numbers in header) - $array['pagination'] = (isset($vars['pagination']) && $vars['pagination']); - pagination($vars, 0, TRUE); // Get default pagesize/pageno - $array['pageno'] = $vars['pageno']; - $array['pagesize'] = $vars['pagesize']; - $start = $array['pagesize'] * $array['pageno'] - $array['pagesize']; - $pagesize = $array['pagesize']; + // With pagination? (display page numbers in header) + //$array['pagination'] = (isset($vars['pagination']) && $vars['pagination']); + $array['pagination'] = TRUE; + pagination($vars, 0, TRUE); // Get default pagesize/pageno + $array['pageno'] = $vars['pageno']; + $array['pagesize'] = $vars['pagesize']; + $start = $array['pagesize'] * $array['pageno'] - $array['pagesize']; + $pagesize = $array['pagesize']; - // Require cached IDs from html/includes/cache-data.inc.php - $cache_bgp = &$GLOBALS['cache']['bgp']; + // Require cached IDs from html/includes/cache-data.inc.php + $cache_bgp = &$GLOBALS['cache']['bgp']; - // Begin query generate - $param = array(); - $where = ' WHERE 1 '; - foreach ($vars as $var => $value) { - if ($value != '') { - switch ($var) { - case "group": - case "group_id": - $values = get_group_entities($value); - $where .= generate_query_values($values, 'bgpPeer_id'); - break; - case 'device': - case 'device_id': - $where .= generate_query_values($value, 'device_id'); - break; - case 'peer': - case 'peer_id': - $where .= generate_query_values($value, 'peer_device_id'); - break; - case 'local_ip': - $where .= generate_query_values(ip_uncompress($value), 'bgpPeerLocalAddr'); - break; - case 'peer_ip': - $where .= generate_query_values(ip_uncompress($value), 'bgpPeerRemoteAddr'); - break; - case 'local_as': - $where .= generate_query_values(bgp_asdot_to_asplain($value), 'local_as'); - break; - case 'peer_as': - $where .= generate_query_values(bgp_asdot_to_asplain($value), 'bgpPeerRemoteAs'); - break; - case 'type': - if ($value === 'external' || $value === 'ebgp') { - $where .= generate_query_values($cache_bgp['external'], 'bgpPeer_id'); - } elseif ($value === 'internal' || $value === 'ibgp') { - $where .= generate_query_values($cache_bgp['internal'], 'bgpPeer_id'); - } - break; - case 'adminstatus': - if ($value === 'stop') { - $where .= generate_query_values($cache_bgp['start'], 'bgpPeer_id', '!='); // NOT IN - } elseif ($value === 'start') { - $where .= generate_query_values($cache_bgp['start'], 'bgpPeer_id'); - } - break; - case 'state': - if ($value === 'down') { - $where .= generate_query_values($cache_bgp['up'], 'bgpPeer_id', '!='); // NOT IN - } elseif ($value === 'up') { - $where .= generate_query_values($cache_bgp['up'], 'bgpPeer_id'); - } - break; - } - } - } + // Begin query generate + $param = []; + $where = []; + foreach ($vars as $var => $value) { + if (!safe_empty($value)) { + switch ($var) { + case "group": + case "group_id": + $values = get_group_entities($value); + $where[] = generate_query_values_ng($values, 'bgpPeer_id'); + break; - // Show peers only for permitted devices - $query_permitted = generate_query_values($cache_bgp['permitted'], 'bgpPeer_id'); + case 'device': + case 'device_id': + $where[] = generate_query_values_ng($value, 'device_id'); + break; - $query = 'FROM `bgpPeers`'; - $query_count = 'SELECT COUNT(*) ' . $query . $where . $query_permitted; // Use only bgpPeer_id and device_id in query! + case 'peer': + case 'peer_id': + $where[] = generate_query_values_ng($value, 'peer_device_id'); + break; - $query .= ' JOIN `devices` USING (`device_id`) '; + case 'local_ip': + $where[] = generate_query_values_ng(ip_uncompress($value), 'bgpPeerLocalAddr'); + break; - //$query .= ' LEFT JOIN `bgpPeers-state` AS S ON `bgpPeer_id` = S.`bgpPeer_id`'; - //$query .= ' LEFT JOIN `devices` AS D ON `device_id` = D.`device_id`'; - $query .= $where . $query_permitted; + case 'peer_ip': + $where[] = generate_query_values_ng(ip_uncompress($value), 'bgpPeerRemoteAddr'); + break; - $query = 'SELECT `hostname`, `bgpLocalAs`, bgpPeers.* ' . $query; - - $sort_dir = $vars['sort_order'] === 'desc' ? ' DESC' : ''; + case 'local_as': + $where[] = generate_query_values_ng(bgp_asdot_to_asplain($value), 'local_as'); + break; - switch($vars['sort']) { - case "device": - $sort = " ORDER BY `hostname`".$sort_dir; - break; + case 'peer_as': + if (is_string($value) && preg_match_all('/AS(?[\d\.]+):/', $value, $matches)) { + //r($matches); + $value = $matches['as']; + } + $where[] = generate_query_values_ng(bgp_asdot_to_asplain($value), 'bgpPeerRemoteAs'); + break; - case "local_as": - $sort = " ORDER BY `local_as`$sort_dir, `virtual_name`$sort_dir"; - break; + case 'type': + if ($value === 'external' || $value === 'ebgp') { + $where[] = generate_query_values_ng($cache_bgp['external'], 'bgpPeer_id'); + } elseif ($value === 'internal' || $value === 'ibgp') { + $where[] = generate_query_values_ng($cache_bgp['internal'], 'bgpPeer_id'); + } + break; - case "peer_ip": - $sort = " ORDER BY `bgpPeerRemoteAddr`".$sort_dir; - break; - - case "peer_as": - $sort = " ORDER BY `bgpPeerRemoteAs`".$sort_dir; - break; + case 'adminstatus': + if ($value === 'stop') { + $where[] = generate_query_values_ng($cache_bgp['start'], 'bgpPeer_id', '!='); // NOT IN + } elseif ($value === 'start') { + $where[] = generate_query_values_ng($cache_bgp['start'], 'bgpPeer_id'); + } + break; case 'state': - $sort = " ORDER BY `bgpPeerAdminStatus`".$sort_dir.", `bgpPeerState`".$sort_dir; - break; - - case 'uptime': - $sort = " ORDER BY `bgpPeerFsmEstablishedTime`".$sort_dir; - break; - - default: - $sort = " ORDER BY `hostname`".$sort_dir.", `bgpPeerRemoteAs`".$sort_dir.", `bgpPeerRemoteAddr`".$sort_dir; + if ($value === 'down') { + $where[] = generate_query_values_ng($cache_bgp['up'], 'bgpPeer_id', '!='); // NOT IN + } elseif ($value === 'up') { + $where[] = generate_query_values_ng($cache_bgp['up'], 'bgpPeer_id'); + } + break; + } } + } - $query .= $sort; - $query .= " LIMIT $start,$pagesize"; + // Show peers only for permitted devices + if ($_SESSION['userlevel'] < 5) { + $where[] = generate_query_values_ng($cache_bgp['permitted'], 'bgpPeer_id'); + } elseif (!$GLOBALS['config']['web_show_disabled'] && $GLOBALS['cache']['devices']['stat']['disabled']) { + // Exclude disabled devices for Global Read+ + $where[] = generate_query_values_ng($GLOBALS['cache']['devices']['disabled'], 'device_id', '!='); + } - $peer_devices = array(); - // Query BGP - foreach (dbFetchRows($query, $param) as $entry) - { - humanize_bgp($entry); + if (!safe_empty($where)) { + $where = 'WHERE ' . implode(' AND ', $where); + } else { + $where = ''; + } + //r($where); - // Collect peer devices for AFI/SAFI - $peer_devices[$entry['device_id']] = 1; + $query_count = 'SELECT COUNT(*) FROM `bgpPeers` ' . $where; // Use only bgpPeer_id and device_id in query! + $array['count'] = dbFetchCell($query_count, $param); + //$array['count'] = dbFetchCell($query_count, $param, TRUE); - $array['entries'][] = $entry; + // Pagination + $array['pagination_html'] = pagination($vars, $array['count']); + + $query = 'SELECT `hostname`, `bgpLocalAs`, `bgpPeers`.*'; + $query .= ' FROM `bgpPeers`'; + $query .= ' JOIN `devices` USING (`device_id`) '; + $query .= $where; + + $sort_dir = $vars['sort_order'] === 'desc' ? ' DESC' : ''; + + switch($vars['sort']) { + case "device": + $sort = " ORDER BY `hostname`".$sort_dir; + break; + + case "local_as": + $sort = " ORDER BY `local_as`$sort_dir, `virtual_name`$sort_dir"; + break; + + case "peer_ip": + $sort = " ORDER BY `bgpPeerRemoteAddr`".$sort_dir; + break; + + case "peer_as": + $sort = " ORDER BY `bgpPeerRemoteAs`".$sort_dir; + break; + + case 'state': + $sort = " ORDER BY `bgpPeerAdminStatus`".$sort_dir.", `bgpPeerState`".$sort_dir; + break; + + case 'uptime': + $sort = " ORDER BY `bgpPeerFsmEstablishedTime`".$sort_dir; + break; + + default: + $sort = " ORDER BY `hostname`".$sort_dir.", `bgpPeerRemoteAs`".$sort_dir.", `bgpPeerRemoteAddr`".$sort_dir; + } + + $query .= $sort; + $query .= " LIMIT $start,$pagesize"; + + $peer_devices = []; + // Query BGP + foreach (dbFetchRows($query, $param) as $entry) { + humanize_bgp($entry); + + // Collect peer devices for AFI/SAFI + $peer_devices[$entry['device_id']] = 1; + + $array['entries'][] = $entry; + } + + // Query AFI/SAFI + if (!safe_empty($peer_devices)) { + $query_afi = 'SELECT * FROM `bgpPeers_cbgp` WHERE 1' . generate_query_values_and(array_keys($peer_devices), 'device_id'); + + foreach (dbFetchRows($query_afi) as $entry) { + $array['afisafi'][$entry['device_id']][$entry['bgpPeer_id']][$entry['afi'] . '.' . $entry['safi']] = [ 'afi' => $entry['afi'], 'safi' => $entry['safi'] ]; } + } - // Query AFI/SAFI - if (count($peer_devices)) - { - $query_afi = 'SELECT * FROM `bgpPeers_cbgp` WHERE 1' . generate_query_values(array_keys($peer_devices), 'device_id'); - foreach (dbFetchRows($query_afi) as $entry) - { - $array['afisafi'][$entry['device_id']][$entry['bgpPeer_id']][$entry['afi'] . '.' . $entry['safi']] = array('afi' => $entry['afi'], 'safi' => $entry['safi']); - } - } - - // Query BGP peers count - if ($array['pagination']) { - $array['count'] = dbFetchCell($query_count, $param); - $array['pagination_html'] = pagination($vars, $array['count']); - } else { - $array['count'] = safe_count($array['entries']); - } - - return $array; + return $array; } // EOF diff --git a/html/includes/print/rows.inc.php b/html/includes/print/rows.inc.php index 6e59ad1d..79a27dbf 100644 --- a/html/includes/print/rows.inc.php +++ b/html/includes/print/rows.inc.php @@ -6,7 +6,7 @@ * * @package observium * @subpackage web - * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2021 Observium Limited + * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2022 Observium Limited * */ @@ -26,39 +26,54 @@ function generate_box_open($args = []) { if (isset($args['title'])) { $return .= '
'.PHP_EOL; if (isset($args['url'])) { $return .= ''; } - if (isset($args['icon'])) { $return .= ''; } + if (isset($args['icon'])) { $return .= get_icon($args['icon']); } $return .= '<' . (isset($args['title-element']) ? $args['title-element'] : 'h3').' class="box-title"'; $return .= isset($args['title-style']) ? ' style="'.$args['title-style'].'"' : ''; $return .= '>'; - $return .= $args['title'].''.PHP_EOL; + $return .= escape_html($args['title']).''.PHP_EOL; if (isset($args['url'])) { $return .= ''; } if (isset($args['header-controls']) && is_array($args['header-controls']['controls'])) { $return .= '
'; foreach($args['header-controls']['controls'] as $control) { - if (isset($control['anchor']) && $control['anchor'] == TRUE) { + $anchor = (isset($control['anchor']) && $control['anchor']) || + (isset($control['config']) && !empty($control['config'])); + if ($anchor) { $return .= ' "ajax_settings('".$control['config']."', '".$control['value']."');" ]; + } } else { //$return .= ' onclick="return false;"'; } + // Additional class $return .= ' class="btn btn-box-tool'; - if (isset($control['class'])) { $return .= ' '.$control['class']; } + if (isset($control['class'])) { + $return .= ' ' . escape_html($control['class']); + } $return .= '"'; - if (isset($control['data'])) { $return .= ' '.$control['data']; } + // Additional params (raw string or array with params + if (isset($control['data'])) { + $params = is_array($control['data']) ? generate_html_attribs($control['data']) : $control['data']; + $return .= ' '.$params; + } $return .= '>'; - if (isset($control['icon'])) { $return .= ' '; } + if (isset($control['icon'])) { $return .= get_icon($control['icon']).' '; } if (isset($control['text'])) { $return .= $control['text']; } - if (isset($control['anchor']) && $control['anchor'] == TRUE) { + if ($anchor) { $return .= ''; } else { $return .= ''; @@ -455,7 +470,7 @@ SCRIPT; foreach ($content as $key => $value) { echo('
'.PHP_EOL); $item = [ - 'id' => "${htmlname}[key][]", + 'id' => "{$htmlname}[key][]", 'name' => 'Key', //'width' => '500px', 'class' => 'input-large', @@ -470,7 +485,7 @@ SCRIPT; } echo(generate_form_element($item)); $item = [ - 'id' => "${htmlname}[value][]", + 'id' => "{$htmlname}[value][]", 'name' => 'Value', //'width' => '500px', 'class' => 'input-xlarge', @@ -509,9 +524,9 @@ SCRIPT; // https://metallurgical.github.io/jquery-metal-clone/ register_html_resource('js', 'jquery.metalClone.js'); // jquery.metalClone.min.js register_html_resource('css', 'metalClone.css'); - $clone_target = "${htmlname}_clone_row"; - $clone_button = "${htmlname}[add]"; - $clone_remove = "${htmlname}[remove]"; + $clone_target = "{$htmlname}_clone_row"; + $clone_button = "{$htmlname}[add]"; + $clone_remove = "{$htmlname}[remove]"; $remove_text = ''; //'Remove'; if ($readonly || (bool)$locked) { $clone_disabled = 'disabled: \'1\','; @@ -531,26 +546,26 @@ SCRIPT; //console.log(element); var regex = /(metalElement\d{0,})/g; var eclass = element.attr('class'); - var others = \$('[id=${clone_target}]').not(element); + var others = \$('[id={$clone_target}]').not(element); $.each(others, function () { \$(this).addClass(eclass); // Add button icon - //console.log(\$(this).find('#${clone_button}')); - \$(this).find('[id=\"${clone_button}\"]').prepend(' ').prepend(\$('', { class: '${icon_add}' })); + //console.log(\$(this).find('#{$clone_button}')); + \$(this).find('[id=\"{$clone_button}\"]').prepend(' ').prepend(\$('', { class: '{$icon_add}' })); // Remove button \$(this).append(\$('