Просмотр исходного кода

Merge pull request #1414 from causefx/v2-develop

V2 develop
causefx 6 лет назад
Родитель
Сommit
c5a7ba3369

+ 2 - 0
.gitignore

@@ -115,6 +115,8 @@ plugins/theme_files/*
 plugins/plugin_files/*
 plugins/plugin_files/*
 !plugins/theme_files/index.html
 !plugins/theme_files/index.html
 !plugins/plugin_files/index.html
 !plugins/plugin_files/index.html
+plugins/images/userTabs/*
+!plugins/images/userTabs/index.html
 # =========================
 # =========================
 # Plugin files
 # Plugin files
 # =========================
 # =========================

+ 42 - 1
api/config/default.php

@@ -8,6 +8,8 @@ return array(
 	'authBackendHostSuffix' => '',
 	'authBackendHostSuffix' => '',
 	'ldapBindUsername' => '',
 	'ldapBindUsername' => '',
 	'ldapBindPassword' => '',
 	'ldapBindPassword' => '',
+	'ldapSSL' => false,
+	'ldapTLS' => false,
 	'authBaseDN' => '',
 	'authBaseDN' => '',
 	'authBackendDomain' => '',
 	'authBackendDomain' => '',
 	'ldapType' => '1',
 	'ldapType' => '1',
@@ -316,37 +318,76 @@ return array(
 	'speedtestHeaderToggle' => true,
 	'speedtestHeaderToggle' => true,
 	'speedtestHeader' => 'Speedtest',
 	'speedtestHeader' => 'Speedtest',
 	'homepageNetdataEnabled' => false,
 	'homepageNetdataEnabled' => false,
-	'homepageNetdataRefresh' => '2500',
+	'homepageNetdataRefresh' => '10000',
 	'homepageNetdataAuth' => '1',
 	'homepageNetdataAuth' => '1',
 	'netdataURL' => '',
 	'netdataURL' => '',
 	'netdata1Title' => '',
 	'netdata1Title' => '',
 	'netdata1Chart' => '',
 	'netdata1Chart' => '',
 	'netdata1Data' => '',
 	'netdata1Data' => '',
 	'netdata1Colour' => '',
 	'netdata1Colour' => '',
+	'netdata1Size' => '',
+	'netdata1sm' => true,
+	'netdata1md' => true,
+	'netdata1lg' => true,
 	'netdata2Title' => '',
 	'netdata2Title' => '',
 	'netdata2Chart' => '',
 	'netdata2Chart' => '',
 	'netdata2Data' => '',
 	'netdata2Data' => '',
 	'netdata2Colour' => '',
 	'netdata2Colour' => '',
+	'netdata2Size' => '',
+	'netdata2sm' => true,
+	'netdata2md' => true,
+	'netdata2lg' => true,
 	'netdata3Title' => '',
 	'netdata3Title' => '',
 	'netdata3Chart' => '',
 	'netdata3Chart' => '',
 	'netdata3Data' => '',
 	'netdata3Data' => '',
 	'netdata3Colour' => '',
 	'netdata3Colour' => '',
+	'netdata3Size' => '',
+	'netdata3sm' => true,
+	'netdata3md' => true,
+	'netdata3lg' => true,
 	'netdata4Title' => '',
 	'netdata4Title' => '',
 	'netdata4Chart' => '',
 	'netdata4Chart' => '',
 	'netdata4Data' => '',
 	'netdata4Data' => '',
 	'netdata4Colour' => '',
 	'netdata4Colour' => '',
+	'netdata4Size' => '',
+	'netdata4sm' => true,
+	'netdata4md' => true,
+	'netdata4lg' => true,
 	'netdata5Title' => '',
 	'netdata5Title' => '',
 	'netdata5Chart' => '',
 	'netdata5Chart' => '',
 	'netdata5Data' => '',
 	'netdata5Data' => '',
 	'netdata5Colour' => '',
 	'netdata5Colour' => '',
+	'netdata5Size' => '',
+	'netdata5sm' => true,
+	'netdata5md' => true,
+	'netdata5lg' => true,
 	'netdata6Title' => '',
 	'netdata6Title' => '',
 	'netdata6Chart' => '',
 	'netdata6Chart' => '',
 	'netdata6Data' => '',
 	'netdata6Data' => '',
 	'netdata6Colour' => '',
 	'netdata6Colour' => '',
+	'netdata6Size' => '',
+	'netdata6sm' => true,
+	'netdata6md' => true,
+	'netdata6lg' => true,
+	'netdata7Title' => '',
+	'netdata7Chart' => '',
+	'netdata7Data' => '',
+	'netdata7Colour' => '',
+	'netdata7Size' => '',
+	'netdata7sm' => true,
+	'netdata7md' => true,
+	'netdata7lg' => true,
 	'netdata1Enabled' => false,
 	'netdata1Enabled' => false,
 	'netdata2Enabled' => false,
 	'netdata2Enabled' => false,
 	'netdata3Enabled' => false,
 	'netdata3Enabled' => false,
 	'netdata4Enabled' => false,
 	'netdata4Enabled' => false,
 	'netdata5Enabled' => false,
 	'netdata5Enabled' => false,
 	'netdata6Enabled' => false,
 	'netdata6Enabled' => false,
+	'netdata7Enabled' => false,
+	'netdataCustom' => '{
+    
+	}',
+	'githubMenuLink' => true,
+	'organizrSupportMenuLink' => true,
+	'organizrDocsMenuLink' => true
 );
 );

+ 2 - 2
api/functions/auth-functions.php

@@ -225,8 +225,8 @@ if (function_exists('ldap_connect')) {
 				'account_suffix' => (empty($GLOBALS['authBackendHostSuffix'])) ? null : $GLOBALS['authBackendHostSuffix'],
 				'account_suffix' => (empty($GLOBALS['authBackendHostSuffix'])) ? null : $GLOBALS['authBackendHostSuffix'],
 				'port' => $ldapPort,
 				'port' => $ldapPort,
 				'follow_referrals' => false,
 				'follow_referrals' => false,
-				'use_ssl' => false,
-				'use_tls' => false,
+				'use_ssl' => $GLOBALS['ldapSSL'],
+				'use_tls' => $GLOBALS['ldapTLS'],
 				'version' => 3,
 				'version' => 3,
 				'timeout' => 5,
 				'timeout' => 5,
 				// Custom LDAP Options
 				// Custom LDAP Options

+ 76 - 56
api/functions/homepage-connect-functions.php

@@ -1535,43 +1535,42 @@ function calendarDaysCheck($entryStart, $entryEnd)
 
 
 function calendarStandardizeTimezone($timezone)
 function calendarStandardizeTimezone($timezone)
 {
 {
-    switch ($timezone) {
-        case('CST'):
-        case('Central Time'):
-        case('Central Standard Time'):
-            $timezone = 'America/Chicago';
-            break;
-        case('CET'):
-        case('Central European Time'):
-            $timezone = 'Europe/Berlin';
-            break;
-        case('EST'):
-        case('Eastern Time'):
-        case('Eastern Standard Time'):
-            $timezone = 'America/New_York';
-            break;
-        case('PST'):
-        case('Pacific Time'):
-        case('Pacific Standard Time'):
-            $timezone = 'America/Los_Angeles';
-            break;
-        case('China Time'):
-        case('China Standard Time'):
-            $timezone = 'Asia/Beijing';
-            break;
-        case('IST'):
-        case('India Time'):
-        case('India Standard Time'):
-            $timezone = 'Asia/New_Delhi';
-            break;
-        case('JST');
-        case('Japan Time'):
-        case('Japan Standard Time'):
-            $timezone = 'Asia/Tokyo';
-            break;
-    }
-
-    return $timezone;
+	switch ($timezone) {
+		case('CST'):
+		case('Central Time'):
+		case('Central Standard Time'):
+			$timezone = 'America/Chicago';
+			break;
+		case('CET'):
+		case('Central European Time'):
+			$timezone = 'Europe/Berlin';
+			break;
+		case('EST'):
+		case('Eastern Time'):
+		case('Eastern Standard Time'):
+			$timezone = 'America/New_York';
+			break;
+		case('PST'):
+		case('Pacific Time'):
+		case('Pacific Standard Time'):
+			$timezone = 'America/Los_Angeles';
+			break;
+		case('China Time'):
+		case('China Standard Time'):
+			$timezone = 'Asia/Beijing';
+			break;
+		case('IST'):
+		case('India Time'):
+		case('India Standard Time'):
+			$timezone = 'Asia/New_Delhi';
+			break;
+		case('JST');
+		case('Japan Time'):
+		case('Japan Standard Time'):
+			$timezone = 'Asia/Tokyo';
+			break;
+	}
+	return $timezone;
 }
 }
 
 
 function getCalenderRepeat($value)
 function getCalenderRepeat($value)
@@ -2636,7 +2635,7 @@ function getMonitorr()
 				// This section grabs the names of all services by regex
 				// This section grabs the names of all services by regex
 				$services = [];
 				$services = [];
 				$servicesMatch = [];
 				$servicesMatch = [];
-				$servicePattern = '/<div id="servicetitle"><div>(.*)<\/div><\/div><div class="btnonline">Online<\/div><\/a><\/div><\/div>|<div id="servicetitleoffline".*><div>(.*)<\/div><\/div><div class="btnoffline".*>Offline<\/div><\/div><\/div>|<div id="servicetitlenolink".*><div>(.*)<\/div><\/div><div class="btnonline".*>Online<\/div><\/div><\/div>/';
+				$servicePattern = '/<div id="servicetitle"><div>(.*)<\/div><\/div><div class="btnonline">Online<\/div><\/a><\/div><\/div>|<div id="servicetitleoffline".*><div>(.*)<\/div><\/div><div class="btnoffline".*>Offline<\/div><\/div><\/div>|<div id="servicetitlenolink".*><div>(.*)<\/div><\/div><div class="btnonline".*>Online<\/div><\/div><\/div>|<div id="servicetitle"><div>(.*)<\/div><\/div><div class="btnunknown">/';
 				preg_match_all($servicePattern, $html, $servicesMatch);
 				preg_match_all($servicePattern, $html, $servicesMatch);
 				unset($servicesMatch[0]);
 				unset($servicesMatch[0]);
 				$servicesMatch = array_values($servicesMatch);
 				$servicesMatch = array_values($servicesMatch);
@@ -2650,7 +2649,7 @@ function getMonitorr()
 				// This section then grabs the status and image of that service with regex
 				// This section then grabs the status and image of that service with regex
 				$statuses = [];
 				$statuses = [];
 				foreach ($services as $service) {
 				foreach ($services as $service) {
-					$statusPattern = '/' . $service . '<\/div><\/div><div class="btnonline">(Online)<\/div>|' . $service . '<\/div><\/div><div class="btnoffline".*>(Offline)<\/div><\/div><\/div>/';
+					$statusPattern = '/' . $service . '<\/div><\/div><div class="btnonline">(Online)<\/div>|' . $service . '<\/div><\/div><div class="btnoffline".*>(Offline)<\/div><\/div><\/div>|' . $service . '<\/div><\/div><div class="btnunknown">(.*)<\/div><\/a>/';
 					$status = [];
 					$status = [];
 					preg_match($statusPattern, $html, $status);
 					preg_match($statusPattern, $html, $status);
 					$statuses[$service] = $status;
 					$statuses[$service] = $status;
@@ -2663,6 +2662,10 @@ function getMonitorr()
 							$statuses[$service] = [
 							$statuses[$service] = [
 								'status' => false
 								'status' => false
 							];
 							];
+						} else if ($match == 'Unresponsive') {
+							$statuses[$service] = [
+								'status' => 'unresponsive'
+							];
 						}
 						}
 					}
 					}
 					$imageMatch = [];
 					$imageMatch = [];
@@ -2708,6 +2711,7 @@ function getMonitorr()
 			}
 			}
 		} catch (Requests_Exception $e) {
 		} catch (Requests_Exception $e) {
 			writeLog('error', 'Monitorr Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
 			writeLog('error', 'Monitorr Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
+			$api['error'] = $e->getMessage();
 		};
 		};
 		$api = isset($api) ? $api : false;
 		$api = isset($api) ? $api : false;
 		return $api;
 		return $api;
@@ -2724,13 +2728,11 @@ function getSpeedtest()
 			$response = Requests::get($dataUrl);
 			$response = Requests::get($dataUrl);
 			if ($response->success) {
 			if ($response->success) {
 				$json = json_decode($response->body, true);
 				$json = json_decode($response->body, true);
-
 				$api['data'] = [
 				$api['data'] = [
 					'current' => $json['data'],
 					'current' => $json['data'],
 					'average' => $json['average'],
 					'average' => $json['average'],
 					'max' => $json['max'],
 					'max' => $json['max'],
 				];
 				];
-
 				$api['options'] = [
 				$api['options'] = [
 					'title' => $GLOBALS['speedtestHeader'],
 					'title' => $GLOBALS['speedtestHeader'],
 					'titleToggle' => $GLOBALS['speedtestHeaderToggle'],
 					'titleToggle' => $GLOBALS['speedtestHeaderToggle'],
@@ -2751,12 +2753,10 @@ function getNetdata()
 		$api = [];
 		$api = [];
 		$api['data'] = [];
 		$api['data'] = [];
 		$api['url'] = $GLOBALS['netdataURL'];
 		$api['url'] = $GLOBALS['netdataURL'];
-
 		$url = qualifyURL($GLOBALS['netdataURL']);
 		$url = qualifyURL($GLOBALS['netdataURL']);
-
-		for($i = 1; $i < 7; $i++) {
-			if($GLOBALS['netdata'.($i).'Enabled']) {
-				switch($GLOBALS['netdata'.$i.'Data']) {
+		for ($i = 1; $i < 8; $i++) {
+			if ($GLOBALS['netdata' . ($i) . 'Enabled']) {
+				switch ($GLOBALS['netdata' . $i . 'Data']) {
 					case 'disk-read':
 					case 'disk-read':
 						$data = disk('in', $url);
 						$data = disk('in', $url);
 						break;
 						break;
@@ -2779,29 +2779,49 @@ function getNetdata()
 					case 'ram-used':
 					case 'ram-used':
 						$data = ram($url);
 						$data = ram($url);
 						break;
 						break;
+					case 'swap-used':
+						$data = swap($url);
+						break;
+					case 'disk-avail':
+						$data = diskSpace('avail', $url);
+						break;
+					case 'disk-used':
+						$data = diskSpace('used', $url);
+						break;
 					case 'ipmi-temp-c':
 					case 'ipmi-temp-c':
 						$data = ipmiTemp($url, 'c');
 						$data = ipmiTemp($url, 'c');
 						break;
 						break;
 					case 'ipmi-temp-f':
 					case 'ipmi-temp-f':
 						$data = ipmiTemp($url, 'f');
 						$data = ipmiTemp($url, 'f');
 						break;
 						break;
+					case 'cpu-temp-c':
+						$data = cpuTemp($url, 'c');
+						break;
+					case 'cpu-temp-f':
+						$data = cpuTemp($url, 'f');
+						break;
+					case 'custom':
+						$data = customNetdata($url, $i);
+						break;
 					default:
 					default:
 						$data = [
 						$data = [
 							'title' => 'DNC',
 							'title' => 'DNC',
 							'value' => 0,
 							'value' => 0,
 							'units' => 'N/A',
 							'units' => 'N/A',
+							'max' => 100,
 						];
 						];
 						break;
 						break;
 				}
 				}
-
-				$data['title'] = $GLOBALS['netdata'.$i.'Title'];
-				$data['colour'] = $GLOBALS['netdata'.$i.'Colour'];
-				$data['chart'] = $GLOBALS['netdata'.$i.'Chart'];
-
+				$data['title'] = $GLOBALS['netdata' . $i . 'Title'];
+				$data['colour'] = $GLOBALS['netdata' . $i . 'Colour'];
+				$data['chart'] = $GLOBALS['netdata' . $i . 'Chart'];
+				$data['size'] = $GLOBALS['netdata' . $i . 'Size'];
+				$data['lg'] = $GLOBALS['netdata' . ($i) . 'lg'];
+				$data['md'] = $GLOBALS['netdata' . ($i) . 'md'];
+				$data['sm'] = $GLOBALS['netdata' . ($i) . 'sm'];
 				array_push($api['data'], $data);
 				array_push($api['data'], $data);
 			}
 			}
 		}
 		}
-
 		$api = isset($api) ? $api : false;
 		$api = isset($api) ? $api : false;
 		return $api;
 		return $api;
 	}
 	}
@@ -3129,8 +3149,8 @@ function testAPIConnection($array)
 					'account_suffix' => (empty($GLOBALS['authBackendHostSuffix'])) ? null : $GLOBALS['authBackendHostSuffix'],
 					'account_suffix' => (empty($GLOBALS['authBackendHostSuffix'])) ? null : $GLOBALS['authBackendHostSuffix'],
 					'port' => $ldapPort,
 					'port' => $ldapPort,
 					'follow_referrals' => false,
 					'follow_referrals' => false,
-					'use_ssl' => false,
-					'use_tls' => false,
+					'use_ssl' => $GLOBALS['ldapSSL'],
+					'use_tls' => $GLOBALS['ldapTLS'],
 					'version' => 3,
 					'version' => 3,
 					'timeout' => 5,
 					'timeout' => 5,
 					// Custom LDAP Options
 					// Custom LDAP Options
@@ -3207,8 +3227,8 @@ function testAPIConnection($array)
 					'account_suffix' => '',
 					'account_suffix' => '',
 					'port' => $ldapPort,
 					'port' => $ldapPort,
 					'follow_referrals' => false,
 					'follow_referrals' => false,
-					'use_ssl' => false,
-					'use_tls' => false,
+					'use_ssl' => $GLOBALS['ldapSSL'],
+					'use_tls' => $GLOBALS['ldapTLS'],
 					'version' => 3,
 					'version' => 3,
 					'timeout' => 5,
 					'timeout' => 5,
 					// Custom LDAP Options
 					// Custom LDAP Options

+ 3 - 293
api/functions/homepage-functions.php

@@ -2619,7 +2619,7 @@ function getHomepageList()
 		),
 		),
 		array(
 		array(
 			'name' => 'Tautulli',
 			'name' => 'Tautulli',
-			'enabled' => true,
+			'enabled' => (strpos('personal', $GLOBALS['license']) !== false) ? true : false,
 			'image' => 'plugins/images/tabs/tautulli.png',
 			'image' => 'plugins/images/tabs/tautulli.png',
 			'category' => 'Monitor',
 			'category' => 'Monitor',
 			'settings' => array(
 			'settings' => array(
@@ -2940,7 +2940,7 @@ function getHomepageList()
 						'type' => 'html',
 						'type' => 'html',
 						'override' => 6,
 						'override' => 6,
 						'label' => 'Info',
 						'label' => 'Info',
-						'html' => '<p>This homepage item requires <a href="https://github.com/henrywhitaker3/Speedtest-Tracker" target="_blank" rel="noreferrer noopener">Speedtest-Tracker</a> to be running on your network.</p>'
+						'html' => '<p>This homepage item requires <a href="https://github.com/henrywhitaker3/Speedtest-Tracker" target="_blank" rel="noreferrer noopener">Speedtest-Tracker <i class="fa fa-external-link" aria-hidden="true"></i></a> to be running on your network.</p>'
 					),
 					),
 					array(
 					array(
 						'type' => 'switch',
 						'type' => 'switch',
@@ -2983,297 +2983,7 @@ function getHomepageList()
 				),
 				),
 			)
 			)
 		),
 		),
-		array(
-			'name' => 'Netdata',
-			'enabled' => true,
-			'image' => 'plugins/images/tabs/netdata.png',
-			'category' => 'Monitor',
-			'settings' => array(
-				'Enable' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'homepageNetdataEnabled',
-						'label' => 'Enable',
-						'value' => $GLOBALS['homepageNetdataEnabled']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageNetdataAuth',
-						'label' => 'Minimum Authentication',
-						'value' => $GLOBALS['homepageNetdataAuth'],
-						'options' => $groups
-					)
-				),
-				'Connection' => array(
-					array(
-						'type' => 'html',
-						'override' => 12,
-						'label' => 'Info',
-						'html' => 'The URL needs to be on the same domain as your Organizr, and be proxied by subdomain. E.g. If Organizr is accessed at: https://domain.com, then your URL for netdata should be: https://netdata.domain.com'
-					),
-					array(
-						'type' => 'input',
-						'name' => 'netdataURL',
-						'label' => 'URL',
-						'value' => $GLOBALS['netdataURL'],
-						'help' => 'Please enter the local IP:PORT of your netdata instance'
-					),
-					array(
-						'type' => 'blank',
-						'label' => ''
-					),
-				),
-				'Chart 1' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'netdata1Enabled',
-						'label' => 'Enable',
-						'value' => $GLOBALS['netdata1Enabled']
-					),
-					array(
-						'type' => 'blank',
-						'label' => ''
-					),
-					array(
-						'type' => 'input',
-						'name' => 'netdata1Title',
-						'label' => 'Title',
-						'value' => $GLOBALS['netdata1Title'],
-						'help' => 'Title for the netdata graph'
-					),
-					array(
-						'type' => 'select',
-						'name' => 'netdata1Data',
-						'label' => 'Data',
-						'value' => $GLOBALS['netdata1Data'],
-						'options' => netdataOptions(),
-					),
-					array(
-						'type' => 'select',
-						'name' => 'netdata1Chart',
-						'label' => 'Chart',
-						'value' => $GLOBALS['netdata1Chart'],
-						'options' => netdataChartOptions(),
-					),
-					array(
-						'type' => 'select',
-						'name' => 'netdata1Colour',
-						'label' => 'Colour',
-						'value' => $GLOBALS['netdata1Colour'],
-						'options' => netdataColourOptions(),
-					),
-				),
-				'Chart 2' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'netdata2Enabled',
-						'label' => 'Enable',
-						'value' => $GLOBALS['netdata2Enabled']
-					),
-					array(
-						'type' => 'blank',
-						'label' => ''
-					),
-					array(
-						'type' => 'input',
-						'name' => 'netdata2Title',
-						'label' => 'Title',
-						'value' => $GLOBALS['netdata2Title'],
-						'help' => 'Title for the netdata graph'
-					),
-					array(
-						'type' => 'select',
-						'name' => 'netdata2Data',
-						'label' => 'Data',
-						'value' => $GLOBALS['netdata2Data'],
-						'options' => netdataOptions(),
-					),
-					array(
-						'type' => 'select',
-						'name' => 'netdata2Chart',
-						'label' => 'Chart',
-						'value' => $GLOBALS['netdata2Chart'],
-						'options' => netdataChartOptions(),
-					),
-					array(
-						'type' => 'select',
-						'name' => 'netdata2Colour',
-						'label' => 'Colour',
-						'value' => $GLOBALS['netdata2Colour'],
-						'options' => netdataColourOptions(),
-					),
-				),
-				'Chart 3' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'netdata3Enabled',
-						'label' => 'Enable',
-						'value' => $GLOBALS['netdata3Enabled']
-					),
-					array(
-						'type' => 'blank',
-						'label' => ''
-					),
-					array(
-						'type' => 'input',
-						'name' => 'netdata3Title',
-						'label' => 'Title',
-						'value' => $GLOBALS['netdata3Title'],
-						'help' => 'Title for the netdata graph'
-					),
-					array(
-						'type' => 'select',
-						'name' => 'netdata3Data',
-						'label' => 'Data',
-						'value' => $GLOBALS['netdata3Data'],
-						'options' => netdataOptions(),
-					),
-					array(
-						'type' => 'select',
-						'name' => 'netdata3Chart',
-						'label' => 'Data',
-						'value' => $GLOBALS['netdata3Chart'],
-						'options' => netdataChartOptions(),
-					),
-					array(
-						'type' => 'select',
-						'name' => 'netdata3Colour',
-						'label' => 'Colour',
-						'value' => $GLOBALS['netdata3Colour'],
-						'options' => netdataColourOptions(),
-					),
-				),
-				'Chart 4' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'netdata4Enabled',
-						'label' => 'Enable',
-						'value' => $GLOBALS['netdata4Enabled']
-					),
-					array(
-						'type' => 'blank',
-						'label' => ''
-					),
-					array(
-						'type' => 'input',
-						'name' => 'netdata4Title',
-						'label' => 'Title',
-						'value' => $GLOBALS['netdata4Title'],
-						'help' => 'Title for the netdata graph'
-					),
-					array(
-						'type' => 'select',
-						'name' => 'netdata4Data',
-						'label' => 'Data',
-						'value' => $GLOBALS['netdata4Data'],
-						'options' => netdataOptions(),
-					),
-					array(
-						'type' => 'select',
-						'name' => 'netdata4Chart',
-						'label' => 'Chart',
-						'value' => $GLOBALS['netdata4Chart'],
-						'options' => netdataChartOptions(),
-					),
-					array(
-						'type' => 'select',
-						'name' => 'netdata4Colour',
-						'label' => 'Colour',
-						'value' => $GLOBALS['netdata4Colour'],
-						'options' => netdataColourOptions(),
-					),
-				),
-				'Chart 5' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'netdata5Enabled',
-						'label' => 'Enable',
-						'value' => $GLOBALS['netdata5Enabled']
-					),
-					array(
-						'type' => 'blank',
-						'label' => ''
-					),
-					array(
-						'type' => 'input',
-						'name' => 'netdata5Title',
-						'label' => 'Title',
-						'value' => $GLOBALS['netdata5Title'],
-						'help' => 'Title for the netdata graph'
-					),
-					array(
-						'type' => 'select',
-						'name' => 'netdata5Data',
-						'label' => 'Data',
-						'value' => $GLOBALS['netdata5Data'],
-						'options' => netdataOptions(),
-					),
-					array(
-						'type' => 'select',
-						'name' => 'netdata5Chart',
-						'label' => 'Chart',
-						'value' => $GLOBALS['netdata5Chart'],
-						'options' => netdataChartOptions(),
-					),
-					array(
-						'type' => 'select',
-						'name' => 'netdata5Colour',
-						'label' => 'Colour',
-						'value' => $GLOBALS['netdata5Colour'],
-						'options' => netdataColourOptions(),
-					),
-				),
-				'Chart 6' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'netdata6Enabled',
-						'label' => 'Enable',
-						'value' => $GLOBALS['netdata6Enabled']
-					),
-					array(
-						'type' => 'blank',
-						'label' => ''
-					),
-					array(
-						'type' => 'input',
-						'name' => 'netdata6Title',
-						'label' => 'Title',
-						'value' => $GLOBALS['netdata6Title'],
-						'help' => 'Title for the netdata graph'
-					),
-					array(
-						'type' => 'select',
-						'name' => 'netdata6Data',
-						'label' => 'Data',
-						'value' => $GLOBALS['netdata6Data'],
-						'options' => netdataOptions(),
-					),
-					array(
-						'type' => 'select',
-						'name' => 'netdata6Chart',
-						'label' => 'Chart',
-						'value' => $GLOBALS['netdata6Chart'],
-						'options' => netdataChartOptions(),
-					),
-					array(
-						'type' => 'select',
-						'name' => 'netdata6Colour',
-						'label' => 'Colour',
-						'value' => $GLOBALS['netdata6Colour'],
-						'options' => netdataColourOptions(),
-					),
-				),
-				'Options' => array(
-					array(
-						'type' => 'select',
-						'name' => 'homepageNetdataRefresh',
-						'label' => 'Refresh Seconds',
-						'value' => $GLOBALS['homepageNetdataRefresh'],
-						'options' => optionTime()
-					),
-				),
-			)
-		),
+		netdataSettngsArray()
 	);
 	);
 }
 }
 
 

+ 354 - 14
api/functions/netdata-functions.php

@@ -1,5 +1,215 @@
 <?php
 <?php
 
 
+function netdataSettngsArray()
+{
+    $array = array(
+        'name' => 'Netdata',
+        'enabled' => true,
+        'image' => 'plugins/images/tabs/netdata.png',
+        'category' => 'Monitor',
+        'settings' => array(
+            'Enable' => array(
+                array(
+                    'type' => 'switch',
+                    'name' => 'homepageNetdataEnabled',
+                    'label' => 'Enable',
+                    'value' => $GLOBALS['homepageNetdataEnabled']
+                ),
+                array(
+                    'type' => 'select',
+                    'name' => 'homepageNetdataAuth',
+                    'label' => 'Minimum Authentication',
+                    'value' => $GLOBALS['homepageNetdataAuth'],
+                    'options' => groupSelect()
+                )
+            ),
+            'Connection' => array(
+                array(
+                    'type' => 'html',
+                    'override' => 12,
+                    'label' => 'Info',
+                    'html' => 'The URL needs to be on the same domain as your Organizr, and be proxied by subdomain. E.g. If Organizr is accessed at: https://domain.com, then your URL for netdata should be: https://netdata.domain.com'
+                ),
+                array(
+                    'type' => 'input',
+                    'name' => 'netdataURL',
+                    'label' => 'URL',
+                    'value' => $GLOBALS['netdataURL'],
+                    'help' => 'Please enter the local IP:PORT of your netdata instance'
+                ),
+                array(
+                    'type' => 'blank',
+                    'label' => ''
+                ),
+            ),
+        )
+    );
+
+    for($i = 1; $i <= 7; $i++) {
+        $array['settings']['Chart '.$i] = array(
+            array(
+                'type' => 'switch',
+                'name' => 'netdata'.$i.'Enabled',
+                'label' => 'Enable',
+                'value' => $GLOBALS['netdata'.$i.'Enabled']
+            ),
+            array(
+                'type' => 'blank',
+                'label' => ''
+            ),
+            array(
+                'type' => 'input',
+                'name' => 'netdata'.$i.'Title',
+                'label' => 'Title',
+                'value' => $GLOBALS['netdata'.$i.'Title'],
+                'help' => 'Title for the netdata graph'
+            ),
+            array(
+                'type' => 'select',
+                'name' => 'netdata'.$i.'Data',
+                'label' => 'Data',
+                'value' => $GLOBALS['netdata'.$i.'Data'],
+                'options' => netdataOptions(),
+            ),
+            array(
+                'type' => 'select',
+                'name' => 'netdata'.$i.'Chart',
+                'label' => 'Chart',
+                'value' => $GLOBALS['netdata'.$i.'Chart'],
+                'options' => netdataChartOptions(),
+            ),
+            array(
+                'type' => 'select',
+                'name' => 'netdata'.$i.'Colour',
+                'label' => 'Colour',
+                'value' => $GLOBALS['netdata'.$i.'Colour'],
+                'options' => netdataColourOptions(),
+            ),
+            array(
+                'type' => 'select',
+                'name' => 'netdata'.$i.'Size',
+                'label' => 'Size',
+                'value' => $GLOBALS['netdata'.$i.'Size'],
+                'options' => netdataSizeOptions(),
+            ),
+            array(
+                'type' => 'blank',
+                'label' => ''
+            ),
+            array(
+                'type' => 'switch',
+                'name' => 'netdata'.$i.'lg',
+                'label' => 'Show on large screens',
+                'value' => $GLOBALS['netdata'.$i.'lg']
+            ),
+            array(
+                'type' => 'switch',
+                'name' => 'netdata'.$i.'md',
+                'label' => 'Show on medium screens',
+                'value' => $GLOBALS['netdata'.$i.'md']
+            ),
+            array(
+                'type' => 'switch',
+                'name' => 'netdata'.$i.'sm',
+                'label' => 'Show on small screens',
+                'value' => $GLOBALS['netdata'.$i.'sm']
+            ),
+        );
+    }
+
+    $array['settings']['Custom data'] = [
+        [
+            'type' => 'html',
+            'label' => '',
+            'override' => 12,
+            'html' => <<<HTML
+            <div>
+                <p>This is where you can define custom data sources for your netdata charts. To use a custom source, you need to select 'Custom' in the data field for the chart.</p>
+                <p>To define a custom data source, you need to add an entry to the JSON below, where the key is the chart number you want the custom data to be used for. Here is an example to set chart 1's custom data source to RAM percentage:</p>
+                <pre>{
+                "1": {
+                    "url": "/api/v1/data?chart=system.ram&format=array&points=540&group=average&gtime=0&options=absolute|percentage|jsonwrap|nonzero&after=-540&dimensions=used|buffers|active|wired",
+                    "value": "result,0",
+                    "units": "%",
+                    "max": 100
+                }
+            }</pre>
+                <p>The URL is appended to your netdata URL and returns JSON formatted data. The value field tells Organizr how to return the value you want from the netdata API. This should be formatted as comma-separated keys to access the desired value.</p>
+                <table class="table table-striped">
+                    <thead>
+                        <tr>
+                            <th>Parameter</th>
+                            <th>Description</th>
+                            <th>Required</th>
+                        </tr>
+                    </thead>
+                    <tbody>
+                        <tr>
+                            <td>url</td>
+                            <td>Specifies the netdata API endpoint</td>
+                            <td><i class="fa fa-check text-success" aria-hidden="true"></i></td>
+                        </tr>
+                        <tr>
+                            <td>value</td>
+                            <td>Specifies the selector used to get the data form the netdata response</td>
+                            <td><i class="fa fa-check text-success" aria-hidden="true"></i></td>
+                        </tr>
+                        <tr>
+                            <td>units</td>
+                            <td>Specifies the units shown in the graph/chart. Defaults to %</td>
+                            <td><i class="fa fa-times text-danger" aria-hidden="true"></i></td>
+                        </tr>
+                        <tr>
+                            <td>max</td>
+                            <td>Specifies the maximum possible value for the data. Defaults to 100</td>
+                            <td><i class="fa fa-times text-danger" aria-hidden="true"></i></td>
+                        </tr>
+                        <tr>
+                            <td>mutator</td>
+                            <td>Used to perform simple mathematical operations on the result (+, -, /, *). For example: dividing the result by 1000 would be '/1000'. These operations can be chained together by putting them in a comma-seprated format.</td>
+                            <td><i class="fa fa-times text-danger" aria-hidden="true"></i></td>
+                        </tr>
+                        <tr>
+                            <td>netdata</td>
+                            <td>Can be used to override the netdata instance data is retrieved from (in the format: http://IP:PORT)</td>
+                            <td><i class="fa fa-times text-danger" aria-hidden="true"></i></td>
+                        </tr>
+                    </tbody>
+                </table>
+            </div>
+            HTML
+        ],
+        [
+            'type' => 'html',
+            'name' => 'netdataCustomTextAce',
+            'class' => 'jsonTextarea hidden',
+            'label' => 'Custom definitions',
+            'override' => 12,
+            'html' => '<div id="netdataCustomTextAce" style="height: 300px;">' . htmlentities($GLOBALS['netdataCustom']) . '</div>',
+        ],
+        [
+            'type' => 'textbox',
+            'name' => 'netdataCustom',
+            'class' => 'jsonTextarea hidden',
+            'id' => 'netdataCustomText',
+            'label' => '',
+            'value' => $GLOBALS['netdataCustom'],
+        ]
+    ];
+
+    $array['settings']['Options'] =  array(
+        array(
+            'type' => 'select',
+            'name' => 'homepageNetdataRefresh',
+            'label' => 'Refresh Seconds',
+            'value' => $GLOBALS['homepageNetdataRefresh'],
+            'options' => optionTime()
+        ),
+    );
+
+    return $array;
+}
+
 function disk($dimension, $url)
 function disk($dimension, $url)
 {
 {
     $data = [];
     $data = [];
@@ -10,8 +220,30 @@ function disk($dimension, $url)
         if ($response->success) {
         if ($response->success) {
             $json = json_decode($response->body, true);
             $json = json_decode($response->body, true);
             $data['value'] = $json['latest_values'][0] / 1000;
             $data['value'] = $json['latest_values'][0] / 1000;
-            $data['percent'] = ($json['latest_values'][0] / $json['max']) * 100;
+            $data['percent'] =  getPercent($json['latest_values'][0], $json['max']);
             $data['units'] = 'MiB/s';
             $data['units'] = 'MiB/s';
+            $data['max'] = $json['max'];
+        }
+    } catch (Requests_Exception $e) {
+        writeLog('error', 'Netdata Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
+    };
+
+    return $data;
+}
+
+function diskSpace($dimension, $url)
+{
+    $data = [];
+    // Get Data
+    $dataUrl = $url . '/api/v1/data?chart=disk_space._&format=json&points=509&group=average&gtime=0&options=ms|jsonwrap|nonzero&after=-540&dimension='.$dimension;
+    try {
+        $response = Requests::get($dataUrl);
+        if ($response->success) {
+            $json = json_decode($response->body, true);
+            $data['value'] = $json['result']['data'][0][1];
+            $data['percent'] = $data['value'];
+            $data['units'] = '%';
+            $data['max'] = 100;
         }
         }
     } catch (Requests_Exception $e) {
     } catch (Requests_Exception $e) {
         writeLog('error', 'Netdata Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
         writeLog('error', 'Netdata Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
@@ -31,8 +263,9 @@ function net($dimension, $url)
         if ($response->success) {
         if ($response->success) {
             $json = json_decode($response->body, true);
             $json = json_decode($response->body, true);
             $data['value'] = $json['latest_values'][0] / 1000;
             $data['value'] = $json['latest_values'][0] / 1000;
-            $data['percent'] = ($json['latest_values'][0] / $json['max']) * 100;
-            $data['units'] = 'megabits/s';
+            $data['percent'] = getPercent($json['latest_values'][0], $json['max']);
+            $data['units'] = 'Mbit/s';
+            $data['max'] = $json['max'];
         }
         }
     } catch (Requests_Exception $e) {
     } catch (Requests_Exception $e) {
         writeLog('error', 'Netdata Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
         writeLog('error', 'Netdata Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
@@ -81,28 +314,135 @@ function ram($url)
     return $data;
     return $data;
 }
 }
 
 
-function ipmiTemp($url, $unit)
+function swap($url)
 {
 {
     $data = [];
     $data = [];
-    $dataUrl = $url . '/api/v1/data?chart=ipmi.temperatures_c&format=array&points=540&group=average&gtime=0&options=absolute|jsonwrap|nonzero&after=-540';
+    $dataUrl = $url . '/api/v1/data?chart=system.swap&format=array&points=540&group=average&gtime=0&options=absolute|percentage|jsonwrap|nonzero&after=-540&dimensions=used';
     try {
     try {
         $response = Requests::get($dataUrl);
         $response = Requests::get($dataUrl);
         if ($response->success) {
         if ($response->success) {
             $json = json_decode($response->body, true);
             $json = json_decode($response->body, true);
             $data['value'] = $json['result'][0];
             $data['value'] = $json['result'][0];
-            if($unit == 'c') {
-                $data['percent'] = ($data['value'] / 50) * 100;
-                $data['max'] = 50;
-            } else if($unit == 'f') {
-                $data['value'] = ($data['value'] * 9/5) + 32;
-                $data['percent'] = ($data['value'] / 122) * 100;
-                $data['max'] = 122;
-            }
-            $data['units'] = '°'.strtoupper($unit);
+            $data['percent'] = $data['value'];
+            $data['max'] = 100;
+            $data['units'] = '%';
         }
         }
     } catch (Requests_Exception $e) {
     } catch (Requests_Exception $e) {
         writeLog('error', 'Netdata Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
         writeLog('error', 'Netdata Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
     };
     };
 
 
     return $data;
     return $data;
+}
+
+function getPercent($val, $max)
+{
+    if($max == 0) {
+        return 0;
+    } else {
+        return ( $val / $max ) * 100;
+    }
+}
+
+function customNetdata($url, $id)
+{
+    try {
+        $customs = json_decode($GLOBALS['netdataCustom'], true, 512, JSON_THROW_ON_ERROR);
+    } catch(Exception $e) {
+        $customs = false;
+    }
+        
+    if($customs == false) {
+        return [
+            'error' => 'unable to parse custom JSON'
+        ];
+    } else if(!isset($customs[$id])) {
+        return [
+            'error' => 'custom definition not found'
+        ];
+    } else {
+        $data = [];
+        $custom = $customs[$id];
+
+        if( isset($custom['url']) && isset($custom['value']) ) {
+            if( isset($custom['netdata']) && $custom['netdata'] != '' ) {
+                $url = qualifyURL($custom['netdata']);
+            }
+            $dataUrl = $url . '/' . $custom['url'];
+            try {
+                $response = Requests::get($dataUrl);
+                if ($response->success) {
+                    $json = json_decode($response->body, true);
+
+                    if( !isset($custom['max']) || $custom['max'] == '' ) {
+                        $custom['max'] = 100;
+                    }
+                    $data['max'] = $custom['max'];
+
+                    if( !isset($custom['units']) || $custom['units'] == '' ) {
+                        $custom['units'] = '%';
+                    }
+                    $data['units'] = $custom['units'];
+    
+                    $selectors = explode(',', $custom['value']);
+                    foreach($selectors as $selector) {
+                        if(is_numeric($selector)) {
+                            $selector = (int) $selector;
+                        }
+                        if(!isset($data['value'])) {
+                            $data['value'] = $json[$selector];
+                        } else {
+                            $data['value'] = $data['value'][$selector];
+                        }
+                    }
+
+                    if(isset($custom['mutator'])) {
+                        $data['value'] = parseMutators($data['value'], $custom['mutator']);
+                    }
+    
+                    if($data['max'] == 0) {
+                        $data['percent'] = 0;
+                    } else {
+                        $data['percent'] = ( $data['value'] / $data['max'] ) * 100;
+                    }
+                }
+            } catch (Requests_Exception $e) {
+                writeLog('error', 'Netdata Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
+            };
+        } else {
+            $data['error'] = 'custom definition incomplete';
+        }
+    
+        return $data;
+    }
+}
+
+function parseMutators($val, $mutators)
+{
+    $mutators = explode(',', $mutators);
+    foreach($mutators as $m) {
+        $op = $m[0];
+        try {
+            $m = (float) substr($m, 1);
+            switch($op) {
+                case '+':
+                    $val = $val + $m;
+                    break;
+                case '-':
+                    $val = $val - $m;
+                    break;
+                case '/':
+                    $val = $val / $m;
+                    break;
+                case '*':
+                    $val = $val * $m;
+                    break;
+                default:
+                    break;
+            }
+        } catch(Exception $e) {
+            //
+        }
+    }
+
+    return $val;
 }
 }

+ 1 - 0
api/functions/normal-functions.php

@@ -79,6 +79,7 @@ function parseDomain($value, $force = false)
 // Cookie Custom Function
 // Cookie Custom Function
 function coookie($type, $name, $value = '', $days = -1, $http = true)
 function coookie($type, $name, $value = '', $days = -1, $http = true)
 {
 {
+	$days = ($days > 365) ? 365 : $days;
 	if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == "https") {
 	if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == "https") {
 		$Secure = true;
 		$Secure = true;
 		$HTTPOnly = true;
 		$HTTPOnly = true;

+ 31 - 5
api/functions/option-functions.php

@@ -176,13 +176,21 @@ function netdataOptions()
 			'value' => 'ram-used',
 			'value' => 'ram-used',
 		],
 		],
 		[
 		[
-			'name' => 'IPMI Temperature C',
-			'value' => 'ipmi-temp-c'
+			'name' => 'Used Swap',
+			'value' => 'swap-used',
 		],
 		],
 		[
 		[
-			'name' => 'IPMI Temperature F',
-			'value' => 'ipmi-temp-f'
+			'name' => 'Disk space used',
+			'value' => 'disk-used',
 		],
 		],
+		[
+			'name' => 'Disk space available',
+			'value' => 'disk-avail',
+		],
+		[
+			'name' => 'Custom',
+			'value' => 'custom',
+		]
 	];
 	];
 }
 }
 
 
@@ -209,7 +217,7 @@ function netdataColourOptions()
 		],
 		],
 		[
 		[
 			'name' => 'Green',
 			'name' => 'Green',
-			'value' => 'DD4400',
+			'value' => '46e302',
 		],
 		],
 		[
 		[
 			'name' => 'Purple',
 			'name' => 'Purple',
@@ -228,4 +236,22 @@ function netdataColourOptions()
 			'value' => 'd66300',
 			'value' => 'd66300',
 		]
 		]
 	];
 	];
+}
+
+function netdataSizeOptions()
+{
+	return [
+		[
+			'name' => 'Large',
+			'value' => 'lg',
+		],
+		[
+			'name' => 'Medium',
+			'value' => 'md',
+		],
+		[
+			'name' => 'Small',
+			'value' => 'sm'
+		]
+	];
 }
 }

+ 74 - 7
api/functions/organizr-functions.php

@@ -131,6 +131,11 @@ function organizrSpecialSettings()
 			'debugArea' => qualifyRequest($GLOBALS['debugAreaAuth']),
 			'debugArea' => qualifyRequest($GLOBALS['debugAreaAuth']),
 			'debugErrors' => $GLOBALS['debugErrors'],
 			'debugErrors' => $GLOBALS['debugErrors'],
 			'sandbox' => $GLOBALS['sandbox'],
 			'sandbox' => $GLOBALS['sandbox'],
+		),
+		'menuLink' => array(
+			'githubMenuLink' => $GLOBALS['githubMenuLink'],
+			'organizrSupportMenuLink' => $GLOBALS['organizrSupportMenuLink'],
+			'organizrDocsMenuLink' => $GLOBALS['organizrDocsMenuLink']
 		)
 		)
 	);
 	);
 }
 }
@@ -684,6 +689,22 @@ function getSettingsMain()
 				'label' => 'Account DN',
 				'label' => 'Account DN',
 				'html' => '<span id="accountDN" class="ldapAuth switchAuth">' . $GLOBALS['authBackendHostPrefix'] . 'TestAcct' . $GLOBALS['authBackendHostSuffix'] . '</span>'
 				'html' => '<span id="accountDN" class="ldapAuth switchAuth">' . $GLOBALS['authBackendHostPrefix'] . 'TestAcct' . $GLOBALS['authBackendHostSuffix'] . '</span>'
 			),
 			),
+			array(
+				'type' => 'switch',
+				'name' => 'ldapSSL',
+				'class' => 'ldapAuth switchAuth',
+				'label' => 'Enable LDAP SSL',
+				'value' => $GLOBALS['ldapSSL'],
+				'help' => 'This will enable the use of SSL for LDAP connections'
+			),
+			array(
+				'type' => 'switch',
+				'name' => 'ldapSSL',
+				'class' => 'ldapAuth switchAuth',
+				'label' => 'Enable LDAP TLS',
+				'value' => $GLOBALS['ldapTLS'],
+				'help' => 'This will enable the use of TLS for LDAP connections'
+			),
 			array(
 			array(
 				'type' => 'button',
 				'type' => 'button',
 				'name' => 'test-button-ldap',
 				'name' => 'test-button-ldap',
@@ -825,6 +846,10 @@ function getSettingsMain()
 						'name' => 'Allow Top Navigation',
 						'name' => 'Allow Top Navigation',
 						'value' => 'allow-top-navigation'
 						'value' => 'allow-top-navigation'
 					),
 					),
+					array(
+						'name' => 'Allow Downloads',
+						'value' => 'allow-downloads'
+					),
 				)
 				)
 			),
 			),
 			array(
 			array(
@@ -1236,6 +1261,24 @@ function getCustomizeAppearance()
 					'label' => 'Show Debug Errors',
 					'label' => 'Show Debug Errors',
 					'value' => $GLOBALS['debugErrors']
 					'value' => $GLOBALS['debugErrors']
 				),
 				),
+				array(
+					'type' => 'switch',
+					'name' => 'githubMenuLink',
+					'label' => 'Show GitHub Repo Link',
+					'value' => $GLOBALS['githubMenuLink']
+				),
+				array(
+					'type' => 'switch',
+					'name' => 'organizrSupportMenuLink',
+					'label' => 'Show Organizr Support Link',
+					'value' => $GLOBALS['organizrSupportMenuLink']
+				),
+				array(
+					'type' => 'switch',
+					'name' => 'organizrDocsMenuLink',
+					'label' => 'Show Organizr Docs Link',
+					'value' => $GLOBALS['organizrDocsMenuLink']
+				),
 				array(
 				array(
 					'type' => 'select',
 					'type' => 'select',
 					'name' => 'unsortedTabs',
 					'name' => 'unsortedTabs',
@@ -1787,16 +1830,35 @@ function showoAuth()
 
 
 function getImages()
 function getImages()
 {
 {
+	$allIconsPrep = array();
+	$allIcons = array();
+	$ignore = array(".", "..", "._.DS_Store", ".DS_Store", ".pydio_id", "index.html");
 	$dirname = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . 'tabs' . DIRECTORY_SEPARATOR;
 	$dirname = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . 'tabs' . DIRECTORY_SEPARATOR;
 	$path = 'plugins/images/tabs/';
 	$path = 'plugins/images/tabs/';
 	$images = scandir($dirname);
 	$images = scandir($dirname);
-	$ignore = array(".", "..", "._.DS_Store", ".DS_Store", ".pydio_id");
-	$allIcons = array();
 	foreach ($images as $image) {
 	foreach ($images as $image) {
 		if (!in_array($image, $ignore)) {
 		if (!in_array($image, $ignore)) {
-			$allIcons[] = $path . $image;
+			$allIconsPrep[$image] = array(
+				'path' => $path,
+				'name' => $image
+			);
 		}
 		}
 	}
 	}
+	$dirname = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . 'userTabs' . DIRECTORY_SEPARATOR;
+	$path = 'plugins/images/userTabs/';
+	$images = scandir($dirname);
+	foreach ($images as $image) {
+		if (!in_array($image, $ignore)) {
+			$allIconsPrep[$image] = array(
+				'path' => $path,
+				'name' => $image
+			);
+		}
+	}
+	ksort($allIconsPrep);
+	foreach ($allIconsPrep as $item) {
+		$allIcons[] = $item['path'] . $item['name'];
+	}
 	return $allIcons;
 	return $allIcons;
 }
 }
 
 
@@ -1817,7 +1879,7 @@ function editImages()
 	$array = array();
 	$array = array();
 	$postCheck = array_filter($_POST);
 	$postCheck = array_filter($_POST);
 	$filesCheck = array_filter($_FILES);
 	$filesCheck = array_filter($_FILES);
-	$approvedPath = 'plugins/images/tabs/';
+	$approvedPath = 'plugins/images/userTabs/';
 	if (!empty($postCheck)) {
 	if (!empty($postCheck)) {
 		$removeImage = $approvedPath . pathinfo($_POST['data']['imagePath'], PATHINFO_BASENAME);
 		$removeImage = $approvedPath . pathinfo($_POST['data']['imagePath'], PATHINFO_BASENAME);
 		if ($_POST['data']['action'] == 'deleteImage' && approvedFileExtension($removeImage)) {
 		if ($_POST['data']['action'] == 'deleteImage' && approvedFileExtension($removeImage)) {
@@ -1831,7 +1893,7 @@ function editImages()
 		ini_set('upload_max_filesize', '10M');
 		ini_set('upload_max_filesize', '10M');
 		ini_set('post_max_size', '10M');
 		ini_set('post_max_size', '10M');
 		$tempFile = $_FILES['file']['tmp_name'];
 		$tempFile = $_FILES['file']['tmp_name'];
-		$targetPath = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . 'tabs' . DIRECTORY_SEPARATOR;
+		$targetPath = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . 'userTabs' . DIRECTORY_SEPARATOR;
 		$targetFile = $targetPath . $_FILES['file']['name'];
 		$targetFile = $targetPath . $_FILES['file']['name'];
 		return (move_uploaded_file($tempFile, $targetFile)) ? true : false;
 		return (move_uploaded_file($tempFile, $targetFile)) ? true : false;
 	}
 	}
@@ -2640,9 +2702,14 @@ function settingsPathChecks()
 
 
 function dockerUpdate()
 function dockerUpdate()
 {
 {
+	$dockerUpdate = null;
 	chdir('/etc/cont-init.d/');
 	chdir('/etc/cont-init.d/');
-	$dockerUpdate = shell_exec('./30-install');
-	return $dockerUpdate;
+	if (file_exists('./30-install')) {
+		$dockerUpdate = shell_exec('./30-install');
+	} elseif (file_exists('./40-install')) {
+		$dockerUpdate = shell_exec('./40-install');
+	}
+	return $dockerUpdate ?? 'Update Failed';
 }
 }
 
 
 function windowsUpdate()
 function windowsUpdate()

+ 1 - 1
api/functions/static-globals.php

@@ -1,7 +1,7 @@
 <?php
 <?php
 // ===================================
 // ===================================
 // Organizr Version
 // Organizr Version
-$GLOBALS['installedVersion'] = '2.0.570';
+$GLOBALS['installedVersion'] = '2.0.631';
 // ===================================
 // ===================================
 // Quick php Version check
 // Quick php Version check
 $GLOBALS['minimumPHP'] = '7.1.3';
 $GLOBALS['minimumPHP'] = '7.1.3';

+ 2 - 1
api/functions/token-functions.php

@@ -54,6 +54,7 @@ function createToken($username, $email, $image, $group, $groupID, $key, $days =
 	if (!isset($GLOBALS['dbLocation']) || !isset($GLOBALS['dbName'])) {
 	if (!isset($GLOBALS['dbLocation']) || !isset($GLOBALS['dbName'])) {
 		return false;
 		return false;
 	}
 	}
+	$days = ($days > 365) ? 365 : $days;
 	//Quick get user ID
 	//Quick get user ID
 	try {
 	try {
 		$database = new Dibi\Connection([
 		$database = new Dibi\Connection([
@@ -138,7 +139,7 @@ function validateToken($token, $global = false)
 			} catch (Dibi\Exception $e) {
 			} catch (Dibi\Exception $e) {
 				$GLOBALS['organizrUser'] = false;
 				$GLOBALS['organizrUser'] = false;
 			}
 			}
-		}else{
+		} else {
 			// Delete cookie & reload page
 			// Delete cookie & reload page
 			coookie('delete', $GLOBALS['cookieName']);
 			coookie('delete', $GLOBALS['cookieName']);
 			$GLOBALS['organizrUser'] = false;
 			$GLOBALS['organizrUser'] = false;

+ 13 - 1
css/organizr.css

@@ -1070,9 +1070,15 @@ input.has-success {
 ul.nav.customtab.nav-tabs.nav-low-margin {
 ul.nav.customtab.nav-tabs.nav-low-margin {
     margin: -25px -25px 0px -25px !important;
     margin: -25px -25px 0px -25px !important;
 }
 }
-i.fa.fa-life-ring.fa-fw {
+#menu-Organizr-Support i {
     color: #C62828;
     color: #C62828;
 }
 }
+#menu-GitHub-Repo i {
+    color: #2cabe3;
+}
+#menu-Organizr-Docs i {
+    color: #707cd2;
+}
 .ping {
 .ping {
     position: relative;
     position: relative;
     margin-top: 0;
     margin-top: 0;
@@ -4387,3 +4393,9 @@ html {
 .noty_layout [data-name~="mojs-shape"] {
 .noty_layout [data-name~="mojs-shape"] {
     pointer-events:none
     pointer-events:none
 }
 }
+.bottom-close-splash {
+    position: absolute;
+    width: 130px;
+    right: 20px;
+    bottom: 20px;
+}

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
css/organizr.min.css


+ 3 - 3
index.php

@@ -1,4 +1,4 @@
-<?php include 'api/functions/static-globals.php';?>
+<?php include 'api/functions/static-globals.php'; ?>
 <!DOCTYPE html>
 <!DOCTYPE html>
 <html lang="en" ontouchmove>
 <html lang="en" ontouchmove>
 
 
@@ -256,7 +256,7 @@
 <script src="plugins/bower_components/jquery-wizard-master/libs/formvalidation/bootstrap.min.js"></script>
 <script src="plugins/bower_components/jquery-wizard-master/libs/formvalidation/bootstrap.min.js"></script>
 <script src="js/bowser.min.js"></script>
 <script src="js/bowser.min.js"></script>
 <script src="js/jasny-bootstrap.js"></script>
 <script src="js/jasny-bootstrap.js"></script>
-<script src="js/cbpFWTabs.js"></script>
+<script src="js/cbpFWTabs.js?v=<?php echo $GLOBALS['fileHash']; ?>"></script>
 <script src="js/js.cookie.js"></script>
 <script src="js/js.cookie.js"></script>
 <script src="js/jquery-lang.js"></script>
 <script src="js/jquery-lang.js"></script>
 <script src="js/jquery-ui.min.js"></script>
 <script src="js/jquery-ui.min.js"></script>
@@ -287,7 +287,7 @@
 <script src="plugins/bower_components/mousetrap/mousetrap.min.js"></script>
 <script src="plugins/bower_components/mousetrap/mousetrap.min.js"></script>
 <script src="plugins/bower_components/bootstrap-treeview-master/dist/bootstrap-treeview.min.js"></script>
 <script src="plugins/bower_components/bootstrap-treeview-master/dist/bootstrap-treeview.min.js"></script>
 <script src="plugins/bower_components/jquery.easy-pie-chart/dist/jquery.easypiechart.min.js"></script>
 <script src="plugins/bower_components/jquery.easy-pie-chart/dist/jquery.easypiechart.min.js"></script>
-<script src="/js/gauge.min.js"></script>
+<script src="js/gauge.min.js"></script>
 <script src="js/jquery.mousewheel.min.js"></script>
 <script src="js/jquery.mousewheel.min.js"></script>
 <script src="js/ua-parser.min.js"></script>
 <script src="js/ua-parser.min.js"></script>
 <script src="js/plyr.js"></script>
 <script src="js/plyr.js"></script>

+ 9 - 5
js/cbpFWTabs.js

@@ -40,11 +40,15 @@
 		// current index
 		// current index
 		this.current = -1;
 		this.current = -1;
 		// show current content item
 		// show current content item
-        if(this.tabs[0].innerHTML.indexOf('#settings') >= 0){
-            this._show(5);
-        }else{
-            this._show();
-        }
+		try{
+			if(this.tabs[0].innerHTML.indexOf('#settings') >= 0){
+				this._show(5);
+			}else{
+				this._show();
+			}
+		}catch{
+			this._show();
+		}
 		// init events
 		// init events
 		this._initEvents();
 		this._initEvents();
 	};
 	};

+ 19 - 0
js/custom.js

@@ -1987,4 +1987,23 @@ $(document).on('click', ".ipInfo", function(){
 $(document).on('click', '.allGroupsList', function() {
 $(document).on('click', '.allGroupsList', function() {
     console.log($(this));
     console.log($(this));
     $(this).toggleClass('active');
     $(this).toggleClass('active');
+});
+// Control init of custom netdata JSON editor
+$(document).on('click', 'li a[aria-controls="Custom data"]', function() {
+    var resizeEditor = function(jsonEditor) {
+        const aceEditor = jsonEditor;
+        const newHeight = aceEditor.getSession().getScreenLength() * (aceEditor.renderer.lineHeight + aceEditor.renderer.scrollBar.getWidth());
+        aceEditor.container.style.height = newHeight + 'px';
+        aceEditor.resize();
+    }
+
+    jsonEditor = ace.edit("netdataCustomTextAce");
+    var JsonMode = ace.require("ace/mode/javascript").Mode;
+    jsonEditor.session.setMode(new JsonMode());
+    jsonEditor.setTheme("ace/theme/idle_fingers");
+    jsonEditor.setShowPrintMargin(false);
+    jsonEditor.session.on('change', function(delta) {
+        $('#netdataCustomText').val(jsonEditor.getValue());
+        $('#customize-appearance-form-save').removeClass('hidden');
+    });
 });
 });

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
js/custom.min.js


+ 263 - 83
js/functions.js

@@ -204,7 +204,7 @@ function isNumberKey(evt) {
     return true;
     return true;
 }
 }
 function setTabInfo(tab,action,value){
 function setTabInfo(tab,action,value){
-    if(tab == 'Organizr-Support'){
+    if(tab == 'Organizr-Support' || tab == 'Organizr-Docs'){
         return false;
         return false;
     }
     }
     if(tab !== null && action !== null && value !== null){
     if(tab !== null && action !== null && value !== null){
@@ -2582,14 +2582,18 @@ function userMenu(user){
 }
 }
 function menuExtras(active){
 function menuExtras(active){
     var supportFrame = buildFrameContainer('Organizr Support','https://organizr.app/support',1);
     var supportFrame = buildFrameContainer('Organizr Support','https://organizr.app/support',1);
-    var adminMenu = (activeInfo.user.groupID <= 1) ? buildMenuList('Organizr Support','https://organizr.app/support',1,'fontawesome::life-ring'): '';
+    var docsFrame = buildFrameContainer('Organizr Docs','https://docs.organizr.app',1);
+    var adminMenu = (activeInfo.user.groupID <= 1 && activeInfo.settings.menuLink.githubMenuLink) ? buildMenuList('GitHub Repo','https://github.com/causefx/organizr',2,'fontawesome::github') : '';
+    adminMenu += (activeInfo.user.groupID <= 1 && activeInfo.settings.menuLink.organizrSupportMenuLink) ? buildMenuList('Organizr Support','https://organizr.app/support',1,'fontawesome::life-ring') : '';
+    adminMenu += (activeInfo.user.groupID <= 1 && activeInfo.settings.menuLink.organizrDocsMenuLink) ? buildMenuList('Organizr Docs','https://docs.organizr.app',1,'simpleline::docs') : '';
+
     $(supportFrame).appendTo($('.iFrame-listing'));
     $(supportFrame).appendTo($('.iFrame-listing'));
+    $(docsFrame).appendTo($('.iFrame-listing'));
 	if(active === true){
 	if(active === true){
 		return `
 		return `
 			<li class="devider"></li>
 			<li class="devider"></li>
 			<li id="sign-out"><a class="waves-effect" onclick="logout();"><i class="fa fa-sign-out fa-fw"></i> <span class="hide-menu" lang="en">Logout</span></a></li>
 			<li id="sign-out"><a class="waves-effect" onclick="logout();"><i class="fa fa-sign-out fa-fw"></i> <span class="hide-menu" lang="en">Logout</span></a></li>
 			<li class="devider"></li>
 			<li class="devider"></li>
-			<li id="github"><a href="https://github.com/causefx/organizr" target="_blank" class="waves-effect"><i class="fa fa-github fa-fw text-success"></i> <span class="hide-menu">GitHub</span></a></li>
 		`+adminMenu;
 		`+adminMenu;
 	}else{
 	}else{
 		return `
 		return `
@@ -2795,7 +2799,7 @@ function buildSplashScreen(json){
         <section id="splashScreen" class="lock-screen splash-screen fade in">
         <section id="splashScreen" class="lock-screen splash-screen fade in">
             <div class="row p-20 flexbox">`+items+`</div>
             <div class="row p-20 flexbox">`+items+`</div>
             <div class="row p-20 p-t-0 flexbox">
             <div class="row p-20 p-t-0 flexbox">
-                <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 col-xl-12 mouse hvr-wobble-bottom" onclick="$('.splash-screen').addClass('hidden').removeClass('in')">
+                <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 col-xl-12 mouse hvr-wobble-bottom bottom-close-splash" onclick="$('.splash-screen').addClass('hidden').removeClass('in')">
                     <div class="homepage-drag fc-event bg-danger lazyload"  data-src="">
                     <div class="homepage-drag fc-event bg-danger lazyload"  data-src="">
                         <span class="homepage-text">&nbsp; Close Splash</span>
                         <span class="homepage-text">&nbsp; Close Splash</span>
                     </div>
                     </div>
@@ -3302,7 +3306,10 @@ function newsLoad(){
         try {
         try {
             var response = JSON.parse(data);
             var response = JSON.parse(data);
             var items = [];
             var items = [];
+            var limit = 5;
+            var count = 0;
             $.each(response, function(i,v) {
             $.each(response, function(i,v) {
+                count++;
                 var newBody = `
                 var newBody = `
                 <h5 class="pull-left">`+moment(v.date).format('LLL')+`</h5>
                 <h5 class="pull-left">`+moment(v.date).format('LLL')+`</h5>
                 <h5 class="pull-right">`+v.author+`</h5>
                 <h5 class="pull-right">`+v.author+`</h5>
@@ -3310,9 +3317,11 @@ function newsLoad(){
                 `+((v.subTitle) ? '<h5>' + v.subTitle + '</h5>' : '' )+`
                 `+((v.subTitle) ? '<h5>' + v.subTitle + '</h5>' : '' )+`
                 <p>`+v.body+`</p>
                 <p>`+v.body+`</p>
                 `;
                 `;
-                items[i] = {
-                    title:v.title,
-                    body:newBody
+                if(count <= limit){
+                    items[i] = {
+                        title:v.title,
+                        body:newBody
+                    }
                 }
                 }
             });
             });
             var body = buildAccordion(items, true);
             var body = buildAccordion(items, true);
@@ -3731,7 +3740,7 @@ function marketplaceJSON(type) {
 }
 }
 function allIcons() {
 function allIcons() {
     return $.ajax({
     return $.ajax({
-        url: "/js/icons.json",
+        url: "js/icons.json",
     });
     });
 }
 }
 function organizrConnect(path){
 function organizrConnect(path){
@@ -4816,7 +4825,7 @@ function buildRequest(array){
 			<div class="white-box m-b-0 search-div resultBox-outside">
 			<div class="white-box m-b-0 search-div resultBox-outside">
 				<div class="form-group m-b-0">
 				<div class="form-group m-b-0">
 					<div id="request-input-div" class="input-group">
 					<div id="request-input-div" class="input-group">
-						<input id="request-input" lang="en" placeholder="Request Show or Movie" type="text" class="form-control inline-focus">
+						<input id="request-input" lang="en" placeholder="Request a Show or Movie" type="text" class="form-control inline-focus">
                         <input id="request-page" type="hidden" class="form-control">
                         <input id="request-page" type="hidden" class="form-control">
                         <div class="input-group-btn">
                         <div class="input-group-btn">
                             <button type="button" class="btn waves-effect waves-light btn-info dropdown-toggle" data-toggle="dropdown" aria-expanded="false"><span lang="en">Suggestions</span> <span class="caret"></span></button>
                             <button type="button" class="btn waves-effect waves-light btn-info dropdown-toggle" data-toggle="dropdown" aria-expanded="false"><span lang="en">Suggestions</span> <span class="caret"></span></button>
@@ -6094,9 +6103,12 @@ function buildPiholeItem(array){
             <div class="card text-white mb-3 pihole-stat bg-green">
             <div class="card text-white mb-3 pihole-stat bg-green">
                 <div class="card-body">
                 <div class="card-body">
                     <div class="inline-block">
                     <div class="inline-block">
-                        <p>Total queries</p>`;
+                        <p class="d-inline mr-1">Total queries</p>`;
         for(var key in data) {
         for(var key in data) {
             var e = data[key];
             var e = data[key];
+            if(length > 1 && !combine) {
+                card += `<p class="d-inline text-muted">(`+key+`)</p>`;
+            }
             card += `<h3 data-toggle="tooltip" data-placement="right" title="`+key+`">`+e['dns_queries_today'].toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")+`</h3>`;
             card += `<h3 data-toggle="tooltip" data-placement="right" title="`+key+`">`+e['dns_queries_today'].toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")+`</h3>`;
         };
         };
         card += `
         card += `
@@ -6114,9 +6126,12 @@ function buildPiholeItem(array){
             <div class="card bg-inverse text-white mb-3 pihole-stat bg-aqua">
             <div class="card bg-inverse text-white mb-3 pihole-stat bg-aqua">
                 <div class="card-body">
                 <div class="card-body">
                     <div class="inline-block">
                     <div class="inline-block">
-                        <p>Queries Blocked</p>`;
+                        <p class="d-inline mr-1">Queries Blocked</p>`;
         for(var key in data) {
         for(var key in data) {
             var e = data[key];
             var e = data[key];
+            if(length > 1 && !combine) {
+                card += `<p class="d-inline text-muted">(`+key+`)</p>`;
+            }
             card += `<h3 data-toggle="tooltip" data-placement="right" title="`+key+`">`+e['ads_blocked_today'].toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")+`</h3>`;
             card += `<h3 data-toggle="tooltip" data-placement="right" title="`+key+`">`+e['ads_blocked_today'].toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")+`</h3>`;
         };
         };
         card += `
         card += `
@@ -6134,9 +6149,12 @@ function buildPiholeItem(array){
             <div class="card bg-inverse text-white mb-3 pihole-stat bg-yellow">
             <div class="card bg-inverse text-white mb-3 pihole-stat bg-yellow">
                 <div class="card-body">
                 <div class="card-body">
                     <div class="inline-block">
                     <div class="inline-block">
-                        <p>Percent Blocked</p>`;
+                        <p class="d-inline mr-1">Percent Blocked</p>`;
         for(var key in data) {
         for(var key in data) {
             var e = data[key];
             var e = data[key];
+            if(length > 1 && !combine) {
+                card += `<p class="d-inline text-muted">(`+key+`)</p>`;
+            }
             card += `<h3 data-toggle="tooltip" data-placement="right" title="`+key+`">`+e['ads_percentage_today'].toFixed(1)+`%</h3>`
             card += `<h3 data-toggle="tooltip" data-placement="right" title="`+key+`">`+e['ads_percentage_today'].toFixed(1)+`%</h3>`
         };
         };
         card += `
         card += `
@@ -6154,9 +6172,12 @@ function buildPiholeItem(array){
             <div class="card bg-inverse text-white mb-3 pihole-stat bg-red">
             <div class="card bg-inverse text-white mb-3 pihole-stat bg-red">
                 <div class="card-body">
                 <div class="card-body">
                     <div class="inline-block">
                     <div class="inline-block">
-                        <p>Domains on Blocklist</p>`;
+                        <p class="d-inline mr-1">Domains on Blocklist</p>`;
         for(var key in data) {
         for(var key in data) {
             var e = data[key];
             var e = data[key];
+            if(length > 1 && !combine) {
+                card += `<p class="d-inline text-muted">(`+key+`)</p>`;
+            }
             card += `<h3 data-toggle="tooltip" data-placement="right" title="`+key+`">`+e['domains_being_blocked'].toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")+`</h3>`;
             card += `<h3 data-toggle="tooltip" data-placement="right" title="`+key+`">`+e['domains_being_blocked'].toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")+`</h3>`;
         };
         };
         card += `
         card += `
@@ -6177,15 +6198,6 @@ function buildPiholeItem(array){
         stats += '</div>';
         stats += '</div>';
     } else {
     } else {
         for(var key in array['data']) {
         for(var key in array['data']) {
-            if(length > 1) {
-                stats += `
-                <div class="row mb-2">
-                    <div class="col-sm-12">
-                        `+key+`
-                    </div>
-                </div>
-                `;
-            }
             var data = array['data'][key];
             var data = array['data'][key];
             obj = {};
             obj = {};
             obj[key] = data;
             obj[key] = data;
@@ -6588,13 +6600,13 @@ function buildTautulliItem(array){
             var section_name = null;
             var section_name = null;
             if(type == 'movie'){
             if(type == 'movie'){
                 extraField = 'Movies';
                 extraField = 'Movies';
-                section_name = 'Movie Libaries';
+                section_name = 'Movie Libraries';
             }else if(type == 'show'){
             }else if(type == 'show'){
                 extraField = 'Shows/Seasons/Episodes';
                 extraField = 'Shows/Seasons/Episodes';
-                section_name = 'TV Show Libaries';
+                section_name = 'TV Show Libraries';
             }else if(type == 'artist'){
             }else if(type == 'artist'){
                 extraField = 'Artists/Albums/Tracks';
                 extraField = 'Artists/Albums/Tracks';
-                section_name = 'Music Libaries';
+                section_name = 'Music Libraries';
             }
             }
             var cardTitle = '<th><span class="pull-left cardTitle">'+section_name.toUpperCase()+'</span><span class="pull-right cardCountType">'+extraField.toUpperCase()+'</th>';
             var cardTitle = '<th><span class="pull-left cardTitle">'+section_name.toUpperCase()+'</span><span class="pull-right cardCountType">'+extraField.toUpperCase()+'</th>';
             var card = `
             var card = `
@@ -7129,8 +7141,13 @@ function buildMonitorrItem(array){
     var services = array['services'];
     var services = array['services'];
 
 
     var buildCard = function(name, data) {
     var buildCard = function(name, data) {
-        if(data.status) { var statusColor = 'success'; var imageText = 'fa fa-check-circle text-success' } 
-            else { var statusColor = 'danger animated-3 loop-animation flash'; var imageText = 'fa fa-times-circle text-danger'}
+        if(data.status == true) {
+            var statusColor = 'success'; var imageText = 'fa fa-check-circle text-success'
+        } else if (data.status == 'unresponsive') {
+            var statusColor = 'warning animated-3 loop-animation flash'; var imageText = 'fa fa-times-circle text-warning'
+        } else {
+            var statusColor = 'danger animated-3 loop-animation flash'; var imageText = 'fa fa-times-circle text-danger'
+        }
         if(options['compact']) {
         if(options['compact']) {
             var card = `
             var card = `
             <div class="col-xl-2 col-lg-3 col-md-4 col-sm-6 col-xs-12">
             <div class="col-xl-2 col-lg-3 col-md-4 col-sm-6 col-xs-12">
@@ -7160,7 +7177,7 @@ function buildMonitorrItem(array){
                             <img class="monitorrImage" src="`+data.image+`" alt="service icon">
                             <img class="monitorrImage" src="`+data.image+`" alt="service icon">
                         </div>
                         </div>
                         <div class="d-inline-block mt-4 py-2 px-4 badge indicator bg-`+statusColor+`">
                         <div class="d-inline-block mt-4 py-2 px-4 badge indicator bg-`+statusColor+`">
-                            <p class="mb-0">`; if(data.status) { card += 'ONLINE' } else { card += 'OFFLINE' } card+=`</p>
+                            <p class="mb-0">`; if(data.status == true) { card += 'ONLINE' } else if(data.status == 'unresponsive') { card += 'UNRESPONSIVE' } else { card += 'OFFLINE' } card+=`</p>
                         </div>
                         </div>
                         `; if (typeof data.link !== 'undefined') { card +=`</a>`; }
                         `; if (typeof data.link !== 'undefined') { card +=`</a>`; }
                         card += `</div>
                         card += `</div>
@@ -7177,27 +7194,31 @@ function buildMonitorrItem(array){
 }
 }
 function buildMonitorr(array){
 function buildMonitorr(array){
     if(array === false){ return ''; }
     if(array === false){ return ''; }
-    var services = (typeof array.services !== 'undefined') ? Object.keys(array.services).length : false;
-    var html = `
-    <div id="allMonitorr">
-		<div class="el-element-overlay row">`
-    if(array['options']['titleToggle']) {
+    if(array.error != undefined) {
+        console.log('Monitorr error: ' + array.error);
+    } else {
+        var services = (typeof array.services !== 'undefined') ? Object.keys(array.services).length : false;
+        var html = `
+        <div id="allMonitorr">
+            <div class="el-element-overlay row">`
+        if(array['options']['titleToggle']) {
+            html += `
+                <div class="col-md-12">
+                    <h4 class="pull-left homepage-element-title"><span lang="en">`+array['options']['title']+`</span> : </h4><h4 class="pull-left">&nbsp;<span class="label label-info m-l-20 checkbox-circle good-monitorr-services mouse" onclick="homepageMonitorr()">`+services+`</span></h4></h4>
+                    <hr class="hidden-xs ml-2">
+                </div>
+                <div class="clearfix"></div>
+            `;
+        }
         html += `
         html += `
-            <div class="col-md-12">
-                <h4 class="pull-left homepage-element-title"><span lang="en">`+array['options']['title']+`</span> : </h4><h4 class="pull-left">&nbsp;<span class="label label-info m-l-20 checkbox-circle good-monitorr-services mouse" onclick="homepageMonitorr()">`+services+`</span></h4></h4>
-                <hr class="hidden-xs ml-2">
+                <div class="monitorrCards">
+                    `+buildMonitorrItem(array)+`
+                </div>
             </div>
             </div>
-            <div class="clearfix"></div>
+        </div>
+        <div class="clearfix"></div>
         `;
         `;
     }
     }
-    html += `
-            <div class="monitorrCards">
-                `+buildMonitorrItem(array)+`
-			</div>
-		</div>
-    </div>
-    <div class="clearfix"></div>
-    `;
     return (array) ? html : '';
     return (array) ? html : '';
 }
 }
 function homepageMonitorr(timeout){
 function homepageMonitorr(timeout){
@@ -7376,10 +7397,6 @@ function buildSpeedtest(array){
 function buildNetdataItem(array){
 function buildNetdataItem(array){
     var html = `
     var html = `
     <style>
     <style>
-    .all-netdata .chart {
-        width: 183px;
-        height:183px;
-    }
     .all-netdata .easyPieChart-value {
     .all-netdata .easyPieChart-value {
         position: absolute;
         position: absolute;
         top: 77px;
         top: 77px;
@@ -7391,11 +7408,9 @@ function buildNetdataItem(array){
     }
     }
     .all-netdata .easyPieChart-title {
     .all-netdata .easyPieChart-title {
         position: absolute;
         position: absolute;
-        top: 37px;
         width: 100%;
         width: 100%;
         text-align: center;
         text-align: center;
         left: 0;
         left: 0;
-        font-size: 15px;
         font-weight: bold;
         font-weight: bold;
     }
     }
     .all-netdata .easyPieChart-units {
     .all-netdata .easyPieChart-units {
@@ -7409,7 +7424,7 @@ function buildNetdataItem(array){
     }
     }
 
 
     .all-netdata .gauge-chart .gauge-value {
     .all-netdata .gauge-chart .gauge-value {
-        position: absolute;
+        position: relative;
         width: 100%;
         width: 100%;
         text-align: center;
         text-align: center;
         top: 30px;
         top: 30px;
@@ -7420,7 +7435,7 @@ function buildNetdataItem(array){
     }
     }
 
 
     .all-netdata .gauge-chart .gauge-title {
     .all-netdata .gauge-chart .gauge-title {
-        position: absolute;
+        position: relative;
         width: 100%;
         width: 100%;
         text-align: center;
         text-align: center;
         top: -10px;
         top: -10px;
@@ -7429,45 +7444,173 @@ function buildNetdataItem(array){
         left: 0;
         left: 0;
         font-size: 15px;
         font-size: 15px;
     }
     }
+
+    .all-netdata .chart-lg .gauge-chart .gauge-value {
+        top: 70px;
+        font-size: 26px;
+    }
+
+    .all-netdata .chart-lg .gauge-chart .gauge-title {
+        top: 45px;
+        font-size: 15px;
+    }
+
+    .all-netdata .chart-md .gauge-chart .gauge-value {
+        top: 65px;
+        font-size: 26px;
+    }
+
+    .all-netdata .chart-md .gauge-chart .gauge-title {
+        top: 45px;
+        font-size: 15px;
+    }
+
+    .all-netdata .chart-sm .gauge-chart .gauge-value {
+        top: 65px;
+        font-size: 26px;
+    }
+
+    .all-netdata .chart-sm .gauge-chart .gauge-title {
+        top: 45px;
+        font-size: 15px;
+    }
+
+    .all-netdata .chart-lg,
+    .all-netdata .chart-md,
+    .all-netdata .chart-sm {
+        display: inline-block;
+        margin: 15px;
+    }
+
+    .all-netdata .chart-lg,
+    .all-netdata .chart-lg .chart {
+        height: 180px;
+        width: 180px;
+    }
+
+    .all-netdata .chart-md,
+    .all-netdata .chart-md .chart {
+        height: 160px;
+        width: 160px;
+    }
+
+    .all-netdata .chart-sm,
+    .all-netdata .chart-sm .chart {
+        height: 140px;
+        width: 140px;
+    }
+
+    .all-netdata .chart-lg .gauge-chart,
+    .all-netdata .gauge-cont.chart-lg {
+        //height: 300px;
+        width: 300px;
+    }
+
+    .all-netdata .chart-md .gauge-chart,
+    .all-netdata .gauge-cont.chart-md {
+        //height: 275px;
+        width: 275px;
+    }
+
+    .all-netdata .chart-sm .gauge-chart,
+    .all-netdata .gauge-cont.chart-sm {
+        //height: 250px;
+        width: 250px;
+    }
+
+    .all-netdata .chart-lg .easyPieChart-title {
+        top: 37px;
+        font-size: 15px;
+    }
+
+    .all-netdata .chart-md .easyPieChart-title {
+        top: 33px;
+        font-size: 13.5px;
+    }
+
+    .all-netdata .chart-sm .easyPieChart-title {
+        top: 30px;
+        font-size: 12px;
+    }
+
+    .all-netdata .chart-lg .easyPieChart-value {
+        top: 75px;
+        font-size: 24.4625px;
+    }
+
+    .all-netdata .chart-md .easyPieChart-value {
+        top: 65px;
+        font-size: 24.4625px;
+    }
+
+    .all-netdata .chart-sm .easyPieChart-value {
+        top: 55px;
+        font-size: 24.4625px;
+    }
+
+    .all-netdata .chart-lg .easyPieChart-units {
+        top: 130px;
+        font-size: 15px;
+    }
+
+    .all-netdata .chart-md .easyPieChart-units {
+        top: 108px;
+        font-size: 15px;
+    }
+
+    .all-netdata .chart-sm .easyPieChart-units {
+        top: 95px;
+        font-size: 15px;
+    }
     </style>
     </style>
     `;
     `;
 
 
-    var buildEasyPieChart = function(e,i) {
+    var buildEasyPieChart = function(e,i,size,easySize,display) {
         return `
         return `
-        <div class="col-lg-2 col-md-3 col-sm-4 col-xs-12 my-3 text-center">
-            <div class="d-flex justify-content-center">
-                <div class="chart" id="easyPieChart`+(i+1)+`" data-percent="`+e.percent+`">
-                    <span class="easyPieChart-title">`+e.title+`</span>
-                    <span class="easyPieChart-value" id="easyPieChart`+(i+1)+`Value">`+parseFloat(e.value).toFixed(1)+`</span>
-                    <span class="easyPieChart-units" id="easyPieChart`+(i+1)+`Units">`+e.units+`</span>
-                </div>
+        <div class="chart-`+size+` my-3 text-center `+display+`">
+            <div class="chart" id="easyPieChart`+(i+1)+`" data-percent="`+e.percent+`">
+                <span class="easyPieChart-title">`+e.title+`</span>
+                <span class="easyPieChart-value" id="easyPieChart`+(i+1)+`Value">`+parseFloat(e.value).toFixed(1)+`</span>
+                <span class="easyPieChart-units" id="easyPieChart`+(i+1)+`Units">`+e.units+`</span>
             </div>
             </div>
         </div>
         </div>
         <script>
         <script>
         $(function() {
         $(function() {
-            $('#easyPieChart`+(i+1)+`').easyPieChart({
-                size: 183,
+            var opts = {
+                size: `+easySize+`,
                 lineWidth: 7,
                 lineWidth: 7,
-                //animate: false,
                 scaleColor: false,
                 scaleColor: false,
                 barColor: '#`+e.colour+`',
                 barColor: '#`+e.colour+`',
-                trackColor: '#bababa',
-            });
+                trackColor: '#636363',
+            };
+            if(`+e.percent+` == 0) {
+                opts.lineCap = 'butt';
+            }
+            $('#easyPieChart`+(i+1)+`').easyPieChart(opts);
         });
         });
         </script>
         </script>
         `;
         `;
     }
     }
 
 
-    var buildGaugeChart = function(e,i) {
+    var buildGaugeChart = function(e,i,size,easySize,display) {
+        switch(size) {
+            case 'lg':
+                easySize = 300;
+                break;
+            case 'sm':
+                easySize = 275;
+                break;
+            case 'md':
+            default:
+                easySize = 250;
+                break;
+        }
         return `
         return `
-        <div class="col-lg-2 col-md-3 col-sm-4 col-xs-12 my-3 text-center">
-            <div class="d-flex justify-content-center">
-                <div class="gauge-chart">
-                    <span class="gauge-title" id="gaugeChart`+(i+1)+`Title">`+e.title+`</span>
-                    <span class="gauge-value" id="gaugeChart`+(i+1)+`Value">`+parseFloat(e.value).toFixed(1)+`</span>
-                    <canvas id="gaugeChart`+(i+1)+`">
-                    </canvas>
-                </div>
+        <div class="mx-0 gauge-cont chart-`+size+` my-3 text-center `+display+`">
+            <div class="gauge-chart text-center">
+                <span class="gauge-title d-block" id="gaugeChart`+(i+1)+`Title">`+e.title+`</span>
+                <span class="gauge-value d-block" id="gaugeChart`+(i+1)+`Value">`+parseFloat(e.value).toFixed(1)+`</span>
+                <canvas id="gaugeChart`+(i+1)+`" style="width: 100%"></canvas>
             </div>
             </div>
         </div>
         </div>
         <script>
         <script>
@@ -7494,7 +7637,7 @@ function buildNetdataItem(array){
             var gauge = new Gauge(target).setOptions(opts); // create sexy gauge!
             var gauge = new Gauge(target).setOptions(opts); // create sexy gauge!
             gauge.maxValue = `+e.max+`; // set max gauge value
             gauge.maxValue = `+e.max+`; // set max gauge value
             gauge.setMinValue(0);  // Prefer setter over gauge.minValue = 0
             gauge.setMinValue(0);  // Prefer setter over gauge.minValue = 0
-            gauge.animationSpeed = 20; // set animation speed (32 is default value)
+            gauge.animationSpeed = 8; // set animation speed (32 is default value)
             gauge.set(`+e.percent+`); // set actual value
             gauge.set(`+e.percent+`); // set actual value
             window.netdata[`+(i+1)+`] = gauge
             window.netdata[`+(i+1)+`] = gauge
         });
         });
@@ -7503,17 +7646,54 @@ function buildNetdataItem(array){
     }
     }
 
 
     array.forEach((e, i) => {
     array.forEach((e, i) => {
-        if(e.chart == 'easypiechart') {
-            html += buildEasyPieChart(e,i);
+        var size = e.size;
+        var easySize;
+        if(size == '') {
+            size = 'md';
+        }
+        switch(size) {
+            case 'lg':
+                easySize = 180;
+                break;
+            case 'sm':
+                easySize = 140;
+                break;
+            case 'md':
+            default:
+                easySize = 160;
+                break;
+        }
+
+        var display = ' ';
+        if(e.lg) {
+            display += ' d-xl-inline-block d-lg-inline-block';
+        } else {
+            display += ' d-xl-none d-lg-none d-none';
+        }
+        if(e.md) {
+            display += ' d-md-inline-block';
+        } else {
+            display += ' d-md-none d-none';
+        }
+        if(e.sm) {
+            display += ' d-sm-inline-block d-xs-inline-block';
+        } else {
+            display += ' d-sm-none d-xs-none d-none';
+        }
+        display += ' ';
+
+        if(e.error) {
+            console.log('Netdata error (Chart ' + (i+1) + '): ' + e.error);
+        } else if(e.chart == 'easypiechart') {
+            html += buildEasyPieChart(e,i,size,easySize,display);
         } else if(e.chart == 'gauge') {
         } else if(e.chart == 'gauge') {
-            html += buildGaugeChart(e,i);
+            html += buildGaugeChart(e,i,size,easySize,display);
         }
         }
     });
     });
     
     
     return html;
     return html;
 }
 }
 function buildNetdata(array){
 function buildNetdata(array){
-    // console.log(array);
     var data = array.data;
     var data = array.data;
     if(array === false){ return ''; }
     if(array === false){ return ''; }
     window.netdata = [];
     window.netdata = [];
@@ -7545,7 +7725,7 @@ function buildNetdata(array){
       
       
       .all-netdata .chart {
       .all-netdata .chart {
           float: left;
           float: left;
-          margin: 10px;
+          //margin: 10px;
       }
       }
       
       
       .all-netdata .percentage,
       .all-netdata .percentage,
@@ -7655,7 +7835,7 @@ function buildNetdata(array){
     html += `
     html += `
     <div class="row">
     <div class="row">
         
         
-            <div class="d-lg-flex d-md-block d-sm-block d-xs-block align-items-center justify-content-center all-netdata">
+            <div class="d-block text-center all-netdata">
     `;
     `;
     html += buildNetdataItem(data);
     html += buildNetdataItem(data);
     html += `
     html += `
@@ -7702,7 +7882,7 @@ function tryUpdateNetdata(array){
             if(window.netdata) {
             if(window.netdata) {
                 if(window.netdata[(i+1)]) {
                 if(window.netdata[(i+1)]) {
                     window.netdata[(i+1)].set(e.percent); // set actual value
                     window.netdata[(i+1)].set(e.percent); // set actual value
-                    $('#gaugeChart' + (i+1) + 'Value').html(parseFloat(e.percent).toFixed(1));
+                    $('#gaugeChart' + (i+1) + 'Value').html(parseFloat(e.value).toFixed(1));
                     existing = true;
                     existing = true;
                 }
                 }
             } else {
             } else {

+ 7 - 0
js/news.json

@@ -1,4 +1,11 @@
 [
 [
+  {
+    "title": "Plex OAuth Patch",
+    "subTitle": "You server hostname will now be listed on OAuth box",
+    "date": "2020-06-18 16:30",
+    "body": "Plex has now fixed the 3 open CVE's pertaining to the ability for an attacker to phish for an Plex Admins token.  Please follow this link to read more about it.  <a href=\"https:\/\/www.bleepingcomputer.com\/news\/security\/plex-fixes-media-server-bugs-allowing-full-system-takeover\" target=\"_blank\">Plex Vulnerabilities Patched<\/a>",
+    "author": "CauseFX"
+  },
   {
   {
     "title": "Tab Redirect Loops and SameSite Cookie Issues",
     "title": "Tab Redirect Loops and SameSite Cookie Issues",
     "subTitle": "New Browser Restrictions",
     "subTitle": "New Browser Restrictions",

+ 7 - 0
js/version.json

@@ -250,5 +250,12 @@
     "new": "Weather Homepage Item|Pi-Hole Homepage Item|Tautulli Homepage Item|Monitorr Homepage Item|Netdata Homepage Item|SpeedTest Homepage Item|HealthChecks Plugin|Bandwidth info to Now Playing|Config files can now contain arrays|Scrape API endpoint|New API Docs WIP located at /api/docs",
     "new": "Weather Homepage Item|Pi-Hole Homepage Item|Tautulli Homepage Item|Monitorr Homepage Item|Netdata Homepage Item|SpeedTest Homepage Item|HealthChecks Plugin|Bandwidth info to Now Playing|Config files can now contain arrays|Scrape API endpoint|New API Docs WIP located at /api/docs",
     "fixed": "iOS icon issue|Language Bug|Cache Image timing|fix hardcoded plugins directory for root (#1341)|Ombi Posters|Plex Timeouts|Indent cleanup|Homepage Item shortcut in Tab Editor broken (#1368)|Empty login form submitting|Sanitize username going into log (#1359)|Ombi Div for tv items|edit sttabs for reflect more than just settings tabs|Emby HomePage Add-in - Item Details URL Needs updating (#1290)|Fix JQuery error on settings save (#1356)",
     "fixed": "iOS icon issue|Language Bug|Cache Image timing|fix hardcoded plugins directory for root (#1341)|Ombi Posters|Plex Timeouts|Indent cleanup|Homepage Item shortcut in Tab Editor broken (#1368)|Empty login form submitting|Sanitize username going into log (#1359)|Ombi Div for tv items|edit sttabs for reflect more than just settings tabs|Emby HomePage Add-in - Item Details URL Needs updating (#1290)|Fix JQuery error on settings save (#1356)",
     "notes": "Added note about Samesite issues|New Radarr Logo|Please report bugs in GitHub issues page"
     "notes": "Added note about Samesite issues|New Radarr Logo|Please report bugs in GitHub issues page"
+  },
+  "2.0.631": {
+    "date": "2020-05-01 19:15",
+    "title": "Whoa - sorry for the wait",
+    "new": "Added css bottom-close-splash",
+    "fixed": "Netadata fixes|Docker update fixes|Tautulli Fixes|Hardcode fixes for days beyond one year|Typos|NZBHydra tab image|fix stray / in index.php and js/functions.php (#1408)|LDAP with STARTTLS support (#1411)",
+    "notes": "Please report bugs in GitHub issues page"
   }
   }
 }
 }

BIN
plugins/images/tabs/nzbhydra.png


+ 10 - 0
plugins/images/userTabs/index.html

@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title>Image folder for User</title>
+</head>
+<body>
+Tab images go here
+</body>
+</html>

Некоторые файлы не были показаны из-за большого количества измененных файлов