Browse Source

Merge pull request #1901 from causefx/v2-develop

V2 develop
causefx 3 years ago
parent
commit
f8581fb9d2

+ 1 - 1
api/classes/organizr.class.php

@@ -67,7 +67,7 @@ class Organizr
 
 	// ===================================
 	// Organizr Version
-	public $version = '2.1.2380';
+	public $version = '2.1.2400';
 	// ===================================
 	// Quick php Version check
 	public $minimumPHP = '7.4';

+ 3 - 0
api/config/default.php

@@ -129,6 +129,8 @@ return [
 	'sickrageDisableCertCheck' => false,
 	'sickrageUseCustomCertificate' => false,
 	'jdownloaderURL' => '',
+	'jdownloaderUsername' => '',
+	'jdownloaderPassword' => '',
 	'jdownloaderCombine' => false,
 	'jdownloaderRefresh' => '60000',
 	'jdownloaderUseCustomCertificate' => false,
@@ -533,6 +535,7 @@ return [
 	'homepagePiholeCombine' => false,
 	'piholeHeaderToggle' => true,
 	'piholeURL' => '',
+	'piholeToken' => '',
 	'homepageAdGuardEnabled' => false,
 	'homepageAdGuardAuth' => '1',
 	'homepageAdGuardRefresh' => '10000',

+ 1 - 1
api/homepage/adguard.php

@@ -27,7 +27,7 @@ trait AdGuardHomepageItem
 					$this->settingsOption('password', 'adGuardPassword'),
         ],
 				'Misc' => [
-					$this->settingsOption('toggle-title', 'adguardToggle'),
+					$this->settingsOption('toggle-title', 'adguardHeaderToggle'),
 					$this->settingsOption('switch', 'homepageAdGuardCombine', ['label' => 'Combine stat cards', 'help' => 'This controls whether to combine the stats for multiple adguard instances into 1 card.']),
 				],
 				'Stats' => [

+ 146 - 132
api/homepage/jdownloader.php

@@ -2,23 +2,23 @@
 
 trait JDownloaderHomepageItem
 {
-	public function jDownloaderSettingsArray($infoOnly = false)
-	{
-		$homepageInformation = [
-			'name' => 'JDownloader',
-			'enabled' => strpos('personal', $this->config['license']) !== false,
-			'image' => 'plugins/images/tabs/jdownloader.png',
-			'category' => 'Downloader',
-			'settingsArray' => __FUNCTION__
-		];
-		if ($infoOnly) {
-			return $homepageInformation;
-		}
-		$homepageSettings = [
-			'debug' => true,
-			'settings' => [
-				'FYI' => [
-					$this->settingsOption('html', null, ['override' => 12, 'html' => '
+    public function jDownloaderSettingsArray($infoOnly = false)
+    {
+        $homepageInformation = [
+            'name' => 'JDownloader',
+            'enabled' => strpos('personal', $this->config['license']) !== false,
+            'image' => 'plugins/images/tabs/jdownloader.png',
+            'category' => 'Downloader',
+            'settingsArray' => __FUNCTION__
+        ];
+        if ($infoOnly) {
+            return $homepageInformation;
+        }
+        $homepageSettings = [
+            'debug' => true,
+            'settings' => [
+                'FYI' => [
+                    $this->settingsOption('html', null, ['override' => 12, 'html' => '
 						<div class="row">
 							<div class="col-lg-12">
 								<div class="panel panel-info">
@@ -36,79 +36,87 @@ trait JDownloaderHomepageItem
 								</div>
 							</div>
 						</div>']
-					),
-				],
-				'Enable' => [
-					$this->settingsOption('enable', 'homepageJdownloaderEnabled'),
-					$this->settingsOption('auth', 'homepageJdownloaderAuth'),
-				],
-				'Connection' => [
-					$this->settingsOption('url', 'jdownloaderURL'),
-					$this->settingsOption('blank'),
-					$this->settingsOption('disable-cert-check', 'jdownloaderDisableCertCheck'),
-					$this->settingsOption('use-custom-certificate', 'jdownloaderUseCustomCertificate'),
-				],
-				'Misc Options' => [
-					$this->settingsOption('refresh', 'jdownloaderRefresh'),
-					$this->settingsOption('combine', 'jdownloaderCombine'),
-				],
-				'Test Connection' => [
-					$this->settingsOption('blank', null, ['label' => 'Please Save before Testing']),
-					$this->settingsOption('test', 'jdownloader'),
-				]
-			]
-		];
-		return array_merge($homepageInformation, $homepageSettings);
-	}
+                    ),
+                ],
+                'Enable' => [
+                    $this->settingsOption('enable', 'homepageJdownloaderEnabled'),
+                    $this->settingsOption('auth', 'homepageJdownloaderAuth'),
+                ],
+                'Connection' => [
+                    $this->settingsOption('url', 'jdownloaderURL'),
+                    $this->settingsOption('url', 'jdownloaderUsername'),
+                    $this->settingsOption('url', 'jdownloaderPassword'),
+                    $this->settingsOption('blank'),
+                    $this->settingsOption('disable-cert-check', 'jdownloaderDisableCertCheck'),
+                    $this->settingsOption('use-custom-certificate', 'jdownloaderUseCustomCertificate'),
+                ],
+                'Misc Options' => [
+                    $this->settingsOption('refresh', 'jdownloaderRefresh'),
+                    $this->settingsOption('combine', 'jdownloaderCombine'),
+                ],
+                'Test Connection' => [
+                    $this->settingsOption('blank', null, ['label' => 'Please Save before Testing']),
+                    $this->settingsOption('test', 'jdownloader'),
+                ]
+            ]
+        ];
+        return array_merge($homepageInformation, $homepageSettings);
+    }
 
-	public function testConnectionJDownloader()
-	{
-		if (empty($this->config['jdownloaderURL'])) {
-			$this->setAPIResponse('error', 'JDownloader URL is not defined', 422);
-			return false;
-		}
-		$url = $this->qualifyURL($this->config['jdownloaderURL']);
-		try {
-			$options = $this->requestOptions($url, $this->config['jdownloaderRefresh'], $this->config['jdownloaderDisableCertCheck'], $this->config['jdownloaderUseCustomCertificate']);
-			$response = Requests::get($url, [], $options);
-			if ($response->success) {
-				$this->setAPIResponse('success', 'API Connection succeeded', 200);
-				return true;
-			} else {
-				$this->setAPIResponse('success', 'JDownloader Error Occurred', 500);
-				return false;
-			}
-		} catch (Requests_Exception $e) {
-			$this->setLoggerChannel('JDownloader')->error($e);
-			$this->setResponse(500, $e->getMessage());
-			return false;
-		}
-	}
+    public function testConnectionJDownloader()
+    {
+        if (empty($this->config['jdownloaderURL'])) {
+            $this->setAPIResponse('error', 'JDownloader URL is not defined', 422);
+            return false;
+        }
+        if (!empty($this->config['jdownloaderUsername']) || !empty($this->config['jdownloaderPassword'])) {
+            $authHeader = 'Authorization: Basic ' . base64_encode($this->config['jdownloaderUsername'] . ':' . $this->config['jdownloaderPassword']);
+            $headers = array('headers' => array($authHeader));
+        } else {
+            $headers = [];
+        }
+        $url = $this->qualifyURL($this->config['jdownloaderURL']);
+        try {
+            $options = $this->requestOptions($url, $this->config['jdownloaderRefresh'], $this->config['jdownloaderDisableCertCheck'], $this->config['jdownloaderUseCustomCertificate']);
+            $response = Requests::get($url, $headers, $options);
+            if ($response->success) {
+                $this->setAPIResponse('success', 'API Connection succeeded', 200);
+                return true;
+            } else {
+                $this->setAPIResponse('success', 'JDownloader Error Occurred', 500);
+                return false;
+            }
+        } catch (Requests_Exception $e) {
+            $this->setLoggerChannel('JDownloader')->error($e);
+            $this->setResponse(500, $e->getMessage());
+            return false;
+        }
+    }
 
-	public function jDownloaderHomepagePermissions($key = null)
-	{
-		$permissions = [
-			'main' => [
-				'enabled' => [
-					'homepageJdownloaderEnabled'
-				],
-				'auth' => [
-					'homepageJdownloaderAuth'
-				],
-				'not_empty' => [
-					'jdownloaderURL'
-				]
-			]
-		];
-		return $this->homepageCheckKeyPermissions($key, $permissions);
-	}
+    public function jDownloaderHomepagePermissions($key = null)
+    {
+        $permissions = [
+            'main' => [
+                'enabled' => [
+                    'homepageJdownloaderEnabled'
+                ],
+                'auth' => [
+                    'homepageJdownloaderAuth'
+                ],
+                'not_empty' => [
+                    'jdownloaderURL'
+                ]
+            ]
+        ];
+        return $this->homepageCheckKeyPermissions($key, $permissions);
+    }
 
-	public function homepageOrderjdownloader()
-	{
-		if ($this->homepageItemPermissions($this->jDownloaderHomepagePermissions('main'))) {
-			$loadingBox = ($this->config['jdownloaderCombine']) ? '' : '<div class="white-box homepage-loading-box"><h2 class="text-center" lang="en">Loading Download Queue...</h2></div>';
-			$builder = ($this->config['jdownloaderCombine']) ? 'buildDownloaderCombined(\'jdownloader\');' : '$("#' . __FUNCTION__ . '").html(buildDownloader("jdownloader"));';
-			return '
+    public function homepageOrderjdownloader()
+    {
+        if ($this->homepageItemPermissions($this->jDownloaderHomepagePermissions('main'))) {
+            $loadingBox = ($this->config['jdownloaderCombine']) ? '' : '<div class="white-box homepage-loading-box"><h2 class="text-center" lang="en">Loading Download Queue...</h2></div>';
+            $builder = ($this->config['jdownloaderCombine']) ? 'buildDownloaderCombined(\'jdownloader\');' : '$("#' . __FUNCTION__ . '").html(buildDownloader("jdownloader"));';
+            return '
 				<div id="' . __FUNCTION__ . '">
 					' . $loadingBox . '
 					<script>
@@ -119,50 +127,56 @@ trait JDownloaderHomepageItem
 					</script>
 				</div>
 				';
-		}
-	}
+        }
+    }
 
-	public function getJdownloaderHomepageQueue()
-	{
-		if (!$this->homepageItemPermissions($this->jDownloaderHomepagePermissions('main'), true)) {
-			return false;
-		}
-		$url = $this->qualifyURL($this->config['jdownloaderURL']);
-		try {
-			$options = $this->requestOptions($url, $this->config['jdownloaderRefresh'], $this->config['jdownloaderDisableCertCheck'], $this->config['jdownloaderUseCustomCertificate']);
-			$response = Requests::get($url, [], $options);
-			if ($response->success) {
-				$temp = json_decode($response->body, true);
-				$packages = $temp['packages'];
-				if ($packages['downloader']) {
-					$api['content']['queueItems'] = $packages['downloader'];
-				} else {
-					$api['content']['queueItems'] = [];
-				}
-				if ($packages['linkgrabber_decrypted']) {
-					$api['content']['grabberItems'] = $packages['linkgrabber_decrypted'];
-				} else {
-					$api['content']['grabberItems'] = [];
-				}
-				if ($packages['linkgrabber_failed']) {
-					$api['content']['encryptedItems'] = $packages['linkgrabber_failed'];
-				} else {
-					$api['content']['encryptedItems'] = [];
-				}
-				if ($packages['linkgrabber_offline']) {
-					$api['content']['offlineItems'] = $packages['linkgrabber_offline'];
-				} else {
-					$api['content']['offlineItems'] = [];
-				}
-				$api['content']['$status'] = array($temp['downloader_state'], $temp['grabber_collecting'], $temp['update_ready']);
-			}
-		} catch (Requests_Exception $e) {
-			$this->setLoggerChannel('JDownloader')->error($e);
-			$this->setResponse(500, $e->getMessage());
-			return false;
-		};
-		$api['content'] = isset($api['content']) ? $api['content'] : false;
-		$this->setAPIResponse('success', null, 200, $api);
-		return $api;
-	}
+    public function getJdownloaderHomepageQueue()
+    {
+        if (!$this->homepageItemPermissions($this->jDownloaderHomepagePermissions('main'), true)) {
+            return false;
+        }
+        $url = $this->qualifyURL($this->config['jdownloaderURL']);
+        if (!empty($this->config['jdownloaderUsername']) || !empty($this->config['jdownloaderPassword'])) {
+            $authHeader = 'Authorization: Basic ' . base64_encode($this->config['jdownloaderUsername'] . ':' . $this->config['jdownloaderPassword']);
+            $headers = array('headers' => array($authHeader));
+        } else {
+            $headers = [];
+        }
+        try {
+            $options = $this->requestOptions($url, $this->config['jdownloaderRefresh'], $this->config['jdownloaderDisableCertCheck'], $this->config['jdownloaderUseCustomCertificate']);
+            $response = Requests::get($url, $headers, $options);
+            if ($response->success) {
+                $temp = json_decode($response->body, true);
+                $packages = $temp['packages'];
+                if ($packages['downloader']) {
+                    $api['content']['queueItems'] = $packages['downloader'];
+                } else {
+                    $api['content']['queueItems'] = [];
+                }
+                if ($packages['linkgrabber_decrypted']) {
+                    $api['content']['grabberItems'] = $packages['linkgrabber_decrypted'];
+                } else {
+                    $api['content']['grabberItems'] = [];
+                }
+                if ($packages['linkgrabber_failed']) {
+                    $api['content']['encryptedItems'] = $packages['linkgrabber_failed'];
+                } else {
+                    $api['content']['encryptedItems'] = [];
+                }
+                if ($packages['linkgrabber_offline']) {
+                    $api['content']['offlineItems'] = $packages['linkgrabber_offline'];
+                } else {
+                    $api['content']['offlineItems'] = [];
+                }
+                $api['content']['$status'] = array($temp['downloader_state'], $temp['grabber_collecting'], $temp['update_ready']);
+            }
+        } catch (Requests_Exception $e) {
+            $this->setLoggerChannel('JDownloader')->error($e);
+            $this->setResponse(500, $e->getMessage());
+            return false;
+        };
+        $api['content'] = isset($api['content']) ? $api['content'] : false;
+        $this->setAPIResponse('success', null, 200, $api);
+        return $api;
+    }
 }

+ 24 - 15
api/homepage/pihole.php

@@ -22,7 +22,8 @@ trait PiHoleHomepageItem
 					$this->settingsOption('auth', 'homepagePiholeAuth'),
 				],
 				'Connection' => [
-					$this->settingsOption('url', 'piholeURL', ['help' => 'Please make sure to use local IP address and port and to include \'/admin/\' at the end of the URL. You can add multiple Pi-holes by comma separating the URLs.', 'placeholder' => 'http(s)://hostname:port/admin/']),
+					$this->settingsOption('multiple-url', 'piholeURL', ['help' => 'Please make sure to use local IP address and port and to include \'/admin/\' at the end of the URL. You can add multiple Pi-holes by comma separating the URLs.', 'placeholder' => 'http(s)://hostname:port/admin/']),
+					$this->settingsOption('multiple-token', 'piholeToken'),
 				],
 				'Misc' => [
 					$this->settingsOption('toggle-title', 'piholeHeaderToggle'),
@@ -43,30 +44,35 @@ trait PiHoleHomepageItem
 			$this->setAPIResponse('error', 'Pihole URL is not defined', 422);
 			return false;
 		}
-		$api = array();
 		$failed = false;
 		$errors = '';
-		$urls = explode(',', $this->config['piholeURL']);
-		foreach ($urls as $url) {
-			$url = $url . '/api.php?';
+		$list = $this->csvHomepageUrlToken($this->config['piholeURL'], $this->config['piholeToken']);
+		foreach ($list as $key => $value) {
+			$url = $value['url'] . '/api.php?status';
+			if ($value['token'] !== '' && $value['token'] !== null) {
+				$url = $url . '&auth=' . $value['token'];
+			}
+			$ip = $this->qualifyURL($url, true)['host'];
 			try {
 				$response = Requests::get($url, [], []);
 				if ($response->success) {
-					@$test = json_decode($response->body, true);
-					if (!is_array($test)) {
-						$ip = $this->qualifyURL($url, true)['host'];
+					$test = $this->testAndFormatString($response->body);
+					if (($test['type'] !== 'json')) {
 						$errors .= $ip . ': Response was not JSON';
 						$failed = true;
+					} else {
+						if (!isset($test['data']['status'])) {
+							$errors .= $ip . ': Missing API Token';
+							$failed = true;
+						}
 					}
 				}
 				if (!$response->success) {
-					$ip = $this->qualifyURL($url, true)['host'];
 					$errors .= $ip . ': Unknown Failure';
 					$failed = true;
 				}
 			} catch (Requests_Exception $e) {
 				$failed = true;
-				$ip = $this->qualifyURL($url, true)['host'];
 				$errors .= $ip . ': ' . $e->getMessage();
 				$this->setLoggerChannel('PiHole')->error($e);
 			};
@@ -119,10 +125,13 @@ trait PiHoleHomepageItem
 		if (!$this->homepageItemPermissions($this->piholeHomepagePermissions('main'), true)) {
 			return false;
 		}
-		$api = array();
-		$urls = explode(',', $this->config['piholeURL']);
-		foreach ($urls as $url) {
-			$url = $url . '/api.php?';
+		$api = [];
+		$list = $this->csvHomepageUrlToken($this->config['piholeURL'], $this->config['piholeToken']);
+		foreach ($list as $key => $value) {
+			$url = $value['url'] . '/api.php?summaryRaw';
+			if ($value['token'] !== '' && $value['token'] !== null) {
+				$url = $url . '&auth=' . $value['token'];
+			}
 			try {
 				$response = Requests::get($url, [], []);
 				if ($response->success) {
@@ -140,7 +149,7 @@ trait PiHoleHomepageItem
 		}
 		$api['options']['combine'] = $this->config['homepagePiholeCombine'];
 		$api['options']['title'] = $this->config['piholeHeaderToggle'];
-		$api = isset($api) ? $api : null;
+		$api = $api ?? null;
 		$this->setAPIResponse('success', null, 200, $api);
 		return $api;
 	}

+ 58 - 34
js/functions.js

@@ -531,7 +531,7 @@ function cleanHash(hash){
     return hash.replaceAll('%20','-');
 }
 function dirtyHash(hash){
-    hash = hash.replace('-','%20');
+    hash = hash.replaceAll('-','%20');
     return decodeURI(hash);
 }
 // What the hell is this?  I don't remember this lol
@@ -3480,9 +3480,14 @@ function buildCategoryEditorItem(array){
 function buildTabEditorItem(array){
 	var tabList = '';
 	$.each(array.tabs, function(i,v) {
-		var deleteDisabled = v.url.indexOf('/page/settings') > 0 ? 'disabled' : 'deleteTab';
-		var buttonDisabled = v.url.indexOf('/page/settings') > 0 ? 'disabled' : '';
-        var typeDisabled = v.url.indexOf('/v2/page/') > 0 ? 'disabled' : '';
+		let deleteDisabled = 'deleteTab';
+		let buttonDisabled = '';
+		let typeDisabled = '';
+		if(v.url !== null){
+			deleteDisabled = v.url.indexOf('/page/settings') > 0 ? 'disabled' : 'deleteTab';
+			buttonDisabled = v.url.indexOf('/page/settings') > 0 ? 'disabled' : '';
+			typeDisabled = v.url.indexOf('/v2/page/') > 0 ? 'disabled' : '';
+		}
 		tabList += `
 		<tr class="tabEditor" data-order="`+v.order+`" data-original-order="`+v.order+`" data-id="`+v.id+`" data-group-id="`+v.group_id+`" data-category-id="`+v.category_id+`" data-name="`+v.name+`" data-url="`+v.url+`" data-local-url="`+v.url_local+`" data-ping-url="`+v.ping_url+`" data-image="`+v.image+`" data-tab-action-type="`+v.timeout+`" data-tab-action-time="`+v.timeout_ms+`">
 			<input type="hidden" class="form-control" name="tab[`+v.id+`].id" value="`+v.id+`">
@@ -7869,6 +7874,7 @@ function buildPiholeItem(array){
     var length = Object.keys(array['data']).length;
     var combine = array['options']['combine'];
     var totalQueries = function(data) {
+
         var card = `
         <div class="col-lg-3 col-md-6 col-sm-6 col-xs-12">
             <div class="card text-white mb-3 pihole-stat bg-green">
@@ -7881,10 +7887,14 @@ function buildPiholeItem(array){
 	            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>`;
-            }
+				let value = 'Error';
+				if(e.length == undefined){
+					value = e['dns_queries_today'].toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
+				}
+	            card += `<h3 data-toggle="tooltip" data-placement="right" title="`+key+`">`+value+`</h3>`;
 
-        };
+            }
+        }
         card += `
                     </div>
                     <i class="fa fa-globe inline-block" aria-hidden="true"></i>
@@ -7907,9 +7917,13 @@ function buildPiholeItem(array){
 		        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>`;
-	        }
-        };
+		        let value = 'Error';
+		        if(e.length == undefined){
+			        value = e['ads_blocked_today'].toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
+		        }
+		        card += `<h3 data-toggle="tooltip" data-placement="right" title="`+key+`">`+value+`</h3>`;
+			}
+        }
         card += `
                     </div>
                     <i class="fa fa-hand-paper-o inline-block" aria-hidden="true"></i>
@@ -7932,9 +7946,13 @@ function buildPiholeItem(array){
 		        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>`
+		        let value = 'Error';
+		        if(e.length == undefined){
+			        value = e['ads_percentage_today'].toFixed(1)
+		        }
+		        card += `<h3 data-toggle="tooltip" data-placement="right" title="`+key+`">`+value+`</h3>`;
 	        }
-        };
+        }
         card += `
                     </div>
                     <i class="fa fa-pie-chart inline-block" aria-hidden="true"></i>
@@ -7957,9 +7975,13 @@ function buildPiholeItem(array){
 		        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>`;
+		        let value = 'Error';
+		        if(e.length == undefined){
+			        value = e['domains_being_blocked'].toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")
+		        }
+		        card += `<h3 data-toggle="tooltip" data-placement="right" title="`+key+`">`+value+`</h3>`;
 	        }
-        };
+        }
         card += `
                     </div>
                     <i class="fa fa-list inline-block" aria-hidden="true"></i>
@@ -7969,26 +7991,28 @@ function buildPiholeItem(array){
         `
         return card;
     };
-    if(combine) {
-        stats += '<div class="row">'
-        stats += totalQueries(array['data']);
-        stats += totalBlocked(array['data']);
-        stats += percentBlocked(array['data']);
-        stats += domainsBlocked(array['data']);
-        stats += '</div>';
-    } else {
-        for(var key in array['data']) {
-            var data = array['data'][key];
-            obj = {};
-            obj[key] = data;
-            stats += '<div class="row">'
-            stats += totalQueries(obj);
-            stats += totalBlocked(obj);
-            stats += percentBlocked(obj);
-            stats += domainsBlocked(obj);
-            stats += '</div>';
-        };
-    }
+
+	if(combine) {
+		stats += '<div class="row">'
+		stats += totalQueries(array['data']);
+		stats += totalBlocked(array['data']);
+		stats += percentBlocked(array['data']);
+		stats += domainsBlocked(array['data']);
+		stats += '</div>';
+	} else {
+		for(var key in array['data']) {
+			var data = array['data'][key];
+			obj = {};
+			obj[key] = data;
+			stats += '<div class="row">'
+			stats += totalQueries(obj);
+			stats += totalBlocked(obj);
+			stats += percentBlocked(obj);
+			stats += domainsBlocked(obj);
+			stats += '</div>';
+		};
+	}
+
     return stats;
 }
 function homepagePihole(timeout){

+ 7 - 0
js/version.json

@@ -607,5 +607,12 @@
     "new": "added debug info to exception for email test",
     "fixed": "fixed tab names not encoding correctly (#1885)|fix github backer issue|fix userDefinedIdReplacementLink if string is blank",
     "notes": "only errors logged|random commit for json file to test|random pest test class"
+  },
+  "2.1.2400": {
+    "date": "2023-01-03 22:57",
+    "title": "Tab Fix and update PiHole HP Item",
+    "new": "added piholeToken to config items",
+    "fixed": "fixed issue if pihole could not connect|Fixed issue with tabs not loading in settings|fix issue with Tab names after F5 reload (#1885)|Fix Latest Pi-Hole breaks stats if password enabled (#1896)|fix toggle header for adguard",
+    "notes": "Implement JDownloader Homepage Basic Auth #1830|Update adguard.php|updated pihole hp item to reflect auth change to new pihole version|updated pihole test connection to reflect auth changes"
   }
 }