├── .gitignore ├── README.md ├── about.php ├── activetriggers.php ├── class_zabbix.php ├── config-core.php ├── config-demo.php ├── config.inc.php ├── cookies.php ├── css ├── bootstrap-responsive.css ├── bootstrap-responsive.min.css ├── bootstrap.css └── bootstrap.min.css ├── dot.htaccess ├── feedback.php ├── functions.php ├── graph.php ├── graph_img.php ├── host.php ├── hostgroups.php ├── hostitems.php ├── hosts.php ├── images └── hosts.png ├── img ├── glyphicons-halflings-white.png └── glyphicons-halflings.png ├── index.php ├── js ├── bootstrap.js └── bootstrap.min.js ├── logout.php ├── template ├── footer.php └── header.php ├── trigger_ack.php └── trigger_info.php /.gitignore: -------------------------------------------------------------------------------- 1 | # Don't track the config.php, that's custom for each user 2 | config.php 3 | 4 | # Just in case you have custom .htaccess files 5 | .htaccess 6 | 7 | # Don't track phpstorm configs 8 | .idea 9 | 10 | # The debuglog for JSON 11 | json.log 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Introduction: Mobile Zabbix 2 | 3 | This project is intended as an extra application, built on top of your current [Zabbix](http://www.zabbix.com) set-up. 4 | 5 | It connects to your Zabbix API URL, using your Zabbix credentials. Afterwards, all settings are pulled from your Zabbix. 6 | This means any change you make in your Zabbix, will be transported to the Mobile ZBX application. 7 | 8 | If you're worried about security implications, please read more on the FAQ at http://www.mozbx.net. 9 | 10 | For more install guides (on your phone, or on your own servers), please see http://www.mozbx.net/install.html. 11 | 12 | ### About the configuration file 13 | 14 | There's a file called "config-core.php" included. Copy that to "config.php" and modify it as you see fit. That file is unique to you, and won't change with future updates. 15 | 16 | ### Installation instructions 17 | 18 | If you have a working webserver with PHP support, it's as easy as cloning the repository and browsing to the URL. By default, no special configs are needed. 19 | You can edit the config.php (a copy of config-core.php) to substitute some variables that suite your own environment. 20 | 21 | ### About the author 22 | 23 | - Built by [Mattias Geniar](http://mattiasgeniar.be) (twitter: [@mattiasgeniar](https://twitter.com/#!/mattiasgeniar) ) 24 | - Submit your issues using [the Github issue tracker](https://github.com/mattiasgeniar/MoZBX/issues) 25 | -------------------------------------------------------------------------------- /about.php: -------------------------------------------------------------------------------- 1 | setUsername($zabbixUser); 13 | $zabbix->setPassword($zabbixPass); 14 | $zabbix->setZabbixApiUrl($zabbixApi); 15 | 16 | // Login 17 | if (isset($zabbixAuthHash) && strlen($zabbixAuthHash) > 0) { 18 | // Try it with the authentication hash we have 19 | $zabbix->setAuthToken($zabbixAuthHash); 20 | } elseif (strlen($zabbix->getUsername()) > 0 && strlen($zabbix->getPassword()) > 0 && strlen($zabbix->getZabbixApiUrl()) > 0) { 21 | // Or try it with our info from the cookies 22 | $zabbix->login(); 23 | } 24 | 25 | if (!$zabbix->isLoggedIn()) { 26 | header("Location: index.php"); 27 | exit(); 28 | } 29 | 30 | require_once("template/header.php"); 31 | 32 | if ($zabbix->isLoggedIn()) { 33 | // Retrieve the data in one go 34 | $zabbix_auth = $zabbix->getAuthToken(); 35 | $zabbix_version = $zabbix->getVersion(); 36 | } 37 | ?> 38 | 39 | 51 | 52 |
53 |

Mobile Zabbix

54 |

55 | Mobile Zabbix version
56 | In development by Mattias Geniar 57 |

58 | 59 |

Changelog: version 0.5

60 |

61 | Improved Zabbix 2.0 compatibility
62 | Introduced entirely new layout (based on Twitter Bootstrap)
63 | Fix #33: SSL tracker image
64 | Fix #32: Home button not working
65 | Fix #31: Windows CRLF
66 | Fix #30: API URL: detect trailing slashes
67 | Fix #29: Automatically determine Zabbix Version (1.8/2.0)
68 | Fix #28: Active Triggers empty on Zabbix 2.0 69 |

70 | 71 |

Changelog: version 0.4

72 |

73 | Changes for Zabbix 2.0 compatibility introduced
74 | Updated jqTouch, using Zepto instead of jqTouch
75 | Performance: default to not showing host-counts per hostgroup
76 |

77 | 78 |

Zabbix Server info

79 |

80 | Zabbix API version on the server.
81 | Retrieved data from .
82 | You are logged in as getUsername()?>.
83 | Your current session-id is getAuthToken()?>
84 |

85 |
86 | 89 | -------------------------------------------------------------------------------- /activetriggers.php: -------------------------------------------------------------------------------- 1 | setUsername($zabbixUser); 13 | $zabbix->setPassword($zabbixPass); 14 | $zabbix->setZabbixApiUrl($zabbixApi); 15 | 16 | // Login 17 | if (isset($zabbixAuthHash) && strlen($zabbixAuthHash) > 0) { 18 | // Try it with the authentication hash we have 19 | $zabbix->setAuthToken($zabbixAuthHash); 20 | } elseif (strlen($zabbix->getUsername()) > 0 && strlen($zabbix->getPassword()) > 0 && strlen($zabbix->getZabbixApiUrl()) > 0) { 21 | // Or try it with our info from the cookies 22 | $zabbix->login(); 23 | } 24 | 25 | if (!$zabbix->isLoggedIn()) { 26 | header("Location: index.php"); 27 | exit(); 28 | } 29 | 30 | require_once("template/header.php"); 31 | 32 | // Get all active triggers (for the counter on homepage); 33 | $triggersActive = $zabbix->getTriggersActive($arrSettings["minimalSeverity"]); 34 | if (!is_array($triggersActive)) 35 | $triggersActive = array(); 36 | ?> 37 | 49 | 50 |
51 | 0) { 53 | // First, group our active triggers per host 54 | $arrSortedTriggers = array(); 55 | 56 | foreach ($triggersActive as $triggerActive) { 57 | if (array_key_exists("hosts", $triggerActive) && array_key_exists(0, $triggerActive["hosts"])) { 58 | $arrSortedTriggers[$triggerActive["hosts"][0]->hostid]["host"] = $triggerActive["hosts"][0]->host; 59 | $arrSortedTriggers[$triggerActive["hosts"][0]->hostid]["triggers"][] = 60 | array( 61 | "description" => $triggerActive["description"], 62 | "comments" => $triggerActive["comments"], 63 | "lastchange" => $triggerActive["lastchange"], 64 | "priority" => $triggerActive["priority"], 65 | "triggerid" => $triggerActive["triggerid"], 66 | ); 67 | } 68 | } 69 | 70 | asort($arrSortedTriggers); 71 | ?> 72 | 96 | 99 |
100 |

101 | There don't seem to be any active triggers. 102 |

103 |
104 | 107 | 108 |
109 | 110 | -------------------------------------------------------------------------------- /class_zabbix.php: -------------------------------------------------------------------------------- 1 | true, 34 | /*CURLOPT_HEADER => true, */ 35 | CURLOPT_TIMEOUT => 30, 36 | CURLOPT_CONNECTTIMEOUT => 5, 37 | CURLOPT_SSL_VERIFYHOST => false, 38 | CURLOPT_SSL_VERIFYPEER => false, 39 | CURLOPT_FOLLOWLOCATION => true, 40 | CURLOPT_FRESH_CONNECT => true 41 | ); 42 | private $json_debug = null; 43 | private $curl_verbose = false; 44 | private $zabbix_tmp_cookies = ""; 45 | private $zabbix_url_graph = ""; 46 | private $zabbix_url_index = ""; 47 | private $http_auth = false; 48 | 49 | /* ########################################################## 50 | ## 51 | ## 52 | ## VARIABLES: generic 53 | ## 54 | ## 55 | ############################################################*/ 56 | 57 | private $auth_token = null; 58 | private $last_error_message = ""; 59 | private $last_error_data = ""; 60 | private $last_error_code = ""; 61 | 62 | /* ########################################################## 63 | ## 64 | ## 65 | ## Construct 66 | ## 67 | ## 68 | ############################################################*/ 69 | 70 | public function __construct($arrSettings) 71 | { 72 | $this->zabbix_url = $arrSettings["zabbixApiUrl"] . "api_jsonrpc.php"; 73 | $this->zabbix_hostname = $arrSettings["zabbixHostname"]; 74 | $this->zabbix_username = $arrSettings["zabbixUsername"]; 75 | $this->zabbix_password = $arrSettings["zabbixPassword"]; 76 | $this->zabbix_hostname = $arrSettings["zabbixHostname"]; 77 | $this->zabbix_tmp_cookies = $arrSettings["pathCookiesStorage"]; 78 | $this->zabbix_url_graph = $arrSettings["zabbixApiUrl"] . "chart2.php"; 79 | $this->zabbix_url_index = $arrSettings["zabbixApiUrl"] . "index.php"; 80 | $this->json_debug = $arrSettings["jsonDebug"]; 81 | $this->json_debug_path = $arrSettings["jsonDebug_path"]; 82 | $this->curl_verbose = $arrSettings["curlVerbose"]; 83 | $this->http_auth = $arrSettings["useHttpAuth"]; 84 | } 85 | 86 | 87 | /* ########################################################## 88 | ## 89 | ## 90 | ## METHODS: public interface for accessing data 91 | ## 92 | ## 93 | ############################################################*/ 94 | 95 | public function getAuthToken() 96 | { 97 | return $this->auth_token; 98 | } 99 | 100 | public function setAuthToken($data) 101 | { 102 | $this->auth_token = $data; 103 | } 104 | 105 | public function getZabbixApiUrl() 106 | { 107 | return $this->zabbix_url; 108 | } 109 | 110 | public function setZabbixApiUrl($data) 111 | { 112 | $this->zabbix_url = $data; 113 | 114 | /* Check if the zabbix_url even contains valid data or not */ 115 | if (strpos($this->zabbix_url, 'api_jsonrpc.php') === false) { 116 | /* The specific PHP page for the API is not in the zabbix_url, add it ourselves */ 117 | if ($this->zabbix_url[strlen($this->zabbix_url) - 1] == "/") 118 | $this->zabbix_url .= "api_jsonrpc.php"; 119 | else 120 | $this->zabbix_url .= "/api_jsonrpc.php"; 121 | } 122 | } 123 | 124 | public function getUsername() 125 | { 126 | return $this->zabbix_username; 127 | } 128 | 129 | public function setUsername($data) 130 | { 131 | $this->zabbix_username = $data; 132 | } 133 | 134 | public function getPassword() 135 | { 136 | return $this->zabbix_password; 137 | } 138 | 139 | public function setPassword($data) 140 | { 141 | $this->zabbix_password = $data; 142 | } 143 | 144 | public function setLastError($code, $message, $data) 145 | { 146 | $this->last_error_code = $code; 147 | $this->last_error_data = $data; 148 | $this->last_error_message = $message; 149 | } 150 | 151 | public function getLastError() 152 | { 153 | return array("code" => $this->last_error_code, 154 | "data" => $this->last_error_data, 155 | "message" => $this->last_error_message, 156 | ); 157 | } 158 | 159 | public function Login() 160 | { 161 | $result = $this->sendRequest("user.authenticate", array("user" => $this->getUsername(), "password" => $this->getPassword())); 162 | 163 | //$result = $this->decodeJson($json_login); 164 | if (isset($result->result)) 165 | $this->auth_token = $result->result; 166 | } 167 | 168 | public function isLoggedIn() 169 | { 170 | return (bool)$this->auth_token; 171 | } 172 | 173 | public function getVersion() 174 | { 175 | // Retrieve Zabbix Version 176 | if (!isset($zabbix_version) || strlen($zabbix_version) == 0) { 177 | $result = $this->sendRequest("apiinfo.version"); 178 | if (isset($result->result)) 179 | $this->zabbix_version = $result->result; 180 | else 181 | $this->zabbix_version = 'unknown'; 182 | } 183 | 184 | return $this->zabbix_version; 185 | } 186 | 187 | public function getHostgroups() 188 | { 189 | // Retrieve all hostgroups for which you have access 190 | $result = $this->sendRequest("hostgroup.get", array("output" => "extend")); 191 | //$result = $this->decodeJson($json_hostgroups); 192 | 193 | if (isset($result->result)) { 194 | $group_objects = $result->result; 195 | if (is_array($group_objects) && count($group_objects) > 0) { 196 | $arrGroups = array(); 197 | foreach ($group_objects as $object) { 198 | $arrGroups[] = array("groupid" => $object->groupid, 199 | "name" => $object->name, 200 | "internal" => $object->internal 201 | ); 202 | } 203 | 204 | return $arrGroups; 205 | } else { 206 | return false; 207 | } 208 | } else { 209 | return false; 210 | } 211 | } 212 | 213 | public function getHostgroupById($hostgroupid) 214 | { 215 | $result = $this->sendRequest("hostgroup.get", 216 | array("output" => "extend", 217 | "groupids" => array($hostgroupid) 218 | ) 219 | ); 220 | if (isset($result->result)) { 221 | $hostgroup_objects = $result->result; 222 | 223 | if (array_key_exists(0, $hostgroup_objects)) 224 | return $hostgroup_objects[0]; 225 | else 226 | return false; 227 | } else { 228 | return false; 229 | } 230 | } 231 | 232 | public function getHostsByGroupId($groupid) 233 | { 234 | $result = $this->sendRequest("host.get", 235 | array( 236 | "output" => "extend", 237 | "groupids" => array($groupid) 238 | ) 239 | ); 240 | 241 | if (isset($result->result)) { 242 | $host_objects = $result->result; 243 | 244 | if (is_array($host_objects) && count($host_objects) > 0) { 245 | $arrHosts = array(); 246 | foreach ($host_objects as $object) { 247 | $arrHosts[$object->hostid] = 248 | array( 249 | "hostid" => $object->hostid, 250 | "host" => $object->host, 251 | "dns" => @isset($object->dns) ? $object->dns : '', 252 | "ip" => @isset($object->ip) ? $object->ip : '', 253 | "useip" => @isset($object->useip) ? $object->useip : '', 254 | "status" => $object->status, /* Enabled or not */ 255 | "available" => $object->available, 256 | "disable_until" => $object->disable_until, 257 | "error" => $object->error, 258 | 259 | ); 260 | } 261 | return $arrHosts; 262 | } else { 263 | return false; 264 | } 265 | } else { 266 | return false; 267 | } 268 | } 269 | 270 | public function getHostById($hostid) 271 | { 272 | if (in_array(substr($this->getVersion(), 0, 3), array('2.2', '2.0', '1.4'))) { 273 | /* Zabbix 2.x compatible: API version 2.0 or 1.4 */ 274 | $result = $this->sendRequest("host.get", 275 | array( 276 | "output" => "extend", 277 | "hostids" => array($hostid), 278 | "selectInterfaces" => "extend", 279 | ) 280 | ); 281 | } else { 282 | /* Zabbix 1.8 compatible */ 283 | $result = $this->sendRequest("host.get", 284 | array( 285 | "output" => "extend", 286 | "hostids" => array($hostid), 287 | ) 288 | ); 289 | } 290 | 291 | if (isset($result->result)) { 292 | $host_object = $result->result; 293 | 294 | if (array_key_exists(0, $host_object)) 295 | return $host_object[0]; 296 | else 297 | return false; 298 | } 299 | } 300 | 301 | public function getTriggersByHostId($hostid) 302 | { 303 | $result = $this->sendRequest("trigger.get", 304 | array("hostids" => array($hostid), 305 | "output" => "extend", 306 | /*"only_true" => 1,*/ 307 | /*"monitored" => 1*/ 308 | ) 309 | ); 310 | 311 | if (isset($result->result)) { 312 | $trigger_objects = $result->result; 313 | 314 | if (is_array($trigger_objects) && count($trigger_objects) > 0) { 315 | $arrTriggers = array(); 316 | foreach ($trigger_objects as $object) { 317 | $arrTriggers[$object->triggerid] = $this->convertTriggerJson($object); 318 | } 319 | return $arrTriggers; 320 | } else { 321 | return false; 322 | } 323 | } 324 | } 325 | 326 | public function getTriggersActive($minimalSeverity) 327 | { 328 | if (in_array(substr($this->getVersion(), 0, 3), array('2.2', '2.0', '1.4'))) { 329 | /* API version 1.4 = zabbix 2.0+ */ 330 | $selectHosts = "selectHosts"; 331 | } else { 332 | $selectHosts = "select_hosts"; 333 | } 334 | 335 | $result = $this->sendRequest("trigger.get", 336 | array( 337 | "monitored" => 1, /* Checks trigger, item and host status (all need to be active/enabled) */ 338 | "output" => "extend", 339 | $selectHosts => "extend", 340 | "min_severity" => $minimalSeverity, 341 | "filter" => array( 342 | "value" => 1, /* Filter by trigger state: 1 = problem */ 343 | ), 344 | "expandDescription" => 1, /* Expand macros in the description. Triggers with macros don't get listed at all, if not enabled. */ 345 | "withLastEventUnacknowledged" => 1, /* Only the unacknowledged triggers */ 346 | ) 347 | ); 348 | 349 | if (isset($result->result)) { 350 | $trigger_objects = $result->result; 351 | 352 | if (is_array($trigger_objects) && count($trigger_objects) > 0) { 353 | $arrTriggers = array(); 354 | foreach ($trigger_objects as $object) { 355 | $arrTriggers[$object->triggerid] = $this->convertTriggerJson($object); 356 | } 357 | return $arrTriggers; 358 | } else { 359 | return false; 360 | } 361 | } 362 | } 363 | 364 | public function getTriggerByTriggerAndHostId($triggerid, $hostid) 365 | { 366 | $result = $this->sendRequest("trigger.get", 367 | array("output" => "extend", 368 | "triggerids" => array($triggerid), 369 | "hostids" => array($hostid), 370 | "expandDescription" => 1, /* expand macros in the description */ 371 | ) 372 | ); 373 | 374 | if (isset($result->result)) { 375 | $trigger_objects = $result->result; 376 | 377 | if (is_array($trigger_objects) && count($trigger_objects) == 1) { 378 | return $this->convertTriggerJson($trigger_objects[0]); 379 | } else { 380 | return false; 381 | } 382 | } 383 | } 384 | 385 | private function convertTriggerJson($object) 386 | { 387 | $arrTrigger = array("triggerid" => $object->triggerid, 388 | "expression" => $object->expression, 389 | "description" => $object->description, 390 | "url" => $object->url, 391 | "status" => $object->status, 392 | "value" => $object->value, 393 | "priority" => $object->priority, 394 | "lastchange" => $object->lastchange, 395 | "dep_level" => @isset($object->dep_level) ? $object->dep_level : '', 396 | "comments" => $object->comments, 397 | "error" => $object->error, 398 | "templateid" => $object->templateid, 399 | "type" => $object->type, 400 | ); 401 | if (isset($object->hosts)) 402 | $arrTrigger["hosts"] = $object->hosts; 403 | 404 | return $arrTrigger; 405 | } 406 | 407 | public function getEventsByTriggerAndHostId($triggerid, $hostid) 408 | { 409 | $result = $this->sendRequest("event.get", 410 | array("triggerids" => $triggerid, 411 | "hostids" => $hostid, 412 | "limit" => 10, 413 | "output" => "extend", 414 | "sortfield" => "clock", 415 | "sortorder" => "DESC") 416 | ); 417 | 418 | if (isset($result->result)) { 419 | $event_objects = $result->result; 420 | 421 | if (is_array($event_objects) && count($event_objects) > 0) { 422 | $arrEvents = array(); 423 | foreach ($event_objects as $event) { 424 | $arrEvents[] = $event; 425 | } 426 | return $arrEvents; 427 | } else { 428 | return false; 429 | } 430 | } 431 | } 432 | 433 | public function acknowledgeEvent($eventid, $comment) 434 | { 435 | $result = $this->sendRequest("event.acknowledge", 436 | array("eventids" => $eventid, 437 | "message" => $comment) 438 | ); 439 | return true; 440 | } 441 | 442 | 443 | public function getGraphsByHostId($hostid) 444 | { 445 | $result = $this->sendRequest("graph.get", 446 | array("hostids" => array($hostid), 447 | "output" => "extend", 448 | /*"only_true" => 1,*/ 449 | /*"monitored" => 1*/ 450 | ) 451 | ); 452 | if (isset($result->result)) { 453 | $graph_objects = $result->result; 454 | 455 | if (is_array($graph_objects) && count($graph_objects) > 0) { 456 | $arrGraphs = array(); 457 | foreach ($graph_objects as $object) { 458 | $arrGraphs[$object->graphid] = 459 | array("graphid" => $object->graphid, 460 | "name" => $object->name, 461 | "width" => $object->width, 462 | "height" => $object->height, 463 | "yaxismin" => $object->yaxismin, 464 | "yaxismax" => $object->yaxismax, 465 | "graphtype" => $object->graphtype, 466 | "show_legend" => $object->show_legend, 467 | "show_3d" => $object->show_3d, 468 | ); 469 | } 470 | return $arrGraphs; 471 | } else { 472 | return false; 473 | } 474 | } else { 475 | return false; 476 | } 477 | } 478 | 479 | public function getGraphById($graphid) 480 | { 481 | $result = $this->sendRequest("graph.get", 482 | array("output" => "extend", 483 | "graphids" => array($graphid) 484 | ) 485 | ); 486 | 487 | if (isset($result->result)) { 488 | $graph_object = $result->result; 489 | 490 | if (array_key_exists(0, $graph_object)) 491 | return $graph_object[0]; 492 | else 493 | return false; 494 | } else { 495 | return false; 496 | } 497 | 498 | } 499 | 500 | public function getGraphImageById($graphid, $period = 3600) 501 | { 502 | global $arrSettings; 503 | 504 | // Cookiename 505 | $filename_cookie = $this->zabbix_tmp_cookies . "zabbix_cookie_" . $graphid . ".txt"; 506 | 507 | $ch = curl_init(); 508 | // Add the URL of Zabbix to perform the login to 509 | curl_setopt($ch, CURLOPT_URL, $this->zabbix_url_index); 510 | // Get the value returned from our curl-call, don't default to stdout 511 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 512 | // Send a POST request 513 | curl_setopt($ch, CURLOPT_POST, true); 514 | // Increase verbosity for debugging 515 | curl_setopt($ch, CURLOPT_VERBOSE, $this->curl_verbose); 516 | // Don't validate SSL certs as must Zabbix installs that have an SSL connection are self-signed 517 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); 518 | // Lighttpd expects this header 519 | curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:')); 520 | 521 | if (in_array(substr($this->getVersion(), 0, 3), array('2.2', '2.0', '1.4'))) { 522 | /* API Version 1.4 = Zabbix 2.0+ */ 523 | $post_data = array( 524 | 'name' => $this->getUsername(), 525 | 'password' => $this->getPassword(), 526 | 'autologin' => 1, 527 | 'request' => '', /* Why is this empty? Zabbix requires it? */ 528 | 'enter' => 'Sign in', /* Zabbix also checks the value of this string ... */ 529 | ); 530 | } else { 531 | $post_data = array( 532 | 'name' => $this->getUsername(), 533 | 'password' => $this->getPassword(), 534 | 'enter' => 'Enter', 535 | ); 536 | } 537 | // Add the POST-data 538 | curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data); 539 | curl_setopt($ch, CURLOPT_COOKIEJAR, $filename_cookie); 540 | curl_setopt($ch, CURLOPT_COOKIEFILE, $filename_cookie); 541 | 542 | if ($this->http_auth) { 543 | curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); 544 | curl_setopt($ch, CURLOPT_USERPWD, $this->zabbix_username . ':' . $this->zabbix_password); 545 | } 546 | 547 | // Login 548 | curl_exec($ch); 549 | 550 | // To debug this call, comment out the Header-set in graph_img.php on line 32 551 | // that way, you'll just return plain text data and no image 552 | //curl_close($ch); 553 | 554 | // Fetch image 555 | // &period= the time, in seconds, of the graph (so: value of 7200 = a 2 hour graph to be shown) 556 | // &stime= the time, in PHP's time() format, from when the graph should begin 557 | // &width= the width of the graph, small enough to fit on mobile devices 558 | curl_setopt($ch, CURLOPT_URL, $this->zabbix_url_graph . "?graphid=" . $graphid . "&width=450&period=" . $period); 559 | $output = curl_exec($ch); 560 | 561 | // Close session 562 | curl_close($ch); 563 | 564 | // Delete our cookie 565 | unlink($filename_cookie); 566 | 567 | // Return the image 568 | return $output; 569 | } 570 | 571 | 572 | /* ########################################################## 573 | ## 574 | ## 575 | ## "Data conversion", make Zabbix Output readable 576 | ## 577 | ## 578 | ############################################################*/ 579 | 580 | public function getTriggerSeverity($priority) 581 | { 582 | switch ($priority) { 583 | case 5: 584 | return "Disaster"; 585 | break; 586 | case 4: 587 | return "High"; 588 | break; 589 | case 3: 590 | return "Average"; 591 | break; 592 | case 2: 593 | return "Warning"; 594 | break; 595 | case 1: 596 | return "Information"; 597 | break; 598 | default: 599 | return "Not classified"; 600 | } 601 | } 602 | 603 | public function getAvailability($available) 604 | { 605 | switch ($available) { 606 | case 2: 607 | return "Zabbix Agent offline"; 608 | break; 609 | case 1: 610 | return "Zabbix Agent online"; 611 | break; 612 | case 0: 613 | return "Zabbix Agent not needed"; 614 | break; 615 | default: 616 | return "unknown"; 617 | break; 618 | } 619 | } 620 | 621 | public function shortTriggerDisplay($arrSeverity) 622 | { 623 | $arrOutput = array(); 624 | asort($arrSeverity); 625 | 626 | foreach ($arrSeverity as $priority => $count) { 627 | if ($count > 0) { 628 | // This is worth mentioning 629 | $arrOutput[] = "" . $count . " " . $this->getTriggerSeverity($priority); 630 | } 631 | } 632 | 633 | if (count($arrOutput) > 0) 634 | return ": " . implode(", ", $arrOutput); 635 | else 636 | return false; 637 | } 638 | 639 | public function sortHostgroupsByName($arrHostgroups) 640 | { 641 | if (is_array($arrHostgroups)) 642 | uasort($arrHostgroups, "arrSortFunctionHostgroupsName"); 643 | 644 | return $arrHostgroups; 645 | } 646 | 647 | public function sortHostsByName($arrHosts) 648 | { 649 | if (is_array($arrHosts)) 650 | uasort($arrHosts, "arrSortFunctionHostsName"); 651 | return $arrHosts; 652 | } 653 | 654 | public function sortGraphsByName($arrGraphs) 655 | { 656 | if (is_array($arrGraphs)) 657 | uasort($arrGraphs, "arrSortFunctionHostgroupsName"); 658 | return $arrGraphs; 659 | } 660 | 661 | public function filterActiveHosts($arrHosts) 662 | { 663 | // Input: array of hosts 664 | // Output: only the active hosts 665 | $arrActiveHosts = array(); 666 | 667 | if (is_array($arrHosts) && count($arrHosts) > 0) { 668 | foreach ($arrHosts as $host) { 669 | if (array_key_exists("status", $host) && $host["status"] == 0) 670 | $arrActiveHosts[] = $host; 671 | } 672 | 673 | return $arrActiveHosts; 674 | } else 675 | return false; 676 | } 677 | 678 | 679 | /* ########################################################## 680 | ## 681 | ## 682 | ## The "core" functions 683 | ## - Send JSON requests to the API 684 | ## - Retrieving & Parsing JSON requests 685 | ## 686 | ## 687 | ############################################################*/ 688 | 689 | public function sendRequest($action, $parameters = '') 690 | { 691 | $curl_init = curl_init($this->zabbix_url); 692 | 693 | // Get our "config" variables 694 | $curl_opts = $this->zabbix_curl_options; 695 | $json_headers = $this->zabbix_json_headers; 696 | 697 | // Build our encoded JSON 698 | $json_data = $this->genericJSONPost($action, $parameters); 699 | 700 | if ($this->http_auth) { 701 | $curl_opts[CURLOPT_HTTPAUTH] = CURLAUTH_BASIC; 702 | $curl_opts[CURLOPT_USERPWD] = $this->zabbix_username . ':' . $this->zabbix_password; 703 | } 704 | 705 | $curl_opts[CURLOPT_VERBOSE] = $this->curl_verbose; 706 | $curl_opts[CURLOPT_HTTPHEADER] = $json_headers; 707 | $curl_opts[CURLOPT_CUSTOMREQUEST] = "POST"; 708 | $curl_opts[CURLOPT_POSTFIELDS] = is_array($json_data) ? http_build_query($json_data) : $json_data; 709 | 710 | curl_setopt_array($curl_init, $curl_opts); 711 | $ret = curl_exec($curl_init); 712 | $http_status = curl_getinfo($curl_init, CURLINFO_HTTP_CODE); 713 | curl_close($curl_init); 714 | 715 | if ($this->json_debug) { 716 | // output to the screen 717 | /*echo "

Json Answer

"; 718 | echo "
";
719 |                    echo var_dump($ret, true);
720 |                    echo "
";*/ 721 | 722 | // log it 723 | $handle = fopen($this->json_debug_path . "json.log", "a"); 724 | fwrite($handle, "\n======= " . date("Y/m/d, H:i:s") . " =======\n"); 725 | fwrite($handle, "Source IP: " . getVisitorIP() . "\n"); 726 | fwrite($handle, "API URL: " . $this->zabbix_url . "\n"); 727 | fwrite($handle, "Action: " . $action . "\n"); 728 | fwrite($handle, "Request: \n"); 729 | fwrite($handle, var_export($json_data, true)); 730 | fwrite($handle, "\n\n"); 731 | fwrite($handle, "Response: \n"); 732 | fwrite($handle, var_export($ret, true)); 733 | fwrite($handle, "\n=======\n"); 734 | fclose($handle); 735 | } 736 | 737 | // Make the output "readable" 738 | $result = $this->decodeJson($ret); 739 | 740 | if (isset($result->error)) { 741 | $this->setLastError($result->error->code, $result->error->message, $result->error->data); 742 | return false; 743 | } else if ($http_status != 200) { 744 | switch ($http_status) { 745 | case 401: 746 | $this->setLastError($http_status, 'Unable to authenticate with server.', 'Unable to authenticate with server.'); 747 | break; 748 | default: 749 | $this->setLastError($http_status, 'Unhandled error code: '.$http_status, 'Unhandled error code: '.$http_status); 750 | } 751 | } else { 752 | return $result; 753 | } 754 | } 755 | 756 | private function genericJSONPost($action, $parameters = '') 757 | { 758 | $json_request = array( 759 | 'auth' => $this->auth_token, 760 | 'method' => $action, 761 | 'id' => 1, 762 | 'params' => is_array($parameters) ? $parameters : array(), 763 | 'jsonrpc' => '2.0' 764 | ); 765 | 766 | return json_encode($json_request); 767 | } 768 | 769 | private function decodeJson($json) 770 | { 771 | $decoded = json_decode($json); 772 | return $decoded; 773 | } 774 | } 775 | 776 | ?> 777 | -------------------------------------------------------------------------------- /config-core.php: -------------------------------------------------------------------------------- 1 | = 83 | 84 | /* Google Analytics */ 85 | $arrSettings["googleAnalytics"] = ""; 98 | ?> 99 | -------------------------------------------------------------------------------- /config-demo.php: -------------------------------------------------------------------------------- 1 | = 76 | 77 | /* Styling for the textfields? */ 78 | //$arrSettings["cssStyleTextfield"] = "border: 1px solid gray; padding: 3px 0px 3px 0px; color: white; "; // For JQT theme 79 | $arrSettings["cssStyleTextfield"] = "border: 1px solid gray; padding: 3px 0px 3px 0px; color: black; "; // For APPLE theme 80 | 81 | /* Styling for the (submit) buttons? */ 82 | $arrSettings["cssStyleButton"] = "border-width: 0 12px; display: block; padding 10px; text-align: center; font-size: 20px;"; 83 | 84 | /* What theme should we use for the app? Options: jqt || apple */ 85 | $arrSettings["appTheme"] = "apple"; 86 | 87 | /* What color should our statusbar be? This should match our theme (jqt = black, apple = white) */ 88 | $arrSettings["appStatusbarColor"] = "white"; 89 | 90 | /* Google Analytics */ 91 | $arrSettings["googleAnalytics"] =""; 104 | ?> -------------------------------------------------------------------------------- /config.inc.php: -------------------------------------------------------------------------------- 1 | 20 | -------------------------------------------------------------------------------- /cookies.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /css/bootstrap-responsive.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Responsive v2.0.4 3 | * 4 | * Copyright 2012 Twitter, Inc 5 | * Licensed under the Apache License v2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Designed and built with all the love in the world @twitter by @mdo and @fat. 9 | */ 10 | 11 | .clearfix { 12 | *zoom: 1; 13 | } 14 | 15 | .clearfix:before, 16 | .clearfix:after { 17 | display: table; 18 | content: ""; 19 | } 20 | 21 | .clearfix:after { 22 | clear: both; 23 | } 24 | 25 | .hide-text { 26 | font: 0/0 a; 27 | color: transparent; 28 | text-shadow: none; 29 | background-color: transparent; 30 | border: 0; 31 | } 32 | 33 | .input-block-level { 34 | display: block; 35 | width: 100%; 36 | min-height: 28px; 37 | -webkit-box-sizing: border-box; 38 | -moz-box-sizing: border-box; 39 | -ms-box-sizing: border-box; 40 | box-sizing: border-box; 41 | } 42 | 43 | .hidden { 44 | display: none; 45 | visibility: hidden; 46 | } 47 | 48 | .visible-phone { 49 | display: none !important; 50 | } 51 | 52 | .visible-tablet { 53 | display: none !important; 54 | } 55 | 56 | .hidden-desktop { 57 | display: none !important; 58 | } 59 | 60 | @media (max-width: 767px) { 61 | .visible-phone { 62 | display: inherit !important; 63 | } 64 | .hidden-phone { 65 | display: none !important; 66 | } 67 | .hidden-desktop { 68 | display: inherit !important; 69 | } 70 | .visible-desktop { 71 | display: none !important; 72 | } 73 | } 74 | 75 | @media (min-width: 768px) and (max-width: 979px) { 76 | .visible-tablet { 77 | display: inherit !important; 78 | } 79 | .hidden-tablet { 80 | display: none !important; 81 | } 82 | .hidden-desktop { 83 | display: inherit !important; 84 | } 85 | .visible-desktop { 86 | display: none !important ; 87 | } 88 | } 89 | 90 | @media (max-width: 480px) { 91 | .nav-collapse { 92 | -webkit-transform: translate3d(0, 0, 0); 93 | } 94 | .page-header h1 small { 95 | display: block; 96 | line-height: 18px; 97 | } 98 | input[type="checkbox"], 99 | input[type="radio"] { 100 | border: 1px solid #ccc; 101 | } 102 | .form-horizontal .control-group > label { 103 | float: none; 104 | width: auto; 105 | padding-top: 0; 106 | text-align: left; 107 | } 108 | .form-horizontal .controls { 109 | margin-left: 0; 110 | } 111 | .form-horizontal .control-list { 112 | padding-top: 0; 113 | } 114 | .form-horizontal .form-actions { 115 | padding-right: 10px; 116 | padding-left: 10px; 117 | } 118 | .modal { 119 | position: absolute; 120 | top: 10px; 121 | right: 10px; 122 | left: 10px; 123 | width: auto; 124 | margin: 0; 125 | } 126 | .modal.fade.in { 127 | top: auto; 128 | } 129 | .modal-header .close { 130 | padding: 10px; 131 | margin: -10px; 132 | } 133 | .carousel-caption { 134 | position: static; 135 | } 136 | } 137 | 138 | @media (max-width: 767px) { 139 | body { 140 | padding-right: 20px; 141 | padding-left: 20px; 142 | } 143 | .navbar-fixed-top, 144 | .navbar-fixed-bottom { 145 | margin-right: -20px; 146 | margin-left: -20px; 147 | } 148 | .container-fluid { 149 | padding: 0; 150 | } 151 | .dl-horizontal dt { 152 | float: none; 153 | width: auto; 154 | clear: none; 155 | text-align: left; 156 | } 157 | .dl-horizontal dd { 158 | margin-left: 0; 159 | } 160 | .container { 161 | width: auto; 162 | } 163 | .row-fluid { 164 | width: 100%; 165 | } 166 | .row, 167 | .thumbnails { 168 | margin-left: 0; 169 | } 170 | [class*="span"], 171 | .row-fluid [class*="span"] { 172 | display: block; 173 | float: none; 174 | width: auto; 175 | margin-left: 0; 176 | } 177 | .input-large, 178 | .input-xlarge, 179 | .input-xxlarge, 180 | input[class*="span"], 181 | select[class*="span"], 182 | textarea[class*="span"], 183 | .uneditable-input { 184 | display: block; 185 | width: 100%; 186 | min-height: 28px; 187 | -webkit-box-sizing: border-box; 188 | -moz-box-sizing: border-box; 189 | -ms-box-sizing: border-box; 190 | box-sizing: border-box; 191 | } 192 | .input-prepend input, 193 | .input-append input, 194 | .input-prepend input[class*="span"], 195 | .input-append input[class*="span"] { 196 | display: inline-block; 197 | width: auto; 198 | } 199 | } 200 | 201 | @media (min-width: 768px) and (max-width: 979px) { 202 | .row { 203 | margin-left: -20px; 204 | *zoom: 1; 205 | } 206 | .row:before, 207 | .row:after { 208 | display: table; 209 | content: ""; 210 | } 211 | .row:after { 212 | clear: both; 213 | } 214 | [class*="span"] { 215 | float: left; 216 | margin-left: 20px; 217 | } 218 | .container, 219 | .navbar-fixed-top .container, 220 | .navbar-fixed-bottom .container { 221 | width: 724px; 222 | } 223 | .span12 { 224 | width: 724px; 225 | } 226 | .span11 { 227 | width: 662px; 228 | } 229 | .span10 { 230 | width: 600px; 231 | } 232 | .span9 { 233 | width: 538px; 234 | } 235 | .span8 { 236 | width: 476px; 237 | } 238 | .span7 { 239 | width: 414px; 240 | } 241 | .span6 { 242 | width: 352px; 243 | } 244 | .span5 { 245 | width: 290px; 246 | } 247 | .span4 { 248 | width: 228px; 249 | } 250 | .span3 { 251 | width: 166px; 252 | } 253 | .span2 { 254 | width: 104px; 255 | } 256 | .span1 { 257 | width: 42px; 258 | } 259 | .offset12 { 260 | margin-left: 764px; 261 | } 262 | .offset11 { 263 | margin-left: 702px; 264 | } 265 | .offset10 { 266 | margin-left: 640px; 267 | } 268 | .offset9 { 269 | margin-left: 578px; 270 | } 271 | .offset8 { 272 | margin-left: 516px; 273 | } 274 | .offset7 { 275 | margin-left: 454px; 276 | } 277 | .offset6 { 278 | margin-left: 392px; 279 | } 280 | .offset5 { 281 | margin-left: 330px; 282 | } 283 | .offset4 { 284 | margin-left: 268px; 285 | } 286 | .offset3 { 287 | margin-left: 206px; 288 | } 289 | .offset2 { 290 | margin-left: 144px; 291 | } 292 | .offset1 { 293 | margin-left: 82px; 294 | } 295 | .row-fluid { 296 | width: 100%; 297 | *zoom: 1; 298 | } 299 | .row-fluid:before, 300 | .row-fluid:after { 301 | display: table; 302 | content: ""; 303 | } 304 | .row-fluid:after { 305 | clear: both; 306 | } 307 | .row-fluid [class*="span"] { 308 | display: block; 309 | float: left; 310 | width: 100%; 311 | min-height: 28px; 312 | margin-left: 2.762430939%; 313 | *margin-left: 2.709239449638298%; 314 | -webkit-box-sizing: border-box; 315 | -moz-box-sizing: border-box; 316 | -ms-box-sizing: border-box; 317 | box-sizing: border-box; 318 | } 319 | .row-fluid [class*="span"]:first-child { 320 | margin-left: 0; 321 | } 322 | .row-fluid .span12 { 323 | width: 99.999999993%; 324 | *width: 99.9468085036383%; 325 | } 326 | .row-fluid .span11 { 327 | width: 91.436464082%; 328 | *width: 91.38327259263829%; 329 | } 330 | .row-fluid .span10 { 331 | width: 82.87292817100001%; 332 | *width: 82.8197366816383%; 333 | } 334 | .row-fluid .span9 { 335 | width: 74.30939226%; 336 | *width: 74.25620077063829%; 337 | } 338 | .row-fluid .span8 { 339 | width: 65.74585634900001%; 340 | *width: 65.6926648596383%; 341 | } 342 | .row-fluid .span7 { 343 | width: 57.182320438000005%; 344 | *width: 57.129128948638304%; 345 | } 346 | .row-fluid .span6 { 347 | width: 48.618784527%; 348 | *width: 48.5655930376383%; 349 | } 350 | .row-fluid .span5 { 351 | width: 40.055248616%; 352 | *width: 40.0020571266383%; 353 | } 354 | .row-fluid .span4 { 355 | width: 31.491712705%; 356 | *width: 31.4385212156383%; 357 | } 358 | .row-fluid .span3 { 359 | width: 22.928176794%; 360 | *width: 22.874985304638297%; 361 | } 362 | .row-fluid .span2 { 363 | width: 14.364640883%; 364 | *width: 14.311449393638298%; 365 | } 366 | .row-fluid .span1 { 367 | width: 5.801104972%; 368 | *width: 5.747913482638298%; 369 | } 370 | input, 371 | textarea, 372 | .uneditable-input { 373 | margin-left: 0; 374 | } 375 | input.span12, 376 | textarea.span12, 377 | .uneditable-input.span12 { 378 | width: 714px; 379 | } 380 | input.span11, 381 | textarea.span11, 382 | .uneditable-input.span11 { 383 | width: 652px; 384 | } 385 | input.span10, 386 | textarea.span10, 387 | .uneditable-input.span10 { 388 | width: 590px; 389 | } 390 | input.span9, 391 | textarea.span9, 392 | .uneditable-input.span9 { 393 | width: 528px; 394 | } 395 | input.span8, 396 | textarea.span8, 397 | .uneditable-input.span8 { 398 | width: 466px; 399 | } 400 | input.span7, 401 | textarea.span7, 402 | .uneditable-input.span7 { 403 | width: 404px; 404 | } 405 | input.span6, 406 | textarea.span6, 407 | .uneditable-input.span6 { 408 | width: 342px; 409 | } 410 | input.span5, 411 | textarea.span5, 412 | .uneditable-input.span5 { 413 | width: 280px; 414 | } 415 | input.span4, 416 | textarea.span4, 417 | .uneditable-input.span4 { 418 | width: 218px; 419 | } 420 | input.span3, 421 | textarea.span3, 422 | .uneditable-input.span3 { 423 | width: 156px; 424 | } 425 | input.span2, 426 | textarea.span2, 427 | .uneditable-input.span2 { 428 | width: 94px; 429 | } 430 | input.span1, 431 | textarea.span1, 432 | .uneditable-input.span1 { 433 | width: 32px; 434 | } 435 | } 436 | 437 | @media (min-width: 1200px) { 438 | .row { 439 | margin-left: -30px; 440 | *zoom: 1; 441 | } 442 | .row:before, 443 | .row:after { 444 | display: table; 445 | content: ""; 446 | } 447 | .row:after { 448 | clear: both; 449 | } 450 | [class*="span"] { 451 | float: left; 452 | margin-left: 30px; 453 | } 454 | .container, 455 | .navbar-fixed-top .container, 456 | .navbar-fixed-bottom .container { 457 | width: 1170px; 458 | } 459 | .span12 { 460 | width: 1170px; 461 | } 462 | .span11 { 463 | width: 1070px; 464 | } 465 | .span10 { 466 | width: 970px; 467 | } 468 | .span9 { 469 | width: 870px; 470 | } 471 | .span8 { 472 | width: 770px; 473 | } 474 | .span7 { 475 | width: 670px; 476 | } 477 | .span6 { 478 | width: 570px; 479 | } 480 | .span5 { 481 | width: 470px; 482 | } 483 | .span4 { 484 | width: 370px; 485 | } 486 | .span3 { 487 | width: 270px; 488 | } 489 | .span2 { 490 | width: 170px; 491 | } 492 | .span1 { 493 | width: 70px; 494 | } 495 | .offset12 { 496 | margin-left: 1230px; 497 | } 498 | .offset11 { 499 | margin-left: 1130px; 500 | } 501 | .offset10 { 502 | margin-left: 1030px; 503 | } 504 | .offset9 { 505 | margin-left: 930px; 506 | } 507 | .offset8 { 508 | margin-left: 830px; 509 | } 510 | .offset7 { 511 | margin-left: 730px; 512 | } 513 | .offset6 { 514 | margin-left: 630px; 515 | } 516 | .offset5 { 517 | margin-left: 530px; 518 | } 519 | .offset4 { 520 | margin-left: 430px; 521 | } 522 | .offset3 { 523 | margin-left: 330px; 524 | } 525 | .offset2 { 526 | margin-left: 230px; 527 | } 528 | .offset1 { 529 | margin-left: 130px; 530 | } 531 | .row-fluid { 532 | width: 100%; 533 | *zoom: 1; 534 | } 535 | .row-fluid:before, 536 | .row-fluid:after { 537 | display: table; 538 | content: ""; 539 | } 540 | .row-fluid:after { 541 | clear: both; 542 | } 543 | .row-fluid [class*="span"] { 544 | display: block; 545 | float: left; 546 | width: 100%; 547 | min-height: 28px; 548 | margin-left: 2.564102564%; 549 | *margin-left: 2.510911074638298%; 550 | -webkit-box-sizing: border-box; 551 | -moz-box-sizing: border-box; 552 | -ms-box-sizing: border-box; 553 | box-sizing: border-box; 554 | } 555 | .row-fluid [class*="span"]:first-child { 556 | margin-left: 0; 557 | } 558 | .row-fluid .span12 { 559 | width: 100%; 560 | *width: 99.94680851063829%; 561 | } 562 | .row-fluid .span11 { 563 | width: 91.45299145300001%; 564 | *width: 91.3997999636383%; 565 | } 566 | .row-fluid .span10 { 567 | width: 82.905982906%; 568 | *width: 82.8527914166383%; 569 | } 570 | .row-fluid .span9 { 571 | width: 74.358974359%; 572 | *width: 74.30578286963829%; 573 | } 574 | .row-fluid .span8 { 575 | width: 65.81196581200001%; 576 | *width: 65.7587743226383%; 577 | } 578 | .row-fluid .span7 { 579 | width: 57.264957265%; 580 | *width: 57.2117657756383%; 581 | } 582 | .row-fluid .span6 { 583 | width: 48.717948718%; 584 | *width: 48.6647572286383%; 585 | } 586 | .row-fluid .span5 { 587 | width: 40.170940171000005%; 588 | *width: 40.117748681638304%; 589 | } 590 | .row-fluid .span4 { 591 | width: 31.623931624%; 592 | *width: 31.5707401346383%; 593 | } 594 | .row-fluid .span3 { 595 | width: 23.076923077%; 596 | *width: 23.0237315876383%; 597 | } 598 | .row-fluid .span2 { 599 | width: 14.529914530000001%; 600 | *width: 14.4767230406383%; 601 | } 602 | .row-fluid .span1 { 603 | width: 5.982905983%; 604 | *width: 5.929714493638298%; 605 | } 606 | input, 607 | textarea, 608 | .uneditable-input { 609 | margin-left: 0; 610 | } 611 | input.span12, 612 | textarea.span12, 613 | .uneditable-input.span12 { 614 | width: 1160px; 615 | } 616 | input.span11, 617 | textarea.span11, 618 | .uneditable-input.span11 { 619 | width: 1060px; 620 | } 621 | input.span10, 622 | textarea.span10, 623 | .uneditable-input.span10 { 624 | width: 960px; 625 | } 626 | input.span9, 627 | textarea.span9, 628 | .uneditable-input.span9 { 629 | width: 860px; 630 | } 631 | input.span8, 632 | textarea.span8, 633 | .uneditable-input.span8 { 634 | width: 760px; 635 | } 636 | input.span7, 637 | textarea.span7, 638 | .uneditable-input.span7 { 639 | width: 660px; 640 | } 641 | input.span6, 642 | textarea.span6, 643 | .uneditable-input.span6 { 644 | width: 560px; 645 | } 646 | input.span5, 647 | textarea.span5, 648 | .uneditable-input.span5 { 649 | width: 460px; 650 | } 651 | input.span4, 652 | textarea.span4, 653 | .uneditable-input.span4 { 654 | width: 360px; 655 | } 656 | input.span3, 657 | textarea.span3, 658 | .uneditable-input.span3 { 659 | width: 260px; 660 | } 661 | input.span2, 662 | textarea.span2, 663 | .uneditable-input.span2 { 664 | width: 160px; 665 | } 666 | input.span1, 667 | textarea.span1, 668 | .uneditable-input.span1 { 669 | width: 60px; 670 | } 671 | .thumbnails { 672 | margin-left: -30px; 673 | } 674 | .thumbnails > li { 675 | margin-left: 30px; 676 | } 677 | .row-fluid .thumbnails { 678 | margin-left: 0; 679 | } 680 | } 681 | 682 | @media (max-width: 979px) { 683 | body { 684 | padding-top: 0; 685 | } 686 | .navbar-fixed-top, 687 | .navbar-fixed-bottom { 688 | position: static; 689 | } 690 | .navbar-fixed-top { 691 | margin-bottom: 18px; 692 | } 693 | .navbar-fixed-bottom { 694 | margin-top: 18px; 695 | } 696 | .navbar-fixed-top .navbar-inner, 697 | .navbar-fixed-bottom .navbar-inner { 698 | padding: 5px; 699 | } 700 | .navbar .container { 701 | width: auto; 702 | padding: 0; 703 | } 704 | .navbar .brand { 705 | padding-right: 10px; 706 | padding-left: 10px; 707 | margin: 0 0 0 -5px; 708 | } 709 | .nav-collapse { 710 | clear: both; 711 | } 712 | .nav-collapse .nav { 713 | float: none; 714 | margin: 0 0 9px; 715 | } 716 | .nav-collapse .nav > li { 717 | float: none; 718 | } 719 | .nav-collapse .nav > li > a { 720 | margin-bottom: 2px; 721 | } 722 | .nav-collapse .nav > .divider-vertical { 723 | display: none; 724 | } 725 | .nav-collapse .nav .nav-header { 726 | color: #999999; 727 | text-shadow: none; 728 | } 729 | .nav-collapse .nav > li > a, 730 | .nav-collapse .dropdown-menu a { 731 | padding: 6px 15px; 732 | font-weight: bold; 733 | color: #999999; 734 | -webkit-border-radius: 3px; 735 | -moz-border-radius: 3px; 736 | border-radius: 3px; 737 | } 738 | .nav-collapse .btn { 739 | padding: 4px 10px 4px; 740 | font-weight: normal; 741 | -webkit-border-radius: 4px; 742 | -moz-border-radius: 4px; 743 | border-radius: 4px; 744 | } 745 | .nav-collapse .dropdown-menu li + li a { 746 | margin-bottom: 2px; 747 | } 748 | .nav-collapse .nav > li > a:hover, 749 | .nav-collapse .dropdown-menu a:hover { 750 | background-color: #222222; 751 | } 752 | .nav-collapse.in .btn-group { 753 | padding: 0; 754 | margin-top: 5px; 755 | } 756 | .nav-collapse .dropdown-menu { 757 | position: static; 758 | top: auto; 759 | left: auto; 760 | display: block; 761 | float: none; 762 | max-width: none; 763 | padding: 0; 764 | margin: 0 15px; 765 | background-color: transparent; 766 | border: none; 767 | -webkit-border-radius: 0; 768 | -moz-border-radius: 0; 769 | border-radius: 0; 770 | -webkit-box-shadow: none; 771 | -moz-box-shadow: none; 772 | box-shadow: none; 773 | } 774 | .nav-collapse .dropdown-menu:before, 775 | .nav-collapse .dropdown-menu:after { 776 | display: none; 777 | } 778 | .nav-collapse .dropdown-menu .divider { 779 | display: none; 780 | } 781 | .nav-collapse .navbar-form, 782 | .nav-collapse .navbar-search { 783 | float: none; 784 | padding: 9px 15px; 785 | margin: 9px 0; 786 | border-top: 1px solid #222222; 787 | border-bottom: 1px solid #222222; 788 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); 789 | -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); 790 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); 791 | } 792 | .navbar .nav-collapse .nav.pull-right { 793 | float: none; 794 | margin-left: 0; 795 | } 796 | .nav-collapse, 797 | .nav-collapse.collapse { 798 | height: 0; 799 | overflow: hidden; 800 | } 801 | .navbar .btn-navbar { 802 | display: block; 803 | } 804 | .navbar-static .navbar-inner { 805 | padding-right: 10px; 806 | padding-left: 10px; 807 | } 808 | } 809 | 810 | @media (min-width: 980px) { 811 | .nav-collapse.collapse { 812 | height: auto !important; 813 | overflow: visible !important; 814 | } 815 | } 816 | -------------------------------------------------------------------------------- /css/bootstrap-responsive.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Responsive v2.0.4 3 | * 4 | * Copyright 2012 Twitter, Inc 5 | * Licensed under the Apache License v2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Designed and built with all the love in the world @twitter by @mdo and @fat. 9 | */.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box}.hidden{display:none;visibility:hidden}.visible-phone{display:none!important}.visible-tablet{display:none!important}.hidden-desktop{display:none!important}@media(max-width:767px){.visible-phone{display:inherit!important}.hidden-phone{display:none!important}.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}}@media(min-width:768px) and (max-width:979px){.visible-tablet{display:inherit!important}.hidden-tablet{display:none!important}.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}}@media(max-width:480px){.nav-collapse{-webkit-transform:translate3d(0,0,0)}.page-header h1 small{display:block;line-height:18px}input[type="checkbox"],input[type="radio"]{border:1px solid #ccc}.form-horizontal .control-group>label{float:none;width:auto;padding-top:0;text-align:left}.form-horizontal .controls{margin-left:0}.form-horizontal .control-list{padding-top:0}.form-horizontal .form-actions{padding-right:10px;padding-left:10px}.modal{position:absolute;top:10px;right:10px;left:10px;width:auto;margin:0}.modal.fade.in{top:auto}.modal-header .close{padding:10px;margin:-10px}.carousel-caption{position:static}}@media(max-width:767px){body{padding-right:20px;padding-left:20px}.navbar-fixed-top,.navbar-fixed-bottom{margin-right:-20px;margin-left:-20px}.container-fluid{padding:0}.dl-horizontal dt{float:none;width:auto;clear:none;text-align:left}.dl-horizontal dd{margin-left:0}.container{width:auto}.row-fluid{width:100%}.row,.thumbnails{margin-left:0}[class*="span"],.row-fluid [class*="span"]{display:block;float:none;width:auto;margin-left:0}.input-large,.input-xlarge,.input-xxlarge,input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box}.input-prepend input,.input-append input,.input-prepend input[class*="span"],.input-append input[class*="span"]{display:inline-block;width:auto}}@media(min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;content:""}.row:after{clear:both}[class*="span"]{float:left;margin-left:20px}.container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px}.span12{width:724px}.span11{width:662px}.span10{width:600px}.span9{width:538px}.span8{width:476px}.span7{width:414px}.span6{width:352px}.span5{width:290px}.span4{width:228px}.span3{width:166px}.span2{width:104px}.span1{width:42px}.offset12{margin-left:764px}.offset11{margin-left:702px}.offset10{margin-left:640px}.offset9{margin-left:578px}.offset8{margin-left:516px}.offset7{margin-left:454px}.offset6{margin-left:392px}.offset5{margin-left:330px}.offset4{margin-left:268px}.offset3{margin-left:206px}.offset2{margin-left:144px}.offset1{margin-left:82px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:28px;margin-left:2.762430939%;*margin-left:2.709239449638298%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .span12{width:99.999999993%;*width:99.9468085036383%}.row-fluid .span11{width:91.436464082%;*width:91.38327259263829%}.row-fluid .span10{width:82.87292817100001%;*width:82.8197366816383%}.row-fluid .span9{width:74.30939226%;*width:74.25620077063829%}.row-fluid .span8{width:65.74585634900001%;*width:65.6926648596383%}.row-fluid .span7{width:57.182320438000005%;*width:57.129128948638304%}.row-fluid .span6{width:48.618784527%;*width:48.5655930376383%}.row-fluid .span5{width:40.055248616%;*width:40.0020571266383%}.row-fluid .span4{width:31.491712705%;*width:31.4385212156383%}.row-fluid .span3{width:22.928176794%;*width:22.874985304638297%}.row-fluid .span2{width:14.364640883%;*width:14.311449393638298%}.row-fluid .span1{width:5.801104972%;*width:5.747913482638298%}input,textarea,.uneditable-input{margin-left:0}input.span12,textarea.span12,.uneditable-input.span12{width:714px}input.span11,textarea.span11,.uneditable-input.span11{width:652px}input.span10,textarea.span10,.uneditable-input.span10{width:590px}input.span9,textarea.span9,.uneditable-input.span9{width:528px}input.span8,textarea.span8,.uneditable-input.span8{width:466px}input.span7,textarea.span7,.uneditable-input.span7{width:404px}input.span6,textarea.span6,.uneditable-input.span6{width:342px}input.span5,textarea.span5,.uneditable-input.span5{width:280px}input.span4,textarea.span4,.uneditable-input.span4{width:218px}input.span3,textarea.span3,.uneditable-input.span3{width:156px}input.span2,textarea.span2,.uneditable-input.span2{width:94px}input.span1,textarea.span1,.uneditable-input.span1{width:32px}}@media(min-width:1200px){.row{margin-left:-30px;*zoom:1}.row:before,.row:after{display:table;content:""}.row:after{clear:both}[class*="span"]{float:left;margin-left:30px}.container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px}.span12{width:1170px}.span11{width:1070px}.span10{width:970px}.span9{width:870px}.span8{width:770px}.span7{width:670px}.span6{width:570px}.span5{width:470px}.span4{width:370px}.span3{width:270px}.span2{width:170px}.span1{width:70px}.offset12{margin-left:1230px}.offset11{margin-left:1130px}.offset10{margin-left:1030px}.offset9{margin-left:930px}.offset8{margin-left:830px}.offset7{margin-left:730px}.offset6{margin-left:630px}.offset5{margin-left:530px}.offset4{margin-left:430px}.offset3{margin-left:330px}.offset2{margin-left:230px}.offset1{margin-left:130px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:28px;margin-left:2.564102564%;*margin-left:2.510911074638298%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.45299145300001%;*width:91.3997999636383%}.row-fluid .span10{width:82.905982906%;*width:82.8527914166383%}.row-fluid .span9{width:74.358974359%;*width:74.30578286963829%}.row-fluid .span8{width:65.81196581200001%;*width:65.7587743226383%}.row-fluid .span7{width:57.264957265%;*width:57.2117657756383%}.row-fluid .span6{width:48.717948718%;*width:48.6647572286383%}.row-fluid .span5{width:40.170940171000005%;*width:40.117748681638304%}.row-fluid .span4{width:31.623931624%;*width:31.5707401346383%}.row-fluid .span3{width:23.076923077%;*width:23.0237315876383%}.row-fluid .span2{width:14.529914530000001%;*width:14.4767230406383%}.row-fluid .span1{width:5.982905983%;*width:5.929714493638298%}input,textarea,.uneditable-input{margin-left:0}input.span12,textarea.span12,.uneditable-input.span12{width:1160px}input.span11,textarea.span11,.uneditable-input.span11{width:1060px}input.span10,textarea.span10,.uneditable-input.span10{width:960px}input.span9,textarea.span9,.uneditable-input.span9{width:860px}input.span8,textarea.span8,.uneditable-input.span8{width:760px}input.span7,textarea.span7,.uneditable-input.span7{width:660px}input.span6,textarea.span6,.uneditable-input.span6{width:560px}input.span5,textarea.span5,.uneditable-input.span5{width:460px}input.span4,textarea.span4,.uneditable-input.span4{width:360px}input.span3,textarea.span3,.uneditable-input.span3{width:260px}input.span2,textarea.span2,.uneditable-input.span2{width:160px}input.span1,textarea.span1,.uneditable-input.span1{width:60px}.thumbnails{margin-left:-30px}.thumbnails>li{margin-left:30px}.row-fluid .thumbnails{margin-left:0}}@media(max-width:979px){body{padding-top:0}.navbar-fixed-top,.navbar-fixed-bottom{position:static}.navbar-fixed-top{margin-bottom:18px}.navbar-fixed-bottom{margin-top:18px}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding:5px}.navbar .container{width:auto;padding:0}.navbar .brand{padding-right:10px;padding-left:10px;margin:0 0 0 -5px}.nav-collapse{clear:both}.nav-collapse .nav{float:none;margin:0 0 9px}.nav-collapse .nav>li{float:none}.nav-collapse .nav>li>a{margin-bottom:2px}.nav-collapse .nav>.divider-vertical{display:none}.nav-collapse .nav .nav-header{color:#999;text-shadow:none}.nav-collapse .nav>li>a,.nav-collapse .dropdown-menu a{padding:6px 15px;font-weight:bold;color:#999;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.nav-collapse .btn{padding:4px 10px 4px;font-weight:normal;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.nav-collapse .dropdown-menu li+li a{margin-bottom:2px}.nav-collapse .nav>li>a:hover,.nav-collapse .dropdown-menu a:hover{background-color:#222}.nav-collapse.in .btn-group{padding:0;margin-top:5px}.nav-collapse .dropdown-menu{position:static;top:auto;left:auto;display:block;float:none;max-width:none;padding:0;margin:0 15px;background-color:transparent;border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.nav-collapse .dropdown-menu:before,.nav-collapse .dropdown-menu:after{display:none}.nav-collapse .dropdown-menu .divider{display:none}.nav-collapse .navbar-form,.nav-collapse .navbar-search{float:none;padding:9px 15px;margin:9px 0;border-top:1px solid #222;border-bottom:1px solid #222;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1)}.navbar .nav-collapse .nav.pull-right{float:none;margin-left:0}.nav-collapse,.nav-collapse.collapse{height:0;overflow:hidden}.navbar .btn-navbar{display:block}.navbar-static .navbar-inner{padding-right:10px;padding-left:10px}}@media(min-width:980px){.nav-collapse.collapse{height:auto!important;overflow:visible!important}} 10 | -------------------------------------------------------------------------------- /dot.htaccess: -------------------------------------------------------------------------------- 1 | # A .htaccess file is a hidden file which runs on Unix servers 2 | # and provides directives. Renaming this file to .htaccess and 3 | # placing in your root directory _should_ enable GZip compression, 4 | # reducing the weight of pages/scripts being served. 5 | 6 | 7 | mod_gzip_on Yes 8 | mod_gzip_dechunk Yes 9 | mod_gzip_item_include file \.(html?|txt|css|js|php|pl)$ 10 | mod_gzip_item_include handler ^cgi-script$ 11 | mod_gzip_item_include mime ^text/.* 12 | mod_gzip_item_include mime ^application/x-javascript.* 13 | mod_gzip_item_exclude mime ^image/.* 14 | mod_gzip_item_exclude rspheader ^Content-Encoding:.*gzip.* 15 | 16 | 17 | 18 | AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript application/x-javascript 19 | 20 | 21 | 22 | ExpiresActive On 23 | ExpiresDefault "access plus 1 seconds" 24 | ExpiresByType text/html "access plus 1 seconds" 25 | ExpiresByType image/gif "access plus 2592000 seconds" 26 | ExpiresByType image/jpeg "access plus 2592000 seconds" 27 | ExpiresByType image/png "access plus 2592000 seconds" 28 | ExpiresByType text/css "access plus 604800 seconds" 29 | ExpiresByType text/javascript "access plus 216000 seconds" 30 | ExpiresByType application/x-javascript "access plus 216000 seconds" 31 | 32 | 33 | 34 | Header unset ETag 35 | 36 | 37 | FileETag None 38 | 39 | 40 | Header unset Last-Modified 41 | 42 | 43 | AddType text/cache-manifest .manifest 44 | 45 | php_flag short_open_tag on -------------------------------------------------------------------------------- /feedback.php: -------------------------------------------------------------------------------- 1 | setUsername($zabbixUser); 11 | $zabbix->setPassword($zabbixPass); 12 | $zabbix->setZabbixApiUrl($zabbixApi); 13 | 14 | // Login 15 | if (strlen($zabbix->getUsername()) > 0 && strlen($zabbix->getPassword()) > 0 && strlen($zabbix->getZabbixApiUrl()) > 0) { 16 | $zabbix->login(); 17 | } 18 | 19 | if (!$zabbix->isLoggedIn()) { 20 | header("Location: index.php"); 21 | exit(); 22 | } 23 | 24 | $boolShowThankyou = false; 25 | $boolShowFeedbackForm = true; 26 | $boolShowTextlengthWarn = false; 27 | 28 | if (isset($_POST['mZabbixFeedback'])) { 29 | // Process feedback 30 | $txtFeedback = htmlentities($_POST['txtFeedback']); 31 | if (strlen($txtFeedback) < 30) { 32 | // Don't bother, type more 33 | $boolShowTextlengthWarn = true; 34 | $boolShowThankyou = false; 35 | $boolShowFeedbackForm = true; 36 | } else { 37 | // Mail me 38 | $source_ip = getVisitorIP(); 39 | $server_variables = $_SERVER; 40 | 41 | // I'll mail myself in HTML, thankyouverymuchkbye 42 | $mailHtml = array(); 43 | $mailHtml[] = "Source: " . $source_ip . ""; 44 | $mailHtml[] = "Date: " . date("Y-m-d, H:i:s") . ""; 45 | $mailHtml[] = "Feedback:
"; 46 | $mailHtml[] = "" . $txtFeedback . ""; 47 | $mailHtml[] = ""; 48 | $mailHtml[] = "
"; 49 | $mailHtml[] = "_SERVER variables"; 50 | foreach ($server_variables as $argument => $value) { 51 | $mailHtml[] = "- " . $argument . ": " . $value; 52 | } 53 | $mailHtml = implode("
", $mailHtml); 54 | 55 | // Content-type for my HTML 56 | $headers = 'MIME-Version: 1.0' . "\r\n"; 57 | $headers .= 'Content-type: text/html; charset=iso-8859-1' . "\r\n"; 58 | 59 | // Additional headers 60 | $headers .= 'To: Mattias Geniar ' . "\r\n"; 61 | $headers .= 'From: mZabbix Feedback ' . "\r\n"; 62 | 63 | // Send it 64 | mail("mattias.geniar@gmail.com", "mZabbix Feedback from " . $source_ip, $mailHtml, $headers); 65 | 66 | // Show some output 67 | $boolShowThankyou = true; 68 | $boolShowFeedbackForm = false; 69 | } 70 | 71 | } else { 72 | $boolShowFeedbackForm = true; 73 | } 74 | 75 | // "templates" 76 | require_once("template/header.php"); 77 | ?> 78 | 79 |
80 |
81 |

Feedback

82 | " target="_webapp">Back 83 |
84 | 85 | 88 |
    89 |
    90 | 91 |
  • 92 | Comments on this app:
    93 |
    95 | "; 98 | } else { 99 | echo ""; 100 | } 101 | ?> 102 | At least 30 characters long. 103 | 104 |
  • 105 | 106 |
  • 107 | 108 |
  • 109 |
    110 |
111 | 116 | 120 | 123 |
124 | 127 | -------------------------------------------------------------------------------- /functions.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /graph.php: -------------------------------------------------------------------------------- 1 | setUsername($zabbixUser); 12 | $zabbix->setPassword($zabbixPass); 13 | $zabbix->setZabbixApiUrl($zabbixApi); 14 | 15 | // Login 16 | if (isset($zabbixAuthHash) && strlen($zabbixAuthHash) > 0) { 17 | // Try it with the authentication hash we have 18 | $zabbix->setAuthToken($zabbixAuthHash); 19 | } elseif (strlen($zabbix->getUsername()) > 0 && strlen($zabbix->getPassword()) > 0 && strlen($zabbix->getZabbixApiUrl()) > 0) { 20 | $zabbix->login(); 21 | } 22 | 23 | if (!$zabbix->isLoggedIn()) { 24 | header("Location: index.php"); 25 | exit(); 26 | } 27 | 28 | require_once("template/header.php"); 29 | 30 | $zabbixGraphId = (string) $_GET['graphid']; 31 | $zabbixGraphPeriod = (string) $_GET['period']; 32 | $zabbixHostId = (string) $_GET['hostid']; 33 | $zabbixHostGroupId = (string) $_GET['groupid']; 34 | $zabbixHostGroupName = (string) urldecode($_GET['groupname']); 35 | $zabbixHostName = (string) urldecode($_GET['hostname']); 36 | 37 | $urlParameters = "hostid=". $zabbixHostId ."&hostname=". $zabbixHostName ."&groupid=". $zabbixHostGroupId ."&groupname=". urlencode($zabbixHostGroupName); 38 | 39 | if ($zabbixGraphId > 0) { 40 | $graph = $zabbix->getGraphById($zabbixGraphId); 41 | } else { 42 | $graph = null; 43 | } 44 | ?> 45 | 74 | 75 |
76 | 77 | 78 | 81 |

name?>

82 |

83 | Now showing a hour period. 84 |

85 | 86 | 87 | 114 |
115 | 118 |
119 | Invalid graph specified. 120 |
121 | 124 | -------------------------------------------------------------------------------- /graph_img.php: -------------------------------------------------------------------------------- 1 | setUsername($zabbixUser); 12 | $zabbix->setPassword($zabbixPass); 13 | $zabbix->setZabbixApiUrl($zabbixApi); 14 | 15 | // Login 16 | if (isset($zabbixAuthHash) && strlen($zabbixAuthHash) > 0) { 17 | // Try it with the authentication hash we have 18 | $zabbix->setAuthToken($zabbixAuthHash); 19 | } elseif (strlen($zabbix->getUsername()) > 0 && strlen($zabbix->getPassword()) > 0 && strlen($zabbix->getZabbixApiUrl()) > 0) { 20 | $zabbix->login(); 21 | } 22 | 23 | if (!$zabbix->isLoggedIn()) { 24 | header("Location: index.php"); 25 | exit(); 26 | } 27 | 28 | $graphid = (string) $_GET['graphid']; 29 | $graphperiod = (string) $_GET['period']; 30 | 31 | // Set correct header 32 | header("Content-Type: image/jpg"); 33 | 34 | // Read the file & output 35 | print $zabbix->getGraphImageById($graphid, $graphperiod); 36 | ?> 37 | -------------------------------------------------------------------------------- /host.php: -------------------------------------------------------------------------------- 1 | setUsername($zabbixUser); 11 | $zabbix->setPassword($zabbixPass); 12 | $zabbix->setZabbixApiUrl($zabbixApi); 13 | 14 | // Login 15 | if (isset($zabbixAuthHash) && strlen($zabbixAuthHash) > 0) { 16 | // Try it with the authentication hash we have 17 | $zabbix->setAuthToken($zabbixAuthHash); 18 | } elseif (strlen($zabbix->getUsername()) > 0 && strlen($zabbix->getPassword()) > 0 && strlen($zabbix->getZabbixApiUrl()) > 0) { 19 | $zabbix->login(); 20 | } 21 | 22 | if (!$zabbix->isLoggedIn()) { 23 | header("Location: index.php"); 24 | exit(); 25 | } 26 | 27 | require_once("template/header.php"); 28 | 29 | $zabbixHostId = (string) $_GET['hostid']; 30 | $zabbixHostGroupId = (string) $_GET['groupid']; 31 | $zabbixHostGroupName = (string) urldecode($_GET['groupname']); 32 | if ($zabbixHostId > 0) { 33 | $host = $zabbix->getHostById($zabbixHostId); 34 | 35 | // Graphs 36 | $graphs = $zabbix->getGraphsByHostId($zabbixHostId); 37 | $graphs = $zabbix->sortGraphsByName($graphs); 38 | 39 | // Triggers 40 | $triggers = $zabbix->getTriggersByHostId($zabbixHostId); 41 | ?> 42 | 43 | 63 | 64 |
65 |

host?>

66 | 67 |

Host details

68 |

69 | Host: host?>
70 | getVersion() == '1.4') { 72 | /* Zabbix 2.x compatible */ 73 | echo "Name: ". $host->name ."
"; 74 | 75 | echo "

Interfaces

"; 76 | $interfaces = (array) $host->interfaces; 77 | if (is_array($interfaces) && count($interfaces) > 0) { 78 | foreach ($host->interfaces as $interfaceId => $interfaceValue) { 79 | echo "DNS: ". $interfaceValue->dns ."
"; 80 | echo "IP: ". $interfaceValue->ip ."
"; 81 | echo "Zabbix Agent Port: ". $interfaceValue->port ."

"; 82 | } 83 | } else { 84 | echo '
This host does not have any configured interfaces.
'; 85 | } 86 | } else { 87 | /* Zabbix 1.8 compatible */ 88 | ?> 89 | DNS: dns) ? $host->dns : '' ?>
90 | IP: ip) ? $host->ip : '' ?>
91 | 94 |

95 | 96 | 0) { 98 | ?> 99 |

Graphs

100 | 113 | 116 |
117 |

This host does not have any available graphs to display. As soon as you add a graph in Zabbix, it will show up here.

118 |
119 | 122 |
123 | 126 |
127 | Invalid hostgroup specified. 128 |
129 | 132 | -------------------------------------------------------------------------------- /hostgroups.php: -------------------------------------------------------------------------------- 1 | setUsername($zabbixUser); 13 | $zabbix->setPassword($zabbixPass); 14 | $zabbix->setZabbixApiUrl($zabbixApi); 15 | 16 | // Login 17 | if (isset($zabbixAuthHash) && strlen($zabbixAuthHash) > 0) { 18 | // Try it with the authentication hash we have 19 | $zabbix->setAuthToken($zabbixAuthHash); 20 | } elseif (strlen($zabbix->getUsername()) > 0 && strlen($zabbix->getPassword()) > 0 && strlen($zabbix->getZabbixApiUrl()) > 0) { 21 | // Or try it with our info from the cookies 22 | $zabbix->login(); 23 | } 24 | 25 | if (!$zabbix->isLoggedIn()) { 26 | header("Location: index.php"); 27 | exit(); 28 | } 29 | 30 | require_once("template/header.php"); 31 | ?> 32 | 44 | 45 |
46 | getHostgroups(); 48 | $zabbixHostgroups = $zabbix->sortHostgroupsByName($zabbixHostgroups); 49 | 50 | if (is_array($zabbixHostgroups) && count($zabbixHostgroups) > 0) { 51 | ?> 52 | 78 | 81 |
82 |

Access Denied

83 |

84 | Sorry, you don't seem have access to any hostgroups.
85 |
86 | Does the user with which you login, have API Access enabled in the Zabbix User Administration screen? 87 |

88 |
89 | 92 |
93 | 94 | -------------------------------------------------------------------------------- /hostitems.php: -------------------------------------------------------------------------------- 1 | 0) { 4 | foreach ($arrZabbixItems["hostgroups"] as $hostgroupid => $hostgroup) { 5 | echo ""; 14 | } 15 | } 16 | 17 | // Second list: detail view of each host 18 | if (is_array($arrZabbixItems["hostgroups"]) && count($arrZabbixItems["hostgroups"]) > 0) { 19 | foreach ($arrZabbixItems["hostgroups"] as $hostgroupid => $hostgroup) { 20 | if (key_exists("hosts", $hostgroup) && is_array($hostgroup["hosts"]) && count($hostgroup["hosts"]) > 0) { 21 | $hosts = $hostgroup["hosts"]; 22 | 23 | foreach ($hosts as $hostid => $host) { 24 | $host_object = $host["host"]; 25 | $trigger = key_exists("triggers", $host) && is_array($host["triggers"]) ? $host["triggers"] : array();; 26 | 27 | //print_r($host_object); 28 | 29 | // Start our detailed list 30 | echo ""; 56 | } 57 | } 58 | } 59 | } 60 | ?> -------------------------------------------------------------------------------- /hosts.php: -------------------------------------------------------------------------------- 1 | setUsername($zabbixUser); 13 | $zabbix->setPassword($zabbixPass); 14 | $zabbix->setZabbixApiUrl($zabbixApi); 15 | 16 | // Login 17 | if (isset($zabbixAuthHash) && strlen($zabbixAuthHash) > 0) { 18 | // Try it with the authentication hash we have 19 | $zabbix->setAuthToken($zabbixAuthHash); 20 | } elseif (strlen($zabbix->getUsername()) > 0 && strlen($zabbix->getPassword()) > 0 && strlen($zabbix->getZabbixApiUrl()) > 0) { 21 | // Or try it with our info from the cookies 22 | $zabbix->login(); 23 | } 24 | 25 | if (!$zabbix->isLoggedIn()) { 26 | header("Location: index.php"); 27 | exit(); 28 | } 29 | 30 | require_once("template/header.php"); 31 | 32 | $zabbixHostgroupId = (string) $_GET['hostgroupid']; 33 | if ($zabbixHostgroupId > 0) { 34 | $hostgroup = $zabbix->getHostgroupById($zabbixHostgroupId); 35 | $hosts = $zabbix->getHostsByGroupId ($zabbixHostgroupId); 36 | $hosts = $zabbix->filterActiveHosts($hosts); 37 | $hosts = $zabbix->sortHostsByName($hosts); 38 | 39 | ?> 40 | 55 | 56 |
57 | 70 | 73 |
74 | Invalid hostgroup, aborting request. 75 |
76 | 77 | 80 | 81 |
82 | 83 | 86 | -------------------------------------------------------------------------------- /images/hosts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgeniar/MoZBX/1dcb90834b1e1c50c7186f54af924bcfc1b603a1/images/hosts.png -------------------------------------------------------------------------------- /img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgeniar/MoZBX/1dcb90834b1e1c50c7186f54af924bcfc1b603a1/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgeniar/MoZBX/1dcb90834b1e1c50c7186f54af924bcfc1b603a1/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | login(); 16 | }else { 17 | // It's a hosted version, perhaps we can recover username & password from cookies? 18 | $zabbixApi = ""; 19 | $zabbixUser = ""; 20 | $zabbixPass = ""; 21 | 22 | // Get the values 23 | require_once("cookies.php"); 24 | 25 | // Populate our class 26 | $zabbix->setUsername($zabbixUser); 27 | $zabbix->setPassword($zabbixPass); 28 | $zabbix->setZabbixApiUrl($zabbixApi); 29 | 30 | // Login 31 | if (strlen($zabbix->getUsername()) > 0 && strlen($zabbix->getPassword()) > 0 && strlen($zabbix->getZabbixApiUrl()) > 0) { 32 | $zabbix->login(); 33 | } 34 | } 35 | 36 | //$zabbix->Login("mattias_api", "test"); 37 | 38 | 39 | if ($zabbix->isLoggedIn()) { 40 | // Authenticated, save cookie 41 | setcookie("zabbixUsername", $zabbix->getUsername(), $arrSettings["cookieExpire"]); 42 | setcookie("zabbixPassword", $zabbix->getPassword(), $arrSettings["cookieExpire"]); 43 | setcookie("zabbixApi", $zabbix->getZabbixApiUrl(), $arrSettings["cookieExpire"]); 44 | setcookie("zabbixAuthHash", $zabbix->getAuthToken(), $arrSettings["cookieExpire"]); 45 | } 46 | 47 | // "templates" 48 | require_once("template/header.php"); 49 | 50 | if ($zabbix->isLoggedIn()) { 51 | 52 | // Retrieve the data in one go 53 | $zabbix_auth = $zabbix->getAuthToken(); 54 | 55 | // Get all active triggers (for the counter on homepage); 56 | $triggersActive = $zabbix->getTriggersActive($arrSettings["minimalSeverity"]); 57 | if (!is_array($triggersActive)) 58 | $triggersActive = array(); 59 | ?> 60 | 67 | 68 |
69 |

Monitoring

70 |

71 | Your hosts
72 | Active Triggers
73 | ">Refresh page
74 |

75 | 76 |

Options

77 |

78 | Logout
79 | Send Feedback
80 | About version
81 |

82 |
83 | 0 ? $_GET['username'] : $zabbixUser; 87 | $zabbixApi = isset($_GET['api_url']) && strlen($_GET['api_url']) > 0 ? $_GET['api_url'] : $zabbixApi; 88 | if (strpos($zabbixApi, 'api_jsonrpc.php') === false) 89 | $zabbixApi .= "api_jsonrpc.php"; 90 | $zabbixHideApi = isset($_GET['hideapi']) ? 'hideapi' : 'donthideapi'; 91 | $zabbixPass = isset($_GET['password']) ? $_GET['password'] : $arrSettings["zabbixPassword"]; 92 | ?> 93 | 100 | 101 |
102 |

Login

103 |
104 |
105 | getLastError()) > 0) { 109 | $arrError = $zabbix->getLastError(); 110 | $errormsg = $arrError["data"]; 111 | } else { 112 | $errormsg = "invalid combination"; 113 | } 114 | ?> 115 |
116 | 117 |
118 | 123 |
124 | 125 |
126 | 127 |

The URL of your public Zabbix web interface.

128 |
129 |
130 | 134 | 135 | 138 | 139 |
140 | 141 |
142 | 143 |
144 |
145 | 146 |
147 | 148 |
149 | 150 |
151 |
152 | 153 |
154 | 155 |
156 |
157 |
158 | 161 |
162 | 165 | -------------------------------------------------------------------------------- /js/bootstrap.js: -------------------------------------------------------------------------------- 1 | /* =================================================== 2 | * bootstrap-transition.js v2.0.4 3 | * http://twitter.github.com/bootstrap/javascript.html#transitions 4 | * =================================================== 5 | * Copyright 2012 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * ========================================================== */ 19 | 20 | 21 | !function ($) { 22 | 23 | $(function () { 24 | 25 | "use strict"; // jshint ;_; 26 | 27 | 28 | /* CSS TRANSITION SUPPORT (http://www.modernizr.com/) 29 | * ======================================================= */ 30 | 31 | $.support.transition = (function () { 32 | 33 | var transitionEnd = (function () { 34 | 35 | var el = document.createElement('bootstrap') 36 | , transEndEventNames = { 37 | 'WebkitTransition' : 'webkitTransitionEnd' 38 | , 'MozTransition' : 'transitionend' 39 | , 'OTransition' : 'oTransitionEnd' 40 | , 'msTransition' : 'MSTransitionEnd' 41 | , 'transition' : 'transitionend' 42 | } 43 | , name 44 | 45 | for (name in transEndEventNames){ 46 | if (el.style[name] !== undefined) { 47 | return transEndEventNames[name] 48 | } 49 | } 50 | 51 | }()) 52 | 53 | return transitionEnd && { 54 | end: transitionEnd 55 | } 56 | 57 | })() 58 | 59 | }) 60 | 61 | }(window.jQuery);/* ========================================================== 62 | * bootstrap-alert.js v2.0.4 63 | * http://twitter.github.com/bootstrap/javascript.html#alerts 64 | * ========================================================== 65 | * Copyright 2012 Twitter, Inc. 66 | * 67 | * Licensed under the Apache License, Version 2.0 (the "License"); 68 | * you may not use this file except in compliance with the License. 69 | * You may obtain a copy of the License at 70 | * 71 | * http://www.apache.org/licenses/LICENSE-2.0 72 | * 73 | * Unless required by applicable law or agreed to in writing, software 74 | * distributed under the License is distributed on an "AS IS" BASIS, 75 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 76 | * See the License for the specific language governing permissions and 77 | * limitations under the License. 78 | * ========================================================== */ 79 | 80 | 81 | !function ($) { 82 | 83 | "use strict"; // jshint ;_; 84 | 85 | 86 | /* ALERT CLASS DEFINITION 87 | * ====================== */ 88 | 89 | var dismiss = '[data-dismiss="alert"]' 90 | , Alert = function (el) { 91 | $(el).on('click', dismiss, this.close) 92 | } 93 | 94 | Alert.prototype.close = function (e) { 95 | var $this = $(this) 96 | , selector = $this.attr('data-target') 97 | , $parent 98 | 99 | if (!selector) { 100 | selector = $this.attr('href') 101 | selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 102 | } 103 | 104 | $parent = $(selector) 105 | 106 | e && e.preventDefault() 107 | 108 | $parent.length || ($parent = $this.hasClass('alert') ? $this : $this.parent()) 109 | 110 | $parent.trigger(e = $.Event('close')) 111 | 112 | if (e.isDefaultPrevented()) return 113 | 114 | $parent.removeClass('in') 115 | 116 | function removeElement() { 117 | $parent 118 | .trigger('closed') 119 | .remove() 120 | } 121 | 122 | $.support.transition && $parent.hasClass('fade') ? 123 | $parent.on($.support.transition.end, removeElement) : 124 | removeElement() 125 | } 126 | 127 | 128 | /* ALERT PLUGIN DEFINITION 129 | * ======================= */ 130 | 131 | $.fn.alert = function (option) { 132 | return this.each(function () { 133 | var $this = $(this) 134 | , data = $this.data('alert') 135 | if (!data) $this.data('alert', (data = new Alert(this))) 136 | if (typeof option == 'string') data[option].call($this) 137 | }) 138 | } 139 | 140 | $.fn.alert.Constructor = Alert 141 | 142 | 143 | /* ALERT DATA-API 144 | * ============== */ 145 | 146 | $(function () { 147 | $('body').on('click.alert.data-api', dismiss, Alert.prototype.close) 148 | }) 149 | 150 | }(window.jQuery);/* ============================================================ 151 | * bootstrap-button.js v2.0.4 152 | * http://twitter.github.com/bootstrap/javascript.html#buttons 153 | * ============================================================ 154 | * Copyright 2012 Twitter, Inc. 155 | * 156 | * Licensed under the Apache License, Version 2.0 (the "License"); 157 | * you may not use this file except in compliance with the License. 158 | * You may obtain a copy of the License at 159 | * 160 | * http://www.apache.org/licenses/LICENSE-2.0 161 | * 162 | * Unless required by applicable law or agreed to in writing, software 163 | * distributed under the License is distributed on an "AS IS" BASIS, 164 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 165 | * See the License for the specific language governing permissions and 166 | * limitations under the License. 167 | * ============================================================ */ 168 | 169 | 170 | !function ($) { 171 | 172 | "use strict"; // jshint ;_; 173 | 174 | 175 | /* BUTTON PUBLIC CLASS DEFINITION 176 | * ============================== */ 177 | 178 | var Button = function (element, options) { 179 | this.$element = $(element) 180 | this.options = $.extend({}, $.fn.button.defaults, options) 181 | } 182 | 183 | Button.prototype.setState = function (state) { 184 | var d = 'disabled' 185 | , $el = this.$element 186 | , data = $el.data() 187 | , val = $el.is('input') ? 'val' : 'html' 188 | 189 | state = state + 'Text' 190 | data.resetText || $el.data('resetText', $el[val]()) 191 | 192 | $el[val](data[state] || this.options[state]) 193 | 194 | // push to event loop to allow forms to submit 195 | setTimeout(function () { 196 | state == 'loadingText' ? 197 | $el.addClass(d).attr(d, d) : 198 | $el.removeClass(d).removeAttr(d) 199 | }, 0) 200 | } 201 | 202 | Button.prototype.toggle = function () { 203 | var $parent = this.$element.parent('[data-toggle="buttons-radio"]') 204 | 205 | $parent && $parent 206 | .find('.active') 207 | .removeClass('active') 208 | 209 | this.$element.toggleClass('active') 210 | } 211 | 212 | 213 | /* BUTTON PLUGIN DEFINITION 214 | * ======================== */ 215 | 216 | $.fn.button = function (option) { 217 | return this.each(function () { 218 | var $this = $(this) 219 | , data = $this.data('button') 220 | , options = typeof option == 'object' && option 221 | if (!data) $this.data('button', (data = new Button(this, options))) 222 | if (option == 'toggle') data.toggle() 223 | else if (option) data.setState(option) 224 | }) 225 | } 226 | 227 | $.fn.button.defaults = { 228 | loadingText: 'loading...' 229 | } 230 | 231 | $.fn.button.Constructor = Button 232 | 233 | 234 | /* BUTTON DATA-API 235 | * =============== */ 236 | 237 | $(function () { 238 | $('body').on('click.button.data-api', '[data-toggle^=button]', function ( e ) { 239 | var $btn = $(e.target) 240 | if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') 241 | $btn.button('toggle') 242 | }) 243 | }) 244 | 245 | }(window.jQuery);/* ========================================================== 246 | * bootstrap-carousel.js v2.0.4 247 | * http://twitter.github.com/bootstrap/javascript.html#carousel 248 | * ========================================================== 249 | * Copyright 2012 Twitter, Inc. 250 | * 251 | * Licensed under the Apache License, Version 2.0 (the "License"); 252 | * you may not use this file except in compliance with the License. 253 | * You may obtain a copy of the License at 254 | * 255 | * http://www.apache.org/licenses/LICENSE-2.0 256 | * 257 | * Unless required by applicable law or agreed to in writing, software 258 | * distributed under the License is distributed on an "AS IS" BASIS, 259 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 260 | * See the License for the specific language governing permissions and 261 | * limitations under the License. 262 | * ========================================================== */ 263 | 264 | 265 | !function ($) { 266 | 267 | "use strict"; // jshint ;_; 268 | 269 | 270 | /* CAROUSEL CLASS DEFINITION 271 | * ========================= */ 272 | 273 | var Carousel = function (element, options) { 274 | this.$element = $(element) 275 | this.options = options 276 | this.options.slide && this.slide(this.options.slide) 277 | this.options.pause == 'hover' && this.$element 278 | .on('mouseenter', $.proxy(this.pause, this)) 279 | .on('mouseleave', $.proxy(this.cycle, this)) 280 | } 281 | 282 | Carousel.prototype = { 283 | 284 | cycle: function (e) { 285 | if (!e) this.paused = false 286 | this.options.interval 287 | && !this.paused 288 | && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) 289 | return this 290 | } 291 | 292 | , to: function (pos) { 293 | var $active = this.$element.find('.active') 294 | , children = $active.parent().children() 295 | , activePos = children.index($active) 296 | , that = this 297 | 298 | if (pos > (children.length - 1) || pos < 0) return 299 | 300 | if (this.sliding) { 301 | return this.$element.one('slid', function () { 302 | that.to(pos) 303 | }) 304 | } 305 | 306 | if (activePos == pos) { 307 | return this.pause().cycle() 308 | } 309 | 310 | return this.slide(pos > activePos ? 'next' : 'prev', $(children[pos])) 311 | } 312 | 313 | , pause: function (e) { 314 | if (!e) this.paused = true 315 | clearInterval(this.interval) 316 | this.interval = null 317 | return this 318 | } 319 | 320 | , next: function () { 321 | if (this.sliding) return 322 | return this.slide('next') 323 | } 324 | 325 | , prev: function () { 326 | if (this.sliding) return 327 | return this.slide('prev') 328 | } 329 | 330 | , slide: function (type, next) { 331 | var $active = this.$element.find('.active') 332 | , $next = next || $active[type]() 333 | , isCycling = this.interval 334 | , direction = type == 'next' ? 'left' : 'right' 335 | , fallback = type == 'next' ? 'first' : 'last' 336 | , that = this 337 | , e = $.Event('slide') 338 | 339 | this.sliding = true 340 | 341 | isCycling && this.pause() 342 | 343 | $next = $next.length ? $next : this.$element.find('.item')[fallback]() 344 | 345 | if ($next.hasClass('active')) return 346 | 347 | if ($.support.transition && this.$element.hasClass('slide')) { 348 | this.$element.trigger(e) 349 | if (e.isDefaultPrevented()) return 350 | $next.addClass(type) 351 | $next[0].offsetWidth // force reflow 352 | $active.addClass(direction) 353 | $next.addClass(direction) 354 | this.$element.one($.support.transition.end, function () { 355 | $next.removeClass([type, direction].join(' ')).addClass('active') 356 | $active.removeClass(['active', direction].join(' ')) 357 | that.sliding = false 358 | setTimeout(function () { that.$element.trigger('slid') }, 0) 359 | }) 360 | } else { 361 | this.$element.trigger(e) 362 | if (e.isDefaultPrevented()) return 363 | $active.removeClass('active') 364 | $next.addClass('active') 365 | this.sliding = false 366 | this.$element.trigger('slid') 367 | } 368 | 369 | isCycling && this.cycle() 370 | 371 | return this 372 | } 373 | 374 | } 375 | 376 | 377 | /* CAROUSEL PLUGIN DEFINITION 378 | * ========================== */ 379 | 380 | $.fn.carousel = function (option) { 381 | return this.each(function () { 382 | var $this = $(this) 383 | , data = $this.data('carousel') 384 | , options = $.extend({}, $.fn.carousel.defaults, typeof option == 'object' && option) 385 | if (!data) $this.data('carousel', (data = new Carousel(this, options))) 386 | if (typeof option == 'number') data.to(option) 387 | else if (typeof option == 'string' || (option = options.slide)) data[option]() 388 | else if (options.interval) data.cycle() 389 | }) 390 | } 391 | 392 | $.fn.carousel.defaults = { 393 | interval: 5000 394 | , pause: 'hover' 395 | } 396 | 397 | $.fn.carousel.Constructor = Carousel 398 | 399 | 400 | /* CAROUSEL DATA-API 401 | * ================= */ 402 | 403 | $(function () { 404 | $('body').on('click.carousel.data-api', '[data-slide]', function ( e ) { 405 | var $this = $(this), href 406 | , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7 407 | , options = !$target.data('modal') && $.extend({}, $target.data(), $this.data()) 408 | $target.carousel(options) 409 | e.preventDefault() 410 | }) 411 | }) 412 | 413 | }(window.jQuery);/* ============================================================= 414 | * bootstrap-collapse.js v2.0.4 415 | * http://twitter.github.com/bootstrap/javascript.html#collapse 416 | * ============================================================= 417 | * Copyright 2012 Twitter, Inc. 418 | * 419 | * Licensed under the Apache License, Version 2.0 (the "License"); 420 | * you may not use this file except in compliance with the License. 421 | * You may obtain a copy of the License at 422 | * 423 | * http://www.apache.org/licenses/LICENSE-2.0 424 | * 425 | * Unless required by applicable law or agreed to in writing, software 426 | * distributed under the License is distributed on an "AS IS" BASIS, 427 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 428 | * See the License for the specific language governing permissions and 429 | * limitations under the License. 430 | * ============================================================ */ 431 | 432 | 433 | !function ($) { 434 | 435 | "use strict"; // jshint ;_; 436 | 437 | 438 | /* COLLAPSE PUBLIC CLASS DEFINITION 439 | * ================================ */ 440 | 441 | var Collapse = function (element, options) { 442 | this.$element = $(element) 443 | this.options = $.extend({}, $.fn.collapse.defaults, options) 444 | 445 | if (this.options.parent) { 446 | this.$parent = $(this.options.parent) 447 | } 448 | 449 | this.options.toggle && this.toggle() 450 | } 451 | 452 | Collapse.prototype = { 453 | 454 | constructor: Collapse 455 | 456 | , dimension: function () { 457 | var hasWidth = this.$element.hasClass('width') 458 | return hasWidth ? 'width' : 'height' 459 | } 460 | 461 | , show: function () { 462 | var dimension 463 | , scroll 464 | , actives 465 | , hasData 466 | 467 | if (this.transitioning) return 468 | 469 | dimension = this.dimension() 470 | scroll = $.camelCase(['scroll', dimension].join('-')) 471 | actives = this.$parent && this.$parent.find('> .accordion-group > .in') 472 | 473 | if (actives && actives.length) { 474 | hasData = actives.data('collapse') 475 | if (hasData && hasData.transitioning) return 476 | actives.collapse('hide') 477 | hasData || actives.data('collapse', null) 478 | } 479 | 480 | this.$element[dimension](0) 481 | this.transition('addClass', $.Event('show'), 'shown') 482 | this.$element[dimension](this.$element[0][scroll]) 483 | } 484 | 485 | , hide: function () { 486 | var dimension 487 | if (this.transitioning) return 488 | dimension = this.dimension() 489 | this.reset(this.$element[dimension]()) 490 | this.transition('removeClass', $.Event('hide'), 'hidden') 491 | this.$element[dimension](0) 492 | } 493 | 494 | , reset: function (size) { 495 | var dimension = this.dimension() 496 | 497 | this.$element 498 | .removeClass('collapse') 499 | [dimension](size || 'auto') 500 | [0].offsetWidth 501 | 502 | this.$element[size !== null ? 'addClass' : 'removeClass']('collapse') 503 | 504 | return this 505 | } 506 | 507 | , transition: function (method, startEvent, completeEvent) { 508 | var that = this 509 | , complete = function () { 510 | if (startEvent.type == 'show') that.reset() 511 | that.transitioning = 0 512 | that.$element.trigger(completeEvent) 513 | } 514 | 515 | this.$element.trigger(startEvent) 516 | 517 | if (startEvent.isDefaultPrevented()) return 518 | 519 | this.transitioning = 1 520 | 521 | this.$element[method]('in') 522 | 523 | $.support.transition && this.$element.hasClass('collapse') ? 524 | this.$element.one($.support.transition.end, complete) : 525 | complete() 526 | } 527 | 528 | , toggle: function () { 529 | this[this.$element.hasClass('in') ? 'hide' : 'show']() 530 | } 531 | 532 | } 533 | 534 | 535 | /* COLLAPSIBLE PLUGIN DEFINITION 536 | * ============================== */ 537 | 538 | $.fn.collapse = function (option) { 539 | return this.each(function () { 540 | var $this = $(this) 541 | , data = $this.data('collapse') 542 | , options = typeof option == 'object' && option 543 | if (!data) $this.data('collapse', (data = new Collapse(this, options))) 544 | if (typeof option == 'string') data[option]() 545 | }) 546 | } 547 | 548 | $.fn.collapse.defaults = { 549 | toggle: true 550 | } 551 | 552 | $.fn.collapse.Constructor = Collapse 553 | 554 | 555 | /* COLLAPSIBLE DATA-API 556 | * ==================== */ 557 | 558 | $(function () { 559 | $('body').on('click.collapse.data-api', '[data-toggle=collapse]', function ( e ) { 560 | var $this = $(this), href 561 | , target = $this.attr('data-target') 562 | || e.preventDefault() 563 | || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7 564 | , option = $(target).data('collapse') ? 'toggle' : $this.data() 565 | $(target).collapse(option) 566 | }) 567 | }) 568 | 569 | }(window.jQuery);/* ============================================================ 570 | * bootstrap-dropdown.js v2.0.4 571 | * http://twitter.github.com/bootstrap/javascript.html#dropdowns 572 | * ============================================================ 573 | * Copyright 2012 Twitter, Inc. 574 | * 575 | * Licensed under the Apache License, Version 2.0 (the "License"); 576 | * you may not use this file except in compliance with the License. 577 | * You may obtain a copy of the License at 578 | * 579 | * http://www.apache.org/licenses/LICENSE-2.0 580 | * 581 | * Unless required by applicable law or agreed to in writing, software 582 | * distributed under the License is distributed on an "AS IS" BASIS, 583 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 584 | * See the License for the specific language governing permissions and 585 | * limitations under the License. 586 | * ============================================================ */ 587 | 588 | 589 | !function ($) { 590 | 591 | "use strict"; // jshint ;_; 592 | 593 | 594 | /* DROPDOWN CLASS DEFINITION 595 | * ========================= */ 596 | 597 | var toggle = '[data-toggle="dropdown"]' 598 | , Dropdown = function (element) { 599 | var $el = $(element).on('click.dropdown.data-api', this.toggle) 600 | $('html').on('click.dropdown.data-api', function () { 601 | $el.parent().removeClass('open') 602 | }) 603 | } 604 | 605 | Dropdown.prototype = { 606 | 607 | constructor: Dropdown 608 | 609 | , toggle: function (e) { 610 | var $this = $(this) 611 | , $parent 612 | , selector 613 | , isActive 614 | 615 | if ($this.is('.disabled, :disabled')) return 616 | 617 | selector = $this.attr('data-target') 618 | 619 | if (!selector) { 620 | selector = $this.attr('href') 621 | selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 622 | } 623 | 624 | $parent = $(selector) 625 | $parent.length || ($parent = $this.parent()) 626 | 627 | isActive = $parent.hasClass('open') 628 | 629 | clearMenus() 630 | 631 | if (!isActive) $parent.toggleClass('open') 632 | 633 | return false 634 | } 635 | 636 | } 637 | 638 | function clearMenus() { 639 | $(toggle).parent().removeClass('open') 640 | } 641 | 642 | 643 | /* DROPDOWN PLUGIN DEFINITION 644 | * ========================== */ 645 | 646 | $.fn.dropdown = function (option) { 647 | return this.each(function () { 648 | var $this = $(this) 649 | , data = $this.data('dropdown') 650 | if (!data) $this.data('dropdown', (data = new Dropdown(this))) 651 | if (typeof option == 'string') data[option].call($this) 652 | }) 653 | } 654 | 655 | $.fn.dropdown.Constructor = Dropdown 656 | 657 | 658 | /* APPLY TO STANDARD DROPDOWN ELEMENTS 659 | * =================================== */ 660 | 661 | $(function () { 662 | $('html').on('click.dropdown.data-api', clearMenus) 663 | $('body') 664 | .on('click.dropdown', '.dropdown form', function (e) { e.stopPropagation() }) 665 | .on('click.dropdown.data-api', toggle, Dropdown.prototype.toggle) 666 | }) 667 | 668 | }(window.jQuery);/* ========================================================= 669 | * bootstrap-modal.js v2.0.4 670 | * http://twitter.github.com/bootstrap/javascript.html#modals 671 | * ========================================================= 672 | * Copyright 2012 Twitter, Inc. 673 | * 674 | * Licensed under the Apache License, Version 2.0 (the "License"); 675 | * you may not use this file except in compliance with the License. 676 | * You may obtain a copy of the License at 677 | * 678 | * http://www.apache.org/licenses/LICENSE-2.0 679 | * 680 | * Unless required by applicable law or agreed to in writing, software 681 | * distributed under the License is distributed on an "AS IS" BASIS, 682 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 683 | * See the License for the specific language governing permissions and 684 | * limitations under the License. 685 | * ========================================================= */ 686 | 687 | 688 | !function ($) { 689 | 690 | "use strict"; // jshint ;_; 691 | 692 | 693 | /* MODAL CLASS DEFINITION 694 | * ====================== */ 695 | 696 | var Modal = function (content, options) { 697 | this.options = options 698 | this.$element = $(content) 699 | .delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this)) 700 | } 701 | 702 | Modal.prototype = { 703 | 704 | constructor: Modal 705 | 706 | , toggle: function () { 707 | return this[!this.isShown ? 'show' : 'hide']() 708 | } 709 | 710 | , show: function () { 711 | var that = this 712 | , e = $.Event('show') 713 | 714 | this.$element.trigger(e) 715 | 716 | if (this.isShown || e.isDefaultPrevented()) return 717 | 718 | $('body').addClass('modal-open') 719 | 720 | this.isShown = true 721 | 722 | escape.call(this) 723 | backdrop.call(this, function () { 724 | var transition = $.support.transition && that.$element.hasClass('fade') 725 | 726 | if (!that.$element.parent().length) { 727 | that.$element.appendTo(document.body) //don't move modals dom position 728 | } 729 | 730 | that.$element 731 | .show() 732 | 733 | if (transition) { 734 | that.$element[0].offsetWidth // force reflow 735 | } 736 | 737 | that.$element.addClass('in') 738 | 739 | transition ? 740 | that.$element.one($.support.transition.end, function () { that.$element.trigger('shown') }) : 741 | that.$element.trigger('shown') 742 | 743 | }) 744 | } 745 | 746 | , hide: function (e) { 747 | e && e.preventDefault() 748 | 749 | var that = this 750 | 751 | e = $.Event('hide') 752 | 753 | this.$element.trigger(e) 754 | 755 | if (!this.isShown || e.isDefaultPrevented()) return 756 | 757 | this.isShown = false 758 | 759 | $('body').removeClass('modal-open') 760 | 761 | escape.call(this) 762 | 763 | this.$element.removeClass('in') 764 | 765 | $.support.transition && this.$element.hasClass('fade') ? 766 | hideWithTransition.call(this) : 767 | hideModal.call(this) 768 | } 769 | 770 | } 771 | 772 | 773 | /* MODAL PRIVATE METHODS 774 | * ===================== */ 775 | 776 | function hideWithTransition() { 777 | var that = this 778 | , timeout = setTimeout(function () { 779 | that.$element.off($.support.transition.end) 780 | hideModal.call(that) 781 | }, 500) 782 | 783 | this.$element.one($.support.transition.end, function () { 784 | clearTimeout(timeout) 785 | hideModal.call(that) 786 | }) 787 | } 788 | 789 | function hideModal(that) { 790 | this.$element 791 | .hide() 792 | .trigger('hidden') 793 | 794 | backdrop.call(this) 795 | } 796 | 797 | function backdrop(callback) { 798 | var that = this 799 | , animate = this.$element.hasClass('fade') ? 'fade' : '' 800 | 801 | if (this.isShown && this.options.backdrop) { 802 | var doAnimate = $.support.transition && animate 803 | 804 | this.$backdrop = $('