Pārlūkot izejas kodu

Merge pull request #1693 from causefx/v2-develop

V2 develop
causefx 4 gadi atpakaļ
vecāks
revīzija
be78638206
54 mainītis faili ar 444 papildinājumiem un 173 dzēšanām
  1. 85 20
      api/classes/organizr.class.php
  2. 5 5
      api/composer.lock
  3. 2 1
      api/config/default.php
  4. 5 1
      api/functions/config-functions.php
  5. 62 0
      api/functions/normal-functions.php
  6. 19 1
      api/functions/organizr-functions.php
  7. 1 0
      api/homepage/calendar.php
  8. 1 0
      api/homepage/couchpotato.php
  9. 1 0
      api/homepage/deluge.php
  10. 1 0
      api/homepage/emby.php
  11. 1 0
      api/homepage/healthchecks.php
  12. 2 0
      api/homepage/html.php
  13. 1 0
      api/homepage/jackett.php
  14. 1 0
      api/homepage/jdownloader.php
  15. 3 9
      api/homepage/jellyfin.php
  16. 1 0
      api/homepage/lidarr.php
  17. 1 0
      api/homepage/misc.php
  18. 1 0
      api/homepage/monitorr.php
  19. 1 0
      api/homepage/netdata.php
  20. 1 0
      api/homepage/nzbget.php
  21. 1 0
      api/homepage/octoprint.php
  22. 2 1
      api/homepage/ombi.php
  23. 1 0
      api/homepage/pihole.php
  24. 19 27
      api/homepage/plex.php
  25. 27 0
      api/homepage/qbittorrent.php
  26. 1 0
      api/homepage/radarr.php
  27. 1 0
      api/homepage/rtorrent.php
  28. 1 0
      api/homepage/sabnzbd.php
  29. 1 0
      api/homepage/sickrage.php
  30. 3 2
      api/homepage/sonarr.php
  31. 1 0
      api/homepage/speedtest.php
  32. 1 0
      api/homepage/tautulli.php
  33. 1 0
      api/homepage/trakt.php
  34. 1 0
      api/homepage/transmission.php
  35. 1 0
      api/homepage/unifi.php
  36. 1 0
      api/homepage/weather.php
  37. 1 1
      api/pages/homepage.php
  38. 2 2
      api/pages/settings-customize-appearance.php
  39. 2 2
      api/pages/settings-settings-main.php
  40. 2 2
      api/pages/settings-settings-sso.php
  41. 10 0
      api/plugins/invites/plugin.php
  42. 10 0
      api/v2/routes/settings.php
  43. 2 2
      api/vendor/adldap2/adldap2/readme.md
  44. 2 4
      api/vendor/adldap2/adldap2/src/Adldap.php
  45. 3 3
      api/vendor/adldap2/adldap2/src/Connections/ConnectionInterface.php
  46. 24 26
      api/vendor/adldap2/adldap2/src/Query/Builder.php
  47. 5 5
      api/vendor/composer/InstalledVersions.php
  48. 6 6
      api/vendor/composer/installed.json
  49. 5 5
      api/vendor/composer/installed.php
  50. 5 1
      css/themes/Organizr.css
  51. 0 18
      js/custom.js
  52. 0 0
      js/custom.min.js
  53. 100 29
      js/functions.js
  54. 7 0
      js/version.json

+ 85 - 20
api/classes/organizr.class.php

@@ -60,7 +60,7 @@ class Organizr
 	
 	
 	// ===================================
 	// ===================================
 	// Organizr Version
 	// Organizr Version
-	public $version = '2.1.446';
+	public $version = '2.1.472';
 	// ===================================
 	// ===================================
 	// Quick php Version check
 	// Quick php Version check
 	public $minimumPHP = '7.3';
 	public $minimumPHP = '7.3';
@@ -647,6 +647,7 @@ class Organizr
 			// Update config.php version if different to the installed version
 			// Update config.php version if different to the installed version
 			if ($updateSuccess && $this->version !== $this->config['configVersion']) {
 			if ($updateSuccess && $this->version !== $this->config['configVersion']) {
 				$this->updateConfig(array('apply_CONFIG_VERSION' => $this->version));
 				$this->updateConfig(array('apply_CONFIG_VERSION' => $this->version));
+				$this->debug('Updated config version to ' . $this->version);
 			}
 			}
 			if ($updateSuccess == false) {
 			if ($updateSuccess == false) {
 				die('Database update failed - Please manually check logs and fix - Then reload this page');
 				die('Database update failed - Please manually check logs and fix - Then reload this page');
@@ -861,7 +862,7 @@ class Organizr
 		return $current;
 		return $current;
 	}
 	}
 	
 	
-	public function config()
+	public function config($tries = 1)
 	{
 	{
 		// Load config or default
 		// Load config or default
 		if (file_exists($this->userConfigPath)) {
 		if (file_exists($this->userConfigPath)) {
@@ -869,6 +870,10 @@ class Organizr
 		} else {
 		} else {
 			$config = $this->fillDefaultConfig($this->loadConfig($this->defaultConfigPath));
 			$config = $this->fillDefaultConfig($this->loadConfig($this->defaultConfigPath));
 		}
 		}
+		if ((!is_array($config) || !file_exists($this->userConfigPath)) && $tries < 5) {
+			$tries++;
+			return $this->config($tries);
+		}
 		return $config;
 		return $config;
 	}
 	}
 	
 	
@@ -1115,7 +1120,29 @@ class Organizr
 				'key' => 'groups'
 				'key' => 'groups'
 			),
 			),
 		];
 		];
-		return $this->processQueries($response);
+		$query = $this->processQueries($response);
+		$this->applyTabVariables($query['tabs']);
+		return $query;
+	}
+	
+	public function applyTabVariables($tabs)
+	{
+		$variables = [
+			'{domain}' => $this->getServer(),
+			'{username}' => $this->user['username'],
+			'{username_lower}' => $this->user['username'],
+			'{email}' => $this->user['email'],
+			'{group}' => $this->user['group'],
+			'{group_id}' => $this->user['groupID'],
+		];
+		if (empty($tabs)) {
+			return $tabs;
+		}
+		foreach ($tabs as $id => $tab) {
+			$tabs[$id]['url'] = $this->userDefinedIdReplacementLink($tab['url'], $variables);
+			$tabs[$id]['url_local'] = $this->userDefinedIdReplacementLink($tab['url'], $variables);
+		}
+		return $tabs;
 	}
 	}
 	
 	
 	public function getUsers()
 	public function getUsers()
@@ -3596,6 +3623,7 @@ class Organizr
 			),
 			),
 		];
 		];
 		$queries = $this->processQueries($response);
 		$queries = $this->processQueries($response);
+		$this->applyTabVariables($queries['tabs']);
 		$all['tabs'] = $queries['tabs'];
 		$all['tabs'] = $queries['tabs'];
 		foreach ($queries['tabs'] as $k => $v) {
 		foreach ($queries['tabs'] as $k => $v) {
 			$v['access_url'] = (!empty($v['url_local']) && ($v['url_local'] !== null) && ($v['url_local'] !== 'null') && $this->isLocal() && $v['type'] !== 0) ? $v['url_local'] : $v['url'];
 			$v['access_url'] = (!empty($v['url_local']) && ($v['url_local'] !== null) && ($v['url_local'] !== 'null') && $this->isLocal() && $v['type'] !== 0) ? $v['url_local'] : $v['url'];
@@ -4195,13 +4223,37 @@ class Organizr
 			if (strtolower($v['name']) === strtolower($item)) {
 			if (strtolower($v['name']) === strtolower($item)) {
 				$functionName = $v['settingsArray'];
 				$functionName = $v['settingsArray'];
 				return $this->$functionName();
 				return $this->$functionName();
-				
 			}
 			}
 		}
 		}
 		$this->setAPIResponse('error', 'Homepage item was not found', 404);
 		$this->setAPIResponse('error', 'Homepage item was not found', 404);
 		return null;
 		return null;
 	}
 	}
 	
 	
+	public function getSettingsHomepageItemDebug($service)
+	{
+		$service = $this->getSettingsHomepageItem($service);
+		if ($service) {
+			$debug = [];
+			foreach ($service['settings'] as $category => $items) {
+				if ($category !== 'About' && $category !== 'Test Connection') {
+					foreach ($items as $item) {
+						if ($item['type'] !== 'html' && $item['type'] !== 'blank' && $item['type'] !== 'button') {
+							if ((stripos($item['name'], 'token') !== false) || (stripos($item['name'], 'key') !== false) || (stripos($item['name'], 'password'))) {
+								if ($item['value'] !== '') {
+									$item['value'] = '**********';
+								}
+							}
+							$debug[$category][$item['name']] = $item['value'];
+						}
+					}
+				}
+			}
+			return $debug;
+		}
+		$this->setAPIResponse('error', 'Homepage item was not found', 404);
+		return null;
+	}
+	
 	public function getSettingsHomepage()
 	public function getSettingsHomepage()
 	{
 	{
 		$this->setGroupOptionsVariable();
 		$this->setGroupOptionsVariable();
@@ -6388,6 +6440,14 @@ class Organizr
 					'header' => null
 					'header' => null
 				];
 				];
 				break;
 				break;
+			case 'qbittorrent':
+				$appDetails = [
+					'url' => 'qBittorrentURL',
+					'enabled' => 'qBittorrentSocksEnabled',
+					'auth' => 'qBittorrentSocksAuth',
+					'header' => null
+				];
+				break;
 			default:
 			default:
 				$appDetails = null;
 				$appDetails = null;
 		}
 		}
@@ -6455,23 +6515,28 @@ class Organizr
 				'data' => $apiData
 				'data' => $apiData
 			];
 			];
 			//$this->debug(json_encode($debugInformation));
 			//$this->debug(json_encode($debugInformation));
-			switch ($requestObject->getMethod()) {
-				case 'GET':
-					$call = Requests::get($url, $headers, $options);
-					break;
-				case 'POST':
-					$call = Requests::post($url, $headers, $apiData, $options);
-					break;
-				case 'DELETE':
-					$call = Requests::delete($url, $headers, $options);
-					break;
-				case 'PUT':
-					$call = Requests::put($url, $headers, $apiData, $options);
-					break;
-				default:
-					$call = Requests::get($url, $headers, $options);
+			try {
+				switch ($requestObject->getMethod()) {
+					case 'GET':
+						$call = Requests::get($url, $headers, $options);
+						break;
+					case 'POST':
+						$call = Requests::post($url, $headers, $apiData, $options);
+						break;
+					case 'DELETE':
+						$call = Requests::delete($url, $headers, $options);
+						break;
+					case 'PUT':
+						$call = Requests::put($url, $headers, $apiData, $options);
+						break;
+					default:
+						$call = Requests::get($url, $headers, $options);
+				}
+				return $call->body;
+			} catch (Requests_Exception $e) {
+				$this->setAPIResponse('error', $e->getMessage(), 500);
+				return null;
 			}
 			}
-			return $call->body;
 		} else {
 		} else {
 			return null;
 			return null;
 		}
 		}

+ 5 - 5
api/composer.lock

@@ -8,16 +8,16 @@
     "packages": [
     "packages": [
         {
         {
             "name": "adldap2/adldap2",
             "name": "adldap2/adldap2",
-            "version": "v10.3.1",
+            "version": "v10.3.3",
             "source": {
             "source": {
                 "type": "git",
                 "type": "git",
                 "url": "https://github.com/Adldap2/Adldap2.git",
                 "url": "https://github.com/Adldap2/Adldap2.git",
-                "reference": "936a4e2eb925d005198f716a75bb78068c4de94d"
+                "reference": "c2a8f72455d3438377d955fc0f4b9ed836b47463"
             },
             },
             "dist": {
             "dist": {
                 "type": "zip",
                 "type": "zip",
-                "url": "https://api.github.com/repos/Adldap2/Adldap2/zipball/936a4e2eb925d005198f716a75bb78068c4de94d",
-                "reference": "936a4e2eb925d005198f716a75bb78068c4de94d",
+                "url": "https://api.github.com/repos/Adldap2/Adldap2/zipball/c2a8f72455d3438377d955fc0f4b9ed836b47463",
+                "reference": "c2a8f72455d3438377d955fc0f4b9ed836b47463",
                 "shasum": ""
                 "shasum": ""
             },
             },
             "require": {
             "require": {
@@ -69,7 +69,7 @@
                 "issues": "https://github.com/Adldap2/Adldap2/issues",
                 "issues": "https://github.com/Adldap2/Adldap2/issues",
                 "source": "https://github.com/Adldap2/Adldap2"
                 "source": "https://github.com/Adldap2/Adldap2"
             },
             },
-            "time": "2020-09-09T12:55:51+00:00"
+            "time": "2021-08-09T15:22:35+00:00"
         },
         },
         {
         {
             "name": "bogstag/oauth2-trakt",
             "name": "bogstag/oauth2-trakt",

+ 2 - 1
api/config/default.php

@@ -42,7 +42,6 @@ return array(
 	'embyTabName' => '',
 	'embyTabName' => '',
 	'embyURL' => '',
 	'embyURL' => '',
 	'embyToken' => '',
 	'embyToken' => '',
-	'jellyfinTabURL' => '',
 	'jellyfinTabName' => '',
 	'jellyfinTabName' => '',
 	'jellyfinURL' => '',
 	'jellyfinURL' => '',
 	'jellyfinToken' => '',
 	'jellyfinToken' => '',
@@ -130,6 +129,8 @@ return array(
 	'qBittorrentApiVersion' => '1',
 	'qBittorrentApiVersion' => '1',
 	'qBittorrentDisableCertCheck' => false,
 	'qBittorrentDisableCertCheck' => false,
 	'qBittorrentRefresh' => '60000',
 	'qBittorrentRefresh' => '60000',
+	'qBittorrentSocksEnabled' => false,
+	'qBittorrentSocksAuth' => '999',
 	'rTorrentURL' => '',
 	'rTorrentURL' => '',
 	'rTorrentURLOverride' => '',
 	'rTorrentURLOverride' => '',
 	'rTorrentUsername' => '',
 	'rTorrentUsername' => '',

+ 5 - 1
api/functions/config-functions.php

@@ -5,7 +5,11 @@ trait ConfigFunctions
 	public function getConfigItem($item)
 	public function getConfigItem($item)
 	{
 	{
 		if ($this->config[$item]) {
 		if ($this->config[$item]) {
-			$this->setAPIResponse('success', null, 200, $this->config[$item]);
+			$configItem = $this->config[$item];
+			if ($item == 'organizrHash') {
+				$configItem = '***Secure***';
+			}
+			$this->setAPIResponse('success', null, 200, $configItem);
 			return $this->config[$item];
 			return $this->config[$item];
 		} else {
 		} else {
 			$this->setAPIResponse('error', $item . ' is not defined or is blank', 404);
 			$this->setAPIResponse('error', $item . ' is not defined or is blank', 404);

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

@@ -562,6 +562,68 @@ trait NormalFunctions
 	{
 	{
 		return (false !== ($pos = strpos($src_str, $search_str))) ? substr_replace($src_str, $replacement_str, $pos, strlen($search_str)) : $src_str;
 		return (false !== ($pos = strpos($src_str, $search_str))) ? substr_replace($src_str, $replacement_str, $pos, strlen($search_str)) : $src_str;
 	}
 	}
+	
+	/**
+	 *  Check if an array is a multidimensional array.
+	 *
+	 * @param array $arr The array to check
+	 * @return  boolean       Whether the the array is a multidimensional array or not
+	 */
+	public function is_multi_array($x)
+	{
+		if (count(array_filter($x, 'is_array')) > 0) return true;
+		return false;
+	}
+	
+	/**
+	 *  Convert an object to an array.
+	 *
+	 * @param array $object The object to convert
+	 * @return  array            The converted array
+	 */
+	public function object_to_array($object)
+	{
+		if (!is_object($object) && !is_array($object)) return $object;
+		return array_map(array($this, 'object_to_array'), (array)$object);
+	}
+	
+	/**
+	 *  Check if a value exists in the array/object.
+	 *
+	 * @param mixed   $needle   The value that you are searching for
+	 * @param mixed   $haystack The array/object to search
+	 * @param boolean $strict   Whether to use strict search or not
+	 * @return  boolean             Whether the value was found or not
+	 */
+	public function search_for_value($needle, $haystack, $strict = true)
+	{
+		$haystack = $this->object_to_array($haystack);
+		if (is_array($haystack)) {
+			if ($this->is_multi_array($haystack)) {   // Multidimensional array
+				foreach ($haystack as $subhaystack) {
+					if ($this->search_for_value($needle, $subhaystack, $strict)) {
+						return true;
+					}
+				}
+			} elseif (array_keys($haystack) !== range(0, count($haystack) - 1)) {    // Associative array
+				foreach ($haystack as $key => $val) {
+					if ($needle == $val && !$strict) {
+						return true;
+					} elseif ($needle === $val && $strict) {
+						return true;
+					}
+				}
+				return false;
+			} else {    // Normal array
+				if ($needle == $haystack && !$strict) {
+					return true;
+				} elseif ($needle === $haystack && $strict) {
+					return true;
+				}
+			}
+		}
+		return false;
+	}
 }
 }
 
 
 // Leave for deluge class
 // Leave for deluge class

+ 19 - 1
api/functions/organizr-functions.php

@@ -499,17 +499,35 @@ trait OrganizrFunctions
 	
 	
 	public function checkFrame($array, $url)
 	public function checkFrame($array, $url)
 	{
 	{
+		
 		if (array_key_exists("x-frame-options", $array)) {
 		if (array_key_exists("x-frame-options", $array)) {
+			if (gettype($array['x-frame-options']) == 'array') {
+				$array['x-frame-options'] = $array['x-frame-options'][0];
+			}
+			$array['x-frame-options'] = strtolower($array['x-frame-options']);
 			if ($array['x-frame-options'] == "deny") {
 			if ($array['x-frame-options'] == "deny") {
 				return false;
 				return false;
 			} elseif ($array['x-frame-options'] == "sameorgin") {
 			} elseif ($array['x-frame-options'] == "sameorgin") {
 				$digest = parse_url($url);
 				$digest = parse_url($url);
-				$host = (isset($digest['host']) ? $digest['host'] : '');
+				$host = ($digest['host'] ?? '');
 				if ($this->getServer() == $host) {
 				if ($this->getServer() == $host) {
 					return true;
 					return true;
 				} else {
 				} else {
 					return false;
 					return false;
 				}
 				}
+			} elseif (strpos($array['x-frame-options'], 'allow-from') !== false) {
+				$explodeServers = explode(' ', $array['x-frame-options']);
+				$allowed = false;
+				foreach ($explodeServers as $server) {
+					$digest = parse_url($server);
+					$host = ($digest['host'] ?? '');
+					if ($this->getServer() == $host) {
+						$allowed = true;
+					}
+				}
+				return $allowed;
+			} else {
+				return false;
 			}
 			}
 		} else {
 		} else {
 			if (!$array) {
 			if (!$array) {

+ 1 - 0
api/homepage/calendar.php

@@ -15,6 +15,7 @@ trait CalendarHomepageItem
 			return $homepageInformation;
 			return $homepageInformation;
 		}
 		}
 		$homepageSettings = array(
 		$homepageSettings = array(
+			'debug' => true,
 			'settings' => array(
 			'settings' => array(
 				'Enable' => array(
 				'Enable' => array(
 					array(
 					array(

+ 1 - 0
api/homepage/couchpotato.php

@@ -16,6 +16,7 @@ trait CouchPotatoHomepageItem
 			return $homepageInformation;
 			return $homepageInformation;
 		}
 		}
 		$homepageSettings = array(
 		$homepageSettings = array(
+			'debug' => true,
 			'settings' => array(
 			'settings' => array(
 				'Enable' => array(
 				'Enable' => array(
 					array(
 					array(

+ 1 - 0
api/homepage/deluge.php

@@ -15,6 +15,7 @@ trait DelugeHomepageItem
 			return $homepageInformation;
 			return $homepageInformation;
 		}
 		}
 		$homepageSettings = array(
 		$homepageSettings = array(
+			'debug' => true,
 			'settings' => array(
 			'settings' => array(
 				'custom' => '
 				'custom' => '
 				<div class="row">
 				<div class="row">

+ 1 - 0
api/homepage/emby.php

@@ -15,6 +15,7 @@ trait EmbyHomepageItem
 			return $homepageInformation;
 			return $homepageInformation;
 		}
 		}
 		$homepageSettings = array(
 		$homepageSettings = array(
+			'debug' => true,
 			'settings' => array(
 			'settings' => array(
 				'Enable' => array(
 				'Enable' => array(
 					array(
 					array(

+ 1 - 0
api/homepage/healthchecks.php

@@ -15,6 +15,7 @@ trait HealthChecksHomepageItem
 			return $homepageInformation;
 			return $homepageInformation;
 		}
 		}
 		$homepageSettings = array(
 		$homepageSettings = array(
+			'debug' => true,
 			'settings' => array(
 			'settings' => array(
 				'Enable' => array(
 				'Enable' => array(
 					array(
 					array(

+ 2 - 0
api/homepage/html.php

@@ -15,6 +15,7 @@ trait HTMLHomepageItem
 			return $homepageInformation;
 			return $homepageInformation;
 		}
 		}
 		$homepageSettings = array(
 		$homepageSettings = array(
+			'debug' => true,
 			'settings' => array(
 			'settings' => array(
 				'Enable' => array(
 				'Enable' => array(
 					array(
 					array(
@@ -68,6 +69,7 @@ trait HTMLHomepageItem
 			'enabled' => strpos('personal,business', $this->config['license']) !== false,
 			'enabled' => strpos('personal,business', $this->config['license']) !== false,
 			'image' => 'plugins/images/tabs/custom2.png',
 			'image' => 'plugins/images/tabs/custom2.png',
 			'category' => 'Custom',
 			'category' => 'Custom',
+			'debug' => true,
 			'settings' => array(
 			'settings' => array(
 				'Enable' => array(
 				'Enable' => array(
 					array(
 					array(

+ 1 - 0
api/homepage/jackett.php

@@ -15,6 +15,7 @@ trait JackettHomepageItem
 			return $homepageInformation;
 			return $homepageInformation;
 		}
 		}
 		$homepageSettings = array(
 		$homepageSettings = array(
+			'debug' => true,
 			'settings' => array(
 			'settings' => array(
 				'Enable' => array(
 				'Enable' => array(
 					array(
 					array(

+ 1 - 0
api/homepage/jdownloader.php

@@ -15,6 +15,7 @@ trait JDownloaderHomepageItem
 			return $homepageInformation;
 			return $homepageInformation;
 		}
 		}
 		$homepageSettings = array(
 		$homepageSettings = array(
+			'debug' => true,
 			'settings' => array(
 			'settings' => array(
 				'custom' => '
 				'custom' => '
 				<div class="row">
 				<div class="row">

+ 3 - 9
api/homepage/jellyfin.php

@@ -16,6 +16,7 @@ trait JellyfinHomepageItem
 			return $homepageInformation;
 			return $homepageInformation;
 		}
 		}
 		$homepageSettings = array(
 		$homepageSettings = array(
+			'debug' => true,
 			'settings' => array(
 			'settings' => array(
 				'Enable' => array(
 				'Enable' => array(
 					array(
 					array(
@@ -126,13 +127,6 @@ trait JellyfinHomepageItem
 						'value' => $this->config['jellyfinTabName'],
 						'value' => $this->config['jellyfinTabName'],
 						'placeholder' => 'Only use if you have Jellyfin in a reverse proxy'
 						'placeholder' => 'Only use if you have Jellyfin in a reverse proxy'
 					),
 					),
-					array(
-						'type' => 'input',
-						'name' => 'jellyfinTabURL',
-						'label' => 'Jellyfin Tab WAN URL',
-						'value' => $this->config['jellyfinTabURL'],
-						'placeholder' => 'http(s)://hostname:port'
-					),
 					array(
 					array(
 						'type' => 'select',
 						'type' => 'select',
 						'name' => 'cacheImageSize',
 						'name' => 'cacheImageSize',
@@ -560,8 +554,8 @@ trait JellyfinHomepageItem
 		$jellyfinItem['address'] = $this->userDefinedIdReplacementLink($this->config['homepageJellyfinLink'], $jellfinVariablesForLink);
 		$jellyfinItem['address'] = $this->userDefinedIdReplacementLink($this->config['homepageJellyfinLink'], $jellfinVariablesForLink);
 		$jellyfinItem['nowPlayingOriginalImage'] = 'api/v2/homepage/image?source=jellyfin&type=' . $jellyfinItem['nowPlayingImageType'] . '&img=' . $jellyfinItem['nowPlayingThumb'] . '&height=' . $nowPlayingHeight . '&width=' . $nowPlayingWidth . '&key=' . $jellyfinItem['nowPlayingKey'] . '$' . $this->randString();
 		$jellyfinItem['nowPlayingOriginalImage'] = 'api/v2/homepage/image?source=jellyfin&type=' . $jellyfinItem['nowPlayingImageType'] . '&img=' . $jellyfinItem['nowPlayingThumb'] . '&height=' . $nowPlayingHeight . '&width=' . $nowPlayingWidth . '&key=' . $jellyfinItem['nowPlayingKey'] . '$' . $this->randString();
 		$jellyfinItem['originalImage'] = 'api/v2/homepage/image?source=jellyfin&type=' . $jellyfinItem['imageType'] . '&img=' . $jellyfinItem['thumb'] . '&height=' . $height . '&width=' . $width . '&key=' . $jellyfinItem['key'] . '$' . $this->randString();
 		$jellyfinItem['originalImage'] = 'api/v2/homepage/image?source=jellyfin&type=' . $jellyfinItem['imageType'] . '&img=' . $jellyfinItem['thumb'] . '&height=' . $height . '&width=' . $width . '&key=' . $jellyfinItem['key'] . '$' . $this->randString();
-		$jellyfinItem['openTab'] = $this->config['jellyfinTabURL'] && $this->config['jellyfinTabName'] ? true : false;
-		$jellyfinItem['tabName'] = $this->config['jellyfinTabName'] ? $this->config['jellyfinTabName'] : '';
+		$jellyfinItem['openTab'] = (bool)$this->config['jellyfinTabName'];
+		$jellyfinItem['tabName'] = $this->config['jellyfinTabName'] ?: '';
 		// Stream info
 		// Stream info
 		$jellyfinItem['userStream'] = array(
 		$jellyfinItem['userStream'] = array(
 			'platform' => @(string)$itemDetails['Client'],
 			'platform' => @(string)$itemDetails['Client'],

+ 1 - 0
api/homepage/lidarr.php

@@ -15,6 +15,7 @@ trait LidarrHomepageItem
 			return $homepageInformation;
 			return $homepageInformation;
 		}
 		}
 		$homepageSettings = array(
 		$homepageSettings = array(
+			'debug' => true,
 			'settings' => array(
 			'settings' => array(
 				'Enable' => array(
 				'Enable' => array(
 					array(
 					array(

+ 1 - 0
api/homepage/misc.php

@@ -15,6 +15,7 @@ trait MiscHomepageItem
 			return $homepageInformation;
 			return $homepageInformation;
 		}
 		}
 		$homepageSettings = array(
 		$homepageSettings = array(
+			'debug' => true,
 			'settings' => array(
 			'settings' => array(
 				'YouTube' => array(
 				'YouTube' => array(
 					array(
 					array(

+ 1 - 0
api/homepage/monitorr.php

@@ -15,6 +15,7 @@ trait MonitorrHomepageItem
 			return $homepageInformation;
 			return $homepageInformation;
 		}
 		}
 		$homepageSettings = array(
 		$homepageSettings = array(
+			'debug' => true,
 			'settings' => array(
 			'settings' => array(
 				'Enable' => array(
 				'Enable' => array(
 					array(
 					array(

+ 1 - 0
api/homepage/netdata.php

@@ -98,6 +98,7 @@ trait NetDataHomepageItem
 			return $homepageInformation;
 			return $homepageInformation;
 		}
 		}
 		$homepageSettings = array(
 		$homepageSettings = array(
+			'debug' => true,
 			'settings' => array(
 			'settings' => array(
 				'Enable' => array(
 				'Enable' => array(
 					array(
 					array(

+ 1 - 0
api/homepage/nzbget.php

@@ -15,6 +15,7 @@ trait NZBGetHomepageItem
 			return $homepageInformation;
 			return $homepageInformation;
 		}
 		}
 		$homepageSettings = array(
 		$homepageSettings = array(
+			'debug' => true,
 			'settings' => array(
 			'settings' => array(
 				'Enable' => array(
 				'Enable' => array(
 					array(
 					array(

+ 1 - 0
api/homepage/octoprint.php

@@ -15,6 +15,7 @@ trait OctoPrintHomepageItem
 			return $homepageInformation;
 			return $homepageInformation;
 		}
 		}
 		$homepageSettings = array(
 		$homepageSettings = array(
+			'debug' => true,
 			'settings' => array(
 			'settings' => array(
 				'Enable' => array(
 				'Enable' => array(
 					array(
 					array(

+ 2 - 1
api/homepage/ombi.php

@@ -16,6 +16,7 @@ trait OmbiHomepageItem
 			return $homepageInformation;
 			return $homepageInformation;
 		}
 		}
 		$homepageSettings = array(
 		$homepageSettings = array(
+			'debug' => true,
 			'settings' => array(
 			'settings' => array(
 				'Enable' => array(
 				'Enable' => array(
 					array(
 					array(
@@ -545,4 +546,4 @@ trait OmbiHomepageItem
 	{
 	{
 		return $type == $this->config['ombiTvDefault'];
 		return $type == $this->config['ombiTvDefault'];
 	}
 	}
-}
+}

+ 1 - 0
api/homepage/pihole.php

@@ -15,6 +15,7 @@ trait PiHoleHomepageItem
 			return $homepageInformation;
 			return $homepageInformation;
 		}
 		}
 		$homepageSettings = array(
 		$homepageSettings = array(
+			'debug' => true,
 			'settings' => array(
 			'settings' => array(
 				'Enable' => array(
 				'Enable' => array(
 					array(
 					array(

+ 19 - 27
api/homepage/plex.php

@@ -37,6 +37,7 @@ trait PlexHomepageItem
 			'enabled' => strpos('personal', $this->config['license']) !== false,
 			'enabled' => strpos('personal', $this->config['license']) !== false,
 			'image' => 'plugins/images/tabs/plex.png',
 			'image' => 'plugins/images/tabs/plex.png',
 			'category' => 'Media Server',
 			'category' => 'Media Server',
+			'debug' => true,
 			'settings' => array(
 			'settings' => array(
 				'Enable' => array(
 				'Enable' => array(
 					array(
 					array(
@@ -267,31 +268,8 @@ trait PlexHomepageItem
 					array(
 					array(
 						'type' => 'switch',
 						'type' => 'switch',
 						'name' => 'homepageUseCustomStreamNames',
 						'name' => 'homepageUseCustomStreamNames',
-						'label' => 'Use custom names for users',
+						'label' => 'Use Tautulli custom names for users',
 						'value' => $this->config['homepageUseCustomStreamNames']
 						'value' => $this->config['homepageUseCustomStreamNames']
-					),
-					array(
-						'type' => 'html',
-						'name' => 'grabFromTautulli',
-						'label' => 'Grab from Tautulli. (Note, you must have set the Tautulli API key already)',
-						'override' => 6,
-						'html' => '<button type="button" onclick="getTautulliFriendlyNames()" class="btn btn-sm btn-success btn-rounded waves-effect waves-light b-none">Grab Names</button>',
-					),
-					array(
-						'type' => 'html',
-						'name' => 'homepageCustomStreamNamesAce',
-						'class' => 'jsonTextarea hidden',
-						'label' => 'Custom definitions for user names (JSON Object, with the key being the plex name, and the value what you want to override with)',
-						'override' => 12,
-						'html' => '<div id="homepageCustomStreamNamesAce" style="height: 300px;">' . htmlentities($this->config['homepageCustomStreamNames']) . '</div>',
-					),
-					array(
-						'type' => 'textbox',
-						'name' => 'homepageCustomStreamNames',
-						'class' => 'jsonTextarea hidden',
-						'id' => 'homepageCustomStreamNamesText',
-						'label' => '',
-						'value' => $this->config['homepageCustomStreamNames'],
 					)
 					)
 				),
 				),
 				'Test Connection' => array(
 				'Test Connection' => array(
@@ -477,6 +455,7 @@ trait PlexHomepageItem
 		if (!$this->homepageItemPermissions($this->plexHomepagePermissions('streams'), true)) {
 		if (!$this->homepageItemPermissions($this->plexHomepagePermissions('streams'), true)) {
 			return false;
 			return false;
 		}
 		}
+		$this->setTautulliFriendlyNames();
 		$ignore = array();
 		$ignore = array();
 		$exclude = explode(',', $this->config['homepagePlexStreamsExclude']);
 		$exclude = explode(',', $this->config['homepagePlexStreamsExclude']);
 		$resolve = true;
 		$resolve = true;
@@ -848,16 +827,16 @@ trait PlexHomepageItem
 		return $plexItem;
 		return $plexItem;
 	}
 	}
 	
 	
-	public function getTautulliFriendlyNames()
+	public function getTautulliFriendlyNames($bypass = null)
 	{
 	{
-		if (!$this->qualifyRequest(1)) {
+		$names = [];
+		if (!$this->qualifyRequest(1) && !$bypass) {
 			return false;
 			return false;
 		}
 		}
 		$url = $this->qualifyURL($this->config['tautulliURL']);
 		$url = $this->qualifyURL($this->config['tautulliURL']);
 		$url .= '/api/v2?apikey=' . $this->config['tautulliApikey'];
 		$url .= '/api/v2?apikey=' . $this->config['tautulliApikey'];
 		$url .= '&cmd=get_users';
 		$url .= '&cmd=get_users';
 		$response = Requests::get($url, [], []);
 		$response = Requests::get($url, [], []);
-		$names = [];
 		try {
 		try {
 			$response = json_decode($response->body, true);
 			$response = json_decode($response->body, true);
 			foreach ($response['response']['data'] as $user) {
 			foreach ($response['response']['data'] as $user) {
@@ -869,6 +848,19 @@ trait PlexHomepageItem
 			$this->setAPIResponse('failure', null, 422, [$e->getMessage()]);
 			$this->setAPIResponse('failure', null, 422, [$e->getMessage()]);
 		}
 		}
 		$this->setAPIResponse('success', null, 200, $names);
 		$this->setAPIResponse('success', null, 200, $names);
+		return $names;
+	}
+	
+	public function setTautulliFriendlyNames()
+	{
+		if ($this->config['tautulliURL'] && $this->config['tautulliApikey'] && $this->config['homepageUseCustomStreamNames']) {
+			$names = $this->getTautulliFriendlyNames(true);
+			if (json_encode($names) !== $this->config['homepageCustomStreamNames']) {
+				$this->updateConfig(array('homepageCustomStreamNames' => json_encode($names)));
+				$this->config['homepageCustomStreamNames'] = json_encode($names);
+				$this->debug('Updating Tautulli custom names config item', 'SYSTEM');
+			}
+		}
 	}
 	}
 	
 	
 	private function formatPlexUserName($item)
 	private function formatPlexUserName($item)

+ 27 - 0
api/homepage/qbittorrent.php

@@ -15,6 +15,7 @@ trait QBitTorrentHomepageItem
 			return $homepageInformation;
 			return $homepageInformation;
 		}
 		}
 		$homepageSettings = array(
 		$homepageSettings = array(
+			'debug' => true,
 			'settings' => array(
 			'settings' => array(
 				'Enable' => array(
 				'Enable' => array(
 					array(
 					array(
@@ -66,6 +67,32 @@ trait QBitTorrentHomepageItem
 						'value' => $this->config['qBittorrentPassword']
 						'value' => $this->config['qBittorrentPassword']
 					)
 					)
 				),
 				),
+				'API SOCKS' => array(
+					array(
+						'type' => 'html',
+						'override' => 12,
+						'label' => '',
+						'html' => '
+							<div class="panel panel-default">
+								<div class="panel-wrapper collapse in">
+									<div class="panel-body">' . $this->socksHeadingHTML('qbittorrent') . '</div>
+								</div>
+							</div>'
+					),
+					array(
+						'type' => 'switch',
+						'name' => 'qBittorrentSocksEnabled',
+						'label' => 'Enable',
+						'value' => $this->config['qBittorrentSocksEnabled']
+					),
+					array(
+						'type' => 'select',
+						'name' => 'qBittorrentSocksAuth',
+						'label' => 'Minimum Authentication',
+						'value' => $this->config['qBittorrentSocksAuth'],
+						'options' => $this->groupOptions
+					),
+				),
 				'Misc Options' => array(
 				'Misc Options' => array(
 					array(
 					array(
 						'type' => 'switch',
 						'type' => 'switch',

+ 1 - 0
api/homepage/radarr.php

@@ -15,6 +15,7 @@ trait RadarrHomepageItem
 			return $homepageInformation;
 			return $homepageInformation;
 		}
 		}
 		$homepageSettings = array(
 		$homepageSettings = array(
+			'debug' => true,
 			'settings' => array(
 			'settings' => array(
 				'Enable' => array(
 				'Enable' => array(
 					array(
 					array(

+ 1 - 0
api/homepage/rtorrent.php

@@ -17,6 +17,7 @@ trait RTorrentHomepageItem
 		}
 		}
 		$xmlStatus = (extension_loaded('xmlrpc')) ? 'Installed' : 'Not Installed';
 		$xmlStatus = (extension_loaded('xmlrpc')) ? 'Installed' : 'Not Installed';
 		$homepageSettings = array(
 		$homepageSettings = array(
+			'debug' => true,
 			'settings' => array(
 			'settings' => array(
 				'FYI' => array(
 				'FYI' => array(
 					array(
 					array(

+ 1 - 0
api/homepage/sabnzbd.php

@@ -16,6 +16,7 @@ trait SabNZBdHomepageItem
 			return $homepageInformation;
 			return $homepageInformation;
 		}
 		}
 		$homepageSettings = array(
 		$homepageSettings = array(
+			'debug' => true,
 			'settings' => array(
 			'settings' => array(
 				'Enable' => array(
 				'Enable' => array(
 					array(
 					array(

+ 1 - 0
api/homepage/sickrage.php

@@ -15,6 +15,7 @@ trait SickRageHomepageItem
 			return $homepageInformation;
 			return $homepageInformation;
 		}
 		}
 		$homepageSettings = array(
 		$homepageSettings = array(
+			'debug' => true,
 			'settings' => array(
 			'settings' => array(
 				'Enable' => array(
 				'Enable' => array(
 					array(
 					array(

+ 3 - 2
api/homepage/sonarr.php

@@ -16,6 +16,7 @@ trait SonarrHomepageItem
 		}
 		}
 		$homepageSettings = array(
 		$homepageSettings = array(
 			'docs' => 'https://docs.organizr.app/books/setup-features/page/sonarr',
 			'docs' => 'https://docs.organizr.app/books/setup-features/page/sonarr',
+			'debug' => true,
 			'settings' => array(
 			'settings' => array(
 				'About' => array(
 				'About' => array(
 					array(
 					array(
@@ -59,7 +60,7 @@ trait SonarrHomepageItem
 						'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
 						'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
 						'placeholder' => 'http(s)://hostname:port',
 						'placeholder' => 'http(s)://hostname:port',
 						'options' => $this->makeOptionsFromValues($this->config['sonarrURL']),
 						'options' => $this->makeOptionsFromValues($this->config['sonarrURL']),
-						'settings' => '{tags: true}',
+						'settings' => '{tags: true, selectOnClose: true, closeOnSelect: true}',
 					),
 					),
 					array(
 					array(
 						'type' => 'select2',
 						'type' => 'select2',
@@ -69,7 +70,7 @@ trait SonarrHomepageItem
 						'label' => 'Sonarr Token',
 						'label' => 'Sonarr Token',
 						'value' => $this->config['sonarrToken'],
 						'value' => $this->config['sonarrToken'],
 						'options' => $this->makeOptionsFromValues($this->config['sonarrToken']),
 						'options' => $this->makeOptionsFromValues($this->config['sonarrToken']),
-						'settings' => '{tags: true, theme: "default password-alt"}',
+						'settings' => '{tags: true, theme: "default password-alt, selectOnClose: true, closeOnSelect: true"}',
 					)
 					)
 				),
 				),
 				'API SOCKS' => array(
 				'API SOCKS' => array(

+ 1 - 0
api/homepage/speedtest.php

@@ -15,6 +15,7 @@ trait SpeedTestHomepageItem
 			return $homepageInformation;
 			return $homepageInformation;
 		}
 		}
 		$homepageSettings = array(
 		$homepageSettings = array(
+			'debug' => true,
 			'settings' => array(
 			'settings' => array(
 				'Enable' => array(
 				'Enable' => array(
 					array(
 					array(

+ 1 - 0
api/homepage/tautulli.php

@@ -15,6 +15,7 @@ trait TautulliHomepageItem
 			return $homepageInformation;
 			return $homepageInformation;
 		}
 		}
 		$homepageSettings = array(
 		$homepageSettings = array(
+			'debug' => true,
 			'settings' => array(
 			'settings' => array(
 				'Enable' => array(
 				'Enable' => array(
 					array(
 					array(

+ 1 - 0
api/homepage/trakt.php

@@ -16,6 +16,7 @@ trait TraktHomepageItem
 		}
 		}
 		$homepageSettings = array(
 		$homepageSettings = array(
 			'docs' => 'https://docs.organizr.app/books/setup-features/page/trakt',
 			'docs' => 'https://docs.organizr.app/books/setup-features/page/trakt',
+			'debug' => true,
 			'settings' => array(
 			'settings' => array(
 				'About' => array(
 				'About' => array(
 					array(
 					array(

+ 1 - 0
api/homepage/transmission.php

@@ -15,6 +15,7 @@ trait TransmissionHomepageItem
 			return $homepageInformation;
 			return $homepageInformation;
 		}
 		}
 		$homepageSettings = array(
 		$homepageSettings = array(
+			'debug' => true,
 			'settings' => array(
 			'settings' => array(
 				'Enable' => array(
 				'Enable' => array(
 					array(
 					array(

+ 1 - 0
api/homepage/unifi.php

@@ -15,6 +15,7 @@ trait UnifiHomepageItem
 			return $homepageInformation;
 			return $homepageInformation;
 		}
 		}
 		$homepageSettings = array(
 		$homepageSettings = array(
+			'debug' => true,
 			'settings' => array(
 			'settings' => array(
 				'Enable' => array(
 				'Enable' => array(
 					array(
 					array(

+ 1 - 0
api/homepage/weather.php

@@ -15,6 +15,7 @@ trait WeatherHomepageItem
 			return $homepageInformation;
 			return $homepageInformation;
 		}
 		}
 		$homepageSettings = array(
 		$homepageSettings = array(
+			'debug' => true,
 			'settings' => array(
 			'settings' => array(
 				'Enable' => array(
 				'Enable' => array(
 					array(
 					array(

+ 1 - 1
api/pages/homepage.php

@@ -107,4 +107,4 @@ $(".homepage-loading-box").fadeOut(5000);
 <!-- /.container-fluid -->
 <!-- /.container-fluid -->
 
 
 ';
 ';
-}
+}

+ 2 - 2
api/pages/settings-customize-appearance.php

@@ -22,10 +22,10 @@ function get_page_settings_customize_appearance($Organizr)
 		<button id="customize-appearance-form-save" onclick="submitSettingsForm(\'customize-appearance-form\')" class="btn btn-sm btn-info btn-rounded waves-effect waves-light pull-right hidden animated loop-animation rubberBand" type="button"><span class="btn-label"><i class="fa fa-save"></i></span><span lang="en">Save</span></button>
 		<button id="customize-appearance-form-save" onclick="submitSettingsForm(\'customize-appearance-form\')" class="btn btn-sm btn-info btn-rounded waves-effect waves-light pull-right hidden animated loop-animation rubberBand" type="button"><span class="btn-label"><i class="fa fa-save"></i></span><span lang="en">Save</span></button>
 	</div>
 	</div>
     <div class="panel-wrapper collapse in" aria-expanded="true">
     <div class="panel-wrapper collapse in" aria-expanded="true">
-        <div class="panel-body bg-org">
+        <div class="bg-org">
             <form id="customize-appearance-form" class="addFormTick" onsubmit="return false;"></form>
             <form id="customize-appearance-form" class="addFormTick" onsubmit="return false;"></form>
         </div>
         </div>
     </div>
     </div>
 </div>
 </div>
 ';
 ';
-}
+}

+ 2 - 2
api/pages/settings-settings-main.php

@@ -21,7 +21,7 @@ function get_page_settings_settings_main($Organizr)
 		<button id="settings-main-form-save" onclick="submitSettingsForm(\'settings-main-form\')" class="btn btn-sm btn-info btn-rounded waves-effect waves-light pull-right hidden animated loop-animation rubberBand" type="button"><span class="btn-label"><i class="fa fa-save"></i></span><span lang="en">Save</span></button>
 		<button id="settings-main-form-save" onclick="submitSettingsForm(\'settings-main-form\')" class="btn btn-sm btn-info btn-rounded waves-effect waves-light pull-right hidden animated loop-animation rubberBand" type="button"><span class="btn-label"><i class="fa fa-save"></i></span><span lang="en">Save</span></button>
 	</div>
 	</div>
     <div class="panel-wrapper collapse in" aria-expanded="true">
     <div class="panel-wrapper collapse in" aria-expanded="true">
-        <div class="panel-body bg-org">
+        <div class="bg-org">
             <form id="settings-main-form" class="addFormTick" onsubmit="return false;"></form>
             <form id="settings-main-form" class="addFormTick" onsubmit="return false;"></form>
         </div>
         </div>
     </div>
     </div>
@@ -58,4 +58,4 @@ function get_page_settings_settings_main($Organizr)
     <div class="clearfix"></div>
     <div class="clearfix"></div>
 </form>
 </form>
 ';
 ';
-}
+}

+ 2 - 2
api/pages/settings-settings-sso.php

@@ -21,10 +21,10 @@ function get_page_settings_settings_sso($Organizr)
 		<button id="sso-form-save" onclick="submitSettingsForm(\'sso-form\')" class="btn btn-sm btn-info btn-rounded waves-effect waves-light pull-right hidden animated loop-animation rubberBand" type="button"><span class="btn-label"><i class="fa fa-save"></i></span><span lang="en">Save</span></button>
 		<button id="sso-form-save" onclick="submitSettingsForm(\'sso-form\')" class="btn btn-sm btn-info btn-rounded waves-effect waves-light pull-right hidden animated loop-animation rubberBand" type="button"><span class="btn-label"><i class="fa fa-save"></i></span><span lang="en">Save</span></button>
 	</div>
 	</div>
     <div class="panel-wrapper collapse in" aria-expanded="true">
     <div class="panel-wrapper collapse in" aria-expanded="true">
-        <div class="panel-body bg-org">
+        <div class="bg-org">
             <form id="sso-form" class="addFormTick" onsubmit="return false;"></form>
             <form id="sso-form" class="addFormTick" onsubmit="return false;"></form>
         </div>
         </div>
     </div>
     </div>
 </div>
 </div>
 ';
 ';
-}
+}

+ 10 - 0
api/plugins/invites/plugin.php

@@ -190,6 +190,16 @@ class Invites extends Organizr
 							foreach ($plex->Server->Section as $child) {
 							foreach ($plex->Server->Section as $child) {
 								$libraryList['libraries'][(string)$child['title']] = (string)$child['id'];
 								$libraryList['libraries'][(string)$child['title']] = (string)$child['id'];
 							}
 							}
+							if ($this->config['INVITES-plexLibraries'] !== '') {
+								$noLongerId = 0;
+								$libraries = explode(',', $this->config['INVITES-plexLibraries']);
+								foreach ($libraries as $child) {
+									if ($this->search_for_value($child, $libraryList)) {
+										$libraryList['libraries']['No Longer Exists - ' . $noLongerId] = $child;
+										$noLongerId++;
+									}
+								}
+							}
 							$libraryList = array_change_key_case($libraryList, CASE_LOWER);
 							$libraryList = array_change_key_case($libraryList, CASE_LOWER);
 							return $libraryList;
 							return $libraryList;
 						}
 						}

+ 10 - 0
api/v2/routes/settings.php

@@ -48,4 +48,14 @@ $app->get('/settings/homepage/{item}', function ($request, $response, $args) {
 	return $response
 	return $response
 		->withHeader('Content-Type', 'application/json;charset=UTF-8')
 		->withHeader('Content-Type', 'application/json;charset=UTF-8')
 		->withStatus($GLOBALS['responseCode']);
 		->withStatus($GLOBALS['responseCode']);
+});
+$app->get('/settings/homepage/{item}/debug', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$GLOBALS['api']['response']['data'] = $Organizr->getSettingsHomepageItemDebug($args['item']);
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
 });
 });

+ 2 - 2
api/vendor/adldap2/adldap2/readme.md

@@ -9,12 +9,12 @@
 <p align="center">
 <p align="center">
     <strong>
     <strong>
         <a href="https://ldaprecord.com">LdapRecord</a> is the successor to Adldap2 - and comes with a ton of new features.
         <a href="https://ldaprecord.com">LdapRecord</a> is the successor to Adldap2 - and comes with a ton of new features.
-    </strong> </br> Adldap2 will continue to be supported with bug fixes, but will not receive new features.
+    </strong> </br> Adldap2 will continue to be supported with bug fixes, <i>but will not receive new features.</i>
 </p>
 </p>
 
 
 <p align="center">
 <p align="center">
  <strong>
  <strong>
- <a href="https://stevebauman.ca/posts/why-ldap-record/">Read Why</a>
+ <a href="https://stevebauman.ca/why-ldap-record/">Read Why</a>
  </strong>
  </strong>
 </p>
 </p>
 
 

+ 2 - 4
api/vendor/adldap2/adldap2/src/Adldap.php

@@ -153,10 +153,8 @@ class Adldap implements AdldapInterface
     public function __call($method, $parameters)
     public function __call($method, $parameters)
     {
     {
         $provider = $this->getDefaultProvider();
         $provider = $this->getDefaultProvider();
-
-        if (!$provider->getConnection()->isBound()) {
-            // We'll make sure we have a bound connection before
-            // allowing dynamic calls on the default provider.
+        
+        if (! $provider->getConnection()->isBound()) {
             $provider->connect();
             $provider->connect();
         }
         }
 
 

+ 3 - 3
api/vendor/adldap2/adldap2/src/Connections/ConnectionInterface.php

@@ -120,7 +120,7 @@ interface ConnectionInterface
      *
      *
      * @link http://php.net/manual/en/function.ldap-get-entries.php
      * @link http://php.net/manual/en/function.ldap-get-entries.php
      *
      *
-     * @param $searchResult
+     * @param resource $searchResult
      *
      *
      * @return mixed
      * @return mixed
      */
      */
@@ -131,7 +131,7 @@ interface ConnectionInterface
      *
      *
      * @link http://php.net/manual/en/function.ldap-count-entries.php
      * @link http://php.net/manual/en/function.ldap-count-entries.php
      *
      *
-     * @param $searchResult
+     * @param resource $searchResult
      *
      *
      * @return int
      * @return int
      */
      */
@@ -155,7 +155,7 @@ interface ConnectionInterface
      *
      *
      * @link http://php.net/manual/en/function.ldap-first-entry.php
      * @link http://php.net/manual/en/function.ldap-first-entry.php
      *
      *
-     * @param $searchResult
+     * @param resource $searchResult
      *
      *
      * @return mixed
      * @return mixed
      */
      */

+ 24 - 26
api/vendor/adldap2/adldap2/src/Query/Builder.php

@@ -509,16 +509,15 @@ class Builder
         do {
         do {
             $this->connection->controlPagedResult($perPage, $isCritical, $cookie);
             $this->connection->controlPagedResult($perPage, $isCritical, $cookie);
 
 
-            // Run the search.
-            $resource = $this->run($filter);
+            if (! $resource = $this->run($filter)) {
+                break;
+            }
 
 
-            if ($resource) {
-                // If we have been given a valid resource, we will retrieve the next
-                // pagination cookie to send for our next pagination request.
-                $this->connection->controlPagedResultResponse($resource, $cookie);
+            // If we have been given a valid resource, we will retrieve the next
+            // pagination cookie to send for our next pagination request.
+            $this->connection->controlPagedResultResponse($resource, $cookie);
 
 
-                $pages[] = $this->parse($resource);
-            }
+            $pages[] = $this->parse($resource);
         } while (!empty($cookie));
         } while (!empty($cookie));
 
 
         // Reset paged result on the current connection. We won't pass in the current $perPage
         // Reset paged result on the current connection. We won't pass in the current $perPage
@@ -559,23 +558,22 @@ class Builder
             // Update the server controls.
             // Update the server controls.
             $this->connection->setOption(LDAP_OPT_SERVER_CONTROLS, $controls);
             $this->connection->setOption(LDAP_OPT_SERVER_CONTROLS, $controls);
 
 
-            // Run the search.
-            $resource = $this->run($filter);
+            if (! $resource = $this->run($filter)) {
+                break;
+            }
 
 
-            if ($resource) {
-                $errorCode = $dn = $errorMessage = $refs = null;
+            $errorCode = $dn = $errorMessage = $refs = null;
 
 
-                // Update the server controls with the servers response.
-                $this->connection->parseResult($resource, $errorCode, $dn, $errorMessage, $refs, $controls);
+            // Update the server controls with the servers response.
+            $this->connection->parseResult($resource, $errorCode, $dn, $errorMessage, $refs, $controls);
 
 
-                $pages[] = $this->parse($resource);
+            $pages[] = $this->parse($resource);
 
 
-                // Reset paged result on the current connection. We won't pass in the current $perPage
-                // parameter since we want to reset the page size to the default '1000'. Sending '0'
-                // eliminates any further opportunity for running queries in the same request,
-                // even though that is supposed to be the correct usage.
-                $controls[LDAP_CONTROL_PAGEDRESULTS]['value']['size'] = $perPage;
-            }
+            // Reset paged result on the current connection. We won't pass in the current $perPage
+            // parameter since we want to reset the page size to the default '1000'. Sending '0'
+            // eliminates any further opportunity for running queries in the same request,
+            // even though that is supposed to be the correct usage.
+            $controls[LDAP_CONTROL_PAGEDRESULTS]['value']['size'] = $perPage;
         } while (!empty($controls[LDAP_CONTROL_PAGEDRESULTS]['value']['cookie']));
         } while (!empty($controls[LDAP_CONTROL_PAGEDRESULTS]['value']['cookie']));
 
 
         // After running the query, we will clear the LDAP server controls. This
         // After running the query, we will clear the LDAP server controls. This
@@ -595,13 +593,13 @@ class Builder
      */
      */
     protected function parse($resource)
     protected function parse($resource)
     {
     {
-        // Normalize entries. Get entries returns false on failure.
-        // We'll always want an array in this situation.
-        $entries = $this->connection->getEntries($resource) ?: [];
-
-        // Free up memory.
         if (is_resource($resource)) {
         if (is_resource($resource)) {
+            $entries = $this->connection->getEntries($resource);
+            
+            // Free up memory.
             $this->connection->freeResult($resource);
             $this->connection->freeResult($resource);
+        } else {
+            $entries = [];
         }
         }
 
 
         return $entries;
         return $entries;

+ 5 - 5
api/vendor/composer/InstalledVersions.php

@@ -29,7 +29,7 @@ private static $installed = array (
     'aliases' => 
     'aliases' => 
     array (
     array (
     ),
     ),
-    'reference' => '414dc4c37d9f1ea1739e52365c87922321a4f984',
+    'reference' => '7bb47ec76fe2d84d5bb6e4102d47337ceb00fcec',
     'name' => '__root__',
     'name' => '__root__',
   ),
   ),
   'versions' => 
   'versions' => 
@@ -41,16 +41,16 @@ private static $installed = array (
       'aliases' => 
       'aliases' => 
       array (
       array (
       ),
       ),
-      'reference' => '414dc4c37d9f1ea1739e52365c87922321a4f984',
+      'reference' => '7bb47ec76fe2d84d5bb6e4102d47337ceb00fcec',
     ),
     ),
     'adldap2/adldap2' => 
     'adldap2/adldap2' => 
     array (
     array (
-      'pretty_version' => 'v10.3.1',
-      'version' => '10.3.1.0',
+      'pretty_version' => 'v10.3.3',
+      'version' => '10.3.3.0',
       'aliases' => 
       'aliases' => 
       array (
       array (
       ),
       ),
-      'reference' => '936a4e2eb925d005198f716a75bb78068c4de94d',
+      'reference' => 'c2a8f72455d3438377d955fc0f4b9ed836b47463',
     ),
     ),
     'bogstag/oauth2-trakt' => 
     'bogstag/oauth2-trakt' => 
     array (
     array (

+ 6 - 6
api/vendor/composer/installed.json

@@ -2,17 +2,17 @@
     "packages": [
     "packages": [
         {
         {
             "name": "adldap2/adldap2",
             "name": "adldap2/adldap2",
-            "version": "v10.3.1",
-            "version_normalized": "10.3.1.0",
+            "version": "v10.3.3",
+            "version_normalized": "10.3.3.0",
             "source": {
             "source": {
                 "type": "git",
                 "type": "git",
                 "url": "https://github.com/Adldap2/Adldap2.git",
                 "url": "https://github.com/Adldap2/Adldap2.git",
-                "reference": "936a4e2eb925d005198f716a75bb78068c4de94d"
+                "reference": "c2a8f72455d3438377d955fc0f4b9ed836b47463"
             },
             },
             "dist": {
             "dist": {
                 "type": "zip",
                 "type": "zip",
-                "url": "https://api.github.com/repos/Adldap2/Adldap2/zipball/936a4e2eb925d005198f716a75bb78068c4de94d",
-                "reference": "936a4e2eb925d005198f716a75bb78068c4de94d",
+                "url": "https://api.github.com/repos/Adldap2/Adldap2/zipball/c2a8f72455d3438377d955fc0f4b9ed836b47463",
+                "reference": "c2a8f72455d3438377d955fc0f4b9ed836b47463",
                 "shasum": ""
                 "shasum": ""
             },
             },
             "require": {
             "require": {
@@ -31,7 +31,7 @@
             "suggest": {
             "suggest": {
                 "ext-fileinfo": "fileinfo is required when retrieving user encoded thumbnails"
                 "ext-fileinfo": "fileinfo is required when retrieving user encoded thumbnails"
             },
             },
-            "time": "2020-09-09T12:55:51+00:00",
+            "time": "2021-08-09T15:22:35+00:00",
             "type": "library",
             "type": "library",
             "installation-source": "dist",
             "installation-source": "dist",
             "autoload": {
             "autoload": {

+ 5 - 5
api/vendor/composer/installed.php

@@ -6,7 +6,7 @@
     'aliases' => 
     'aliases' => 
     array (
     array (
     ),
     ),
-    'reference' => '414dc4c37d9f1ea1739e52365c87922321a4f984',
+    'reference' => '7bb47ec76fe2d84d5bb6e4102d47337ceb00fcec',
     'name' => '__root__',
     'name' => '__root__',
   ),
   ),
   'versions' => 
   'versions' => 
@@ -18,16 +18,16 @@
       'aliases' => 
       'aliases' => 
       array (
       array (
       ),
       ),
-      'reference' => '414dc4c37d9f1ea1739e52365c87922321a4f984',
+      'reference' => '7bb47ec76fe2d84d5bb6e4102d47337ceb00fcec',
     ),
     ),
     'adldap2/adldap2' => 
     'adldap2/adldap2' => 
     array (
     array (
-      'pretty_version' => 'v10.3.1',
-      'version' => '10.3.1.0',
+      'pretty_version' => 'v10.3.3',
+      'version' => '10.3.3.0',
       'aliases' => 
       'aliases' => 
       array (
       array (
       ),
       ),
-      'reference' => '936a4e2eb925d005198f716a75bb78068c4de94d',
+      'reference' => 'c2a8f72455d3438377d955fc0f4b9ed836b47463',
     ),
     ),
     'bogstag/oauth2-trakt' => 
     'bogstag/oauth2-trakt' => 
     array (
     array (

+ 5 - 1
css/themes/Organizr.css

@@ -310,7 +310,7 @@ a.mytooltip {
   background-color: #1f1f1f!important;
   background-color: #1f1f1f!important;
 }
 }
 .asColorPicker-dropdown, .checkbox label::before, .clockpicker-popover .popover-content, .clockpicker-popover .popover-title, .customvtab .tabs-vertical li.active a, .customvtab .tabs-vertical li.active a:focus, .customvtab .tabs-vertical li.active a:hover, .dropdown-menu, .fc-day, .footable-row-detail, .img-thumbnail, .input-group-addon, .jsgrid-filter-row>td, .jsgrid-header-sortable:hover, .nav-tabs>li>a:focus, .nav-tabs>li>a:hover, .pager .disabled>a, .pager .disabled>a:focus, .pager .disabled>a:hover, .pager .disabled>span, .pager li>a:focus, .pager li>a:hover, .pagination>.disabled>a, .pagination>.disabled>a:focus, .pagination>.disabled>a:hover, .pagination>.disabled>span, .pagination>.disabled>span:focus, .pagination>.disabled>span:hover, .popover, .popover-title, .right-sidebar, code, table.dataTable tbody tr {
 .asColorPicker-dropdown, .checkbox label::before, .clockpicker-popover .popover-content, .clockpicker-popover .popover-title, .customvtab .tabs-vertical li.active a, .customvtab .tabs-vertical li.active a:focus, .customvtab .tabs-vertical li.active a:hover, .dropdown-menu, .fc-day, .footable-row-detail, .img-thumbnail, .input-group-addon, .jsgrid-filter-row>td, .jsgrid-header-sortable:hover, .nav-tabs>li>a:focus, .nav-tabs>li>a:hover, .pager .disabled>a, .pager .disabled>a:focus, .pager .disabled>a:hover, .pager .disabled>span, .pager li>a:focus, .pager li>a:hover, .pagination>.disabled>a, .pagination>.disabled>a:focus, .pagination>.disabled>a:hover, .pagination>.disabled>span, .pagination>.disabled>span:focus, .pagination>.disabled>span:hover, .popover, .popover-title, .right-sidebar, code, table.dataTable tbody tr {
-  background: #1b1b1b;
+  background: #111;
 }
 }
 .table>tbody>tr>td, .table>tbody>tr>th, .table>tfoot>tr>td, .table>tfoot>tr>th, .table>thead>tr>td, .table>thead>tr>th {
 .table>tbody>tr>td, .table>tbody>tr>th, .table>tfoot>tr>td, .table>tfoot>tr>th, .table>thead>tr>td, .table>thead>tr>th {
   padding: 8px;
   padding: 8px;
@@ -358,4 +358,8 @@ span.select2-results {
 }
 }
 #organizrNewsPanel .panel-body {
 #organizrNewsPanel .panel-body {
     background: #2d2c2c;
     background: #2d2c2c;
+}
+.nav>li>a:focus, .nav>li>a:hover {
+    text-decoration: none;
+    background-color: #191919;
 }
 }

+ 0 - 18
js/custom.js

@@ -1826,25 +1826,7 @@ $(document).on('click', '.close-editHomepageItemDiv',function () {
 	//$('html').removeAttr('style');
 	//$('html').removeAttr('style');
 	Custombox.modal.closeAll()
 	Custombox.modal.closeAll()
 })
 })
-// Control init of custom plex JSON editor
-$(document).on('click', '#homepage-Plex-form li a[aria-controls="Misc Options"]', 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("homepageCustomStreamNamesAce");
-    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) {
-        $('#homepageCustomStreamNamesText').val(jsonEditor.getValue());
-        $('#customize-appearance-form-save').removeClass('hidden');
-    });
-});
 // Trakt image fix
 // Trakt image fix
 $(document).on('click', '.get-tmdb-image', function() {
 $(document).on('click', '.get-tmdb-image', function() {
 	let target = $(this).attr('data-target');
 	let target = $(this).attr('data-target');

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
js/custom.min.js


+ 100 - 29
js/functions.js

@@ -23,7 +23,7 @@ var tabInformation = {};
 var tabActionsList = [];
 var tabActionsList = [];
 tabActionsList['refresh'] = [];
 tabActionsList['refresh'] = [];
 tabActionsList['close'] = [];
 tabActionsList['close'] = [];
-
+$.xhrPool = [];
 // Add new jquery serializeObject function
 // Add new jquery serializeObject function
 $.fn.serializeObject = function()
 $.fn.serializeObject = function()
 {
 {
@@ -767,6 +767,7 @@ function closeTab(tab){
                    if($('#menu-'+tab).attr('data-url') == 'api/v2/page/homepage'){
                    if($('#menu-'+tab).attr('data-url') == 'api/v2/page/homepage'){
 	                   organizrConsole('Organizr Function','Clearing All Homepage AJAX calls');
 	                   organizrConsole('Organizr Function','Clearing All Homepage AJAX calls');
                        clearAJAX('homepage');
                        clearAJAX('homepage');
+	                   $.xhrPool.abortAll();
                    }
                    }
 	               organizrConsole('Tab Function','Closing tab: '+tab);
 	               organizrConsole('Tab Function','Closing tab: '+tab);
                    $('#internal-'+cleanClass(tab)).html('');
                    $('#internal-'+cleanClass(tab)).html('');
@@ -807,6 +808,11 @@ function reloadTab(tab, type){
 		case 0:
 		case 0:
 		case '0':
 		case '0':
 		case 'internal':
 		case 'internal':
+			if($('#menu-'+cleanClass(tab)).attr('data-url') == 'api/v2/page/homepage'){
+				organizrConsole('Organizr Function','Clearing All Homepage AJAX calls');
+				clearAJAX('homepage');
+				$.xhrPool.abortAll();
+			}
 		    var dataURL = $('.frame-'+cleanClass(tab)).attr('data-url');
 		    var dataURL = $('.frame-'+cleanClass(tab)).attr('data-url');
 		    var dataName = $('.frame-'+cleanClass(tab)).attr('data-name');
 		    var dataName = $('.frame-'+cleanClass(tab)).attr('data-name');
             $('#frame-'+cleanClass(tab)).html('');
             $('#frame-'+cleanClass(tab)).html('');
@@ -847,6 +853,11 @@ function reloadCurrentTab(){
 		case '0':
 		case '0':
 		case 'internal':
 		case 'internal':
 			var activeInternal = $('.internal-listing').find('.show');
 			var activeInternal = $('.internal-listing').find('.show');
+			if($(activeInternal).attr('data-url') == 'api/v2/page/homepage'){
+				organizrConsole('Organizr Function','Clearing All Homepage AJAX calls');
+				clearAJAX('homepage');
+				$.xhrPool.abortAll();
+			}
 			$(activeInternal).html('');
 			$(activeInternal).html('');
 			loadInternal(activeInternal.attr('data-url'),activeInternal.attr('data-name'));
 			loadInternal(activeInternal.attr('data-url'),activeInternal.attr('data-name'));
 			break;
 			break;
@@ -914,6 +925,7 @@ function closeCurrentTab(event){
             if($('#menu-'+cleanClass(tab)).attr('data-url') == 'api/v2/page/homepage'){
             if($('#menu-'+cleanClass(tab)).attr('data-url') == 'api/v2/page/homepage'){
 	            organizrConsole('Organizr Function','Clearing All Homepage AJAX calls');
 	            organizrConsole('Organizr Function','Clearing All Homepage AJAX calls');
                 clearAJAX('homepage');
                 clearAJAX('homepage');
+	            $.xhrPool.abortAll();
             }
             }
 			organizrConsole('Organizr Function','Closing tab: '+tab);
 			organizrConsole('Organizr Function','Closing tab: '+tab);
 			$('#internal'+extra+'-'+cleanClass(tab)).html('');
 			$('#internal'+extra+'-'+cleanClass(tab)).html('');
@@ -1155,7 +1167,7 @@ function buildPluginsItem(array){
                     <button id="`+v.idPrefix+`-settings-page-save" onclick="submitSettingsForm('`+v.idPrefix+`-settings-page')" class="btn btn-sm btn-info btn-rounded waves-effect waves-light pull-right hidden animated loop-animation rubberBand m-r-20" type="button"><span class="btn-label"><i class="fa fa-save"></i></span><span lang="en">Save</span></button>
                     <button id="`+v.idPrefix+`-settings-page-save" onclick="submitSettingsForm('`+v.idPrefix+`-settings-page')" class="btn btn-sm btn-info btn-rounded waves-effect waves-light pull-right hidden animated loop-animation rubberBand m-r-20" type="button"><span class="btn-label"><i class="fa fa-save"></i></span><span lang="en">Save</span></button>
                 </div>
                 </div>
                 <div class="panel-wrapper collapse in" aria-expanded="true">
                 <div class="panel-wrapper collapse in" aria-expanded="true">
-                    <div class="panel-body bg-org">
+                    <div class="bg-org">
                         <fieldset id="`+v.idPrefix+`-settings-items" style="border:0;" class=""><h2>Loading...</h2></fieldset>
                         <fieldset id="`+v.idPrefix+`-settings-items" style="border:0;" class=""><h2>Loading...</h2></fieldset>
                     </div>
                     </div>
                     <div class="clearfix"></div>
                     <div class="clearfix"></div>
@@ -1666,20 +1678,37 @@ function themeStatus(name=null,version=null){
         return 'Not Installed';
         return 'Not Installed';
     }
     }
 }
 }
+function copyHomepageJSON(item){
+	organizrAPI2('GET','api/v2/settings/homepage/'+item+'/debug').success(function(data) {
+		try {
+			let response = data.response;
+			let debug = response.data;
+			clipboard(true, JSON.stringify(debug,null,'\t'));
+			message("",window.lang.translate('Copied JSON to clipboard'),activeInfo.settings.notifications.position,"#FFF","success","5000");
+		}catch(e) {
+			organizrCatchError(e,data);
+		}
+	}).fail(function(xhr) {
+		OrganizrApiError(xhr, 'Copy JSON Failed');
+	});
+}
 function homepageItemFormHTML(v){
 function homepageItemFormHTML(v){
 	let docs = (typeof v.docs == 'undefined') ? '' : `<small class="pl-5"><a class="btn btn-sm btn-primary waves-effect waves-light" href="${v.docs}" target="_blank"> <i class="icon-docs m-r-5"></i> <span lang="en">Support Docs</span></a></small>`;
 	let docs = (typeof v.docs == 'undefined') ? '' : `<small class="pl-5"><a class="btn btn-sm btn-primary waves-effect waves-light" href="${v.docs}" target="_blank"> <i class="icon-docs m-r-5"></i> <span lang="en">Support Docs</span></a></small>`;
+	let debug = (typeof v.debug == 'undefined') ? false : true;
+	debug = (debug === true) ? (v.debug) : false;
+	debug = (debug === true) ? `<small class="pl-5"><a href="javascript:copyHomepageJSON('${v.name}')" class="btn btn-sm btn-info waves-effect waves-light copyHomepageJSON"> <i class="ti-clipboard m-r-5"></i> <span lang="en">Copy JSON</span></a></small>` : '';
 	return `
 	return `
 	<a id="editHomepageItemCall" href="#editHomepageItemDiv" class="hidden">homepage item</a>
 	<a id="editHomepageItemCall" href="#editHomepageItemDiv" class="hidden">homepage item</a>
 	<form id="homepage-`+v.name+`-form" class="white-popup mfp-with-anim homepageForm addFormTick">
 	<form id="homepage-`+v.name+`-form" class="white-popup mfp-with-anim homepageForm addFormTick">
 		<fieldset style="border:0;" class="col-md-10 col-md-offset-1">
 		<fieldset style="border:0;" class="col-md-10 col-md-offset-1">
             <div class="panel bg-org panel-info">
             <div class="panel bg-org panel-info">
                 <div class="panel-heading">
                 <div class="panel-heading">
-                    <span class="" lang="en">`+v.name+`</span>${docs}
+                    <span class="" lang="en">`+v.name+`</span>${docs}${debug}
                     <button type="button" class="btn bg-org btn-circle close-popup pull-right close-editHomepageItemDiv"><i class="fa fa-times"></i> </button>
                     <button type="button" class="btn bg-org btn-circle close-popup pull-right close-editHomepageItemDiv"><i class="fa fa-times"></i> </button>
-                    <button id="homepage-`+v.name+`-form-save" onclick="submitSettingsForm('homepage-`+v.name+`-form')" class="btn btn-sm btn-info btn-rounded waves-effect waves-light pull-right hidden animated loop-animation rubberBand m-r-20" type="button"><span class="btn-label"><i class="fa fa-save"></i></span><span lang="en">Save</span></button>
+                    <button id="homepage-`+v.name+`-form-save" onclick="submitSettingsForm('homepage-`+v.name+`-form', true)" class="btn btn-sm btn-info btn-rounded waves-effect waves-light pull-right hidden animated loop-animation rubberBand m-r-20" type="button"><span class="btn-label"><i class="fa fa-save"></i></span><span lang="en">Save</span></button>
                 </div>
                 </div>
                 <div class="panel-wrapper collapse in" aria-expanded="true">
                 <div class="panel-wrapper collapse in" aria-expanded="true">
-                    <div class="panel-body bg-org">
+                    <div class="bg-org">
                         `+buildFormGroup(v.settings)+`
                         `+buildFormGroup(v.settings)+`
                     </div>
                     </div>
                 </div>
                 </div>
@@ -1811,8 +1840,8 @@ function buildHomepage(){
 }
 }
 function buildFormGroup(array){
 function buildFormGroup(array){
     var mainCount = 0;
     var mainCount = 0;
-	var group = '<div class="tab-content">';
-	var uList = '<ul class="nav customtab nav-tabs nav-low-margin" role="tablist">';
+	var group = '<div class="tab-content w-100">';
+	var uList = '<div class="vtabs customvtab"><ul class="nav tabs-vertical" role="tablist">';
 	$.each(array, function(i,v) {
 	$.each(array, function(i,v) {
         mainCount++;
         mainCount++;
 		var count = 0;
 		var count = 0;
@@ -1891,7 +1920,7 @@ function buildFormGroup(array){
 			group += '</div>';
 			group += '</div>';
 		}
 		}
 	});
 	});
-	return uList+'</ul>'+group;
+	return uList+'</ul>'+group+'</div>';
 }
 }
 function createImageSwal(attr){
 function createImageSwal(attr){
 	let title = attr.attr('data-title');
 	let title = attr.attr('data-title');
@@ -2956,7 +2985,7 @@ function buildSplashScreen(json){
         closeSideMenu();
         closeSideMenu();
 	    organizrConsole('Organizr Function','Adding Splash Screen');
 	    organizrConsole('Organizr Function','Adding Splash Screen');
         var splash = `
         var splash = `
-        <section id="splashScreen" class="lock-screen splash-screen fade ${hiddenSplash}">
+        <section id="splashScreen" class="lock-screen splash-screen default-scroller fade ${hiddenSplash}">
             <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 bottom-close-splash" 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')">
@@ -3260,7 +3289,7 @@ function getSubmitSettingsFormValueObject(form, index, value){
     values = {name: index, value: values, type: 'array'};
     values = {name: index, value: values, type: 'array'};
     return values;
     return values;
 }
 }
-function submitSettingsForm(form){
+function submitSettingsForm(form, homepageItem = false){
     var list = $( "#"+form ).serializeToJSON();
     var list = $( "#"+form ).serializeToJSON();
     var size = 0;
     var size = 0;
     var submit = {};
     var submit = {};
@@ -3290,8 +3319,34 @@ function submitSettingsForm(form){
 			}catch(e) {
 			}catch(e) {
 				organizrCatchError(e,data);
 				organizrCatchError(e,data);
 			}
 			}
-			message('Updated Items',response.message,activeInfo.settings.notifications.position,"#FFF","success","5000");
 			if(callbacks){ callbacks.fire(); }
 			if(callbacks){ callbacks.fire(); }
+			if(homepageItem) {
+				let html = `
+		        <div class="panel panel-default">
+                    <div class="panel-heading">${response.message}</div>
+                    <div class="panel-wrapper collapse in">
+                        <div class="panel-body">
+                            <div class="overlay-box">
+                                <div class="user-content">
+                                    <h4 lang="en">Close Homepage Settings?</h4>
+                                    <div class="button-box">
+				                        <button class="btn btn-info waves-effect waves-light" type="button" onclick="swal.close();Custombox.modal.close()"><span class="btn-label"><i class="ti-check"></i></span>Yes</button>
+				                        <button class="btn btn-danger waves-effect waves-light" type="button" onclick="swal.close()"><span class="btn-label"><i class="ti-close"></i></span>No</button>                        
+				                    </div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+		    `;
+				swal({
+					content: createElementFromHTML(html),
+					buttons: false,
+					className: 'bg-org'
+				})
+			}else{
+				message('Updated Items',response.message,activeInfo.settings.notifications.position,"#FFF","success","5000");
+			}
 		}).fail(function(xhr) {
 		}).fail(function(xhr) {
 			OrganizrApiError(xhr, 'Update Error');
 			OrganizrApiError(xhr, 'Update Error');
 		});
 		});
@@ -4015,7 +4070,21 @@ function settingsAPI2(post, callbacks=null, asyncValue=true){
 		console.error(post.error);
 		console.error(post.error);
 	});
 	});
 }
 }
+$.xhrPool.abortAll = function(url) {
+	$(this).each(function(i, jqXHR) { //  cycle through list of recorded connection
+		if (!url || url === jqXHR.requestURL) {
+			organizrConsole('Organizr API Abort',jqXHR.requestURL,'info');
+			jqXHR.abort(); //  aborts connection
+			$.xhrPool.splice(i, 1); //  removes from list by index
+		}
+	});
+};
+$.ajaxPrefilter(function(options, originalOptions, jqXHR) {
+	//organizrConsole('Organizr API Function',options.url,'info');
+	jqXHR.requestURL = options.url;
+});
 function organizrAPI2(type,path,data=null,asyncValue=true){
 function organizrAPI2(type,path,data=null,asyncValue=true){
+	$.xhrPool.abortAll(path);
 	var timeout = 10000;
 	var timeout = 10000;
 	switch(path){
 	switch(path){
 		case 'api/v2/windows/update':
 		case 'api/v2/windows/update':
@@ -4035,6 +4104,11 @@ function organizrAPI2(type,path,data=null,asyncValue=true){
 				beforeSend: function(request) {
 				beforeSend: function(request) {
 					request.setRequestHeader("Token", activeInfo.token);
 					request.setRequestHeader("Token", activeInfo.token);
 					request.setRequestHeader("formKey", local('g','formKey'));
 					request.setRequestHeader("formKey", local('g','formKey'));
+					$.xhrPool.push(request);
+				},
+				complete: function(jqXHR) {
+					var i = $.xhrPool.indexOf(jqXHR); //  get index for current connection completed
+					if (i > -1) $.xhrPool.splice(i, 1); //  removes from list by index
 				},
 				},
 				timeout: timeout,
 				timeout: timeout,
 			});
 			});
@@ -4047,6 +4121,11 @@ function organizrAPI2(type,path,data=null,asyncValue=true){
 				beforeSend: function(request) {
 				beforeSend: function(request) {
 					request.setRequestHeader("Token", activeInfo.token);
 					request.setRequestHeader("Token", activeInfo.token);
 					request.setRequestHeader("formKey", local('g','formKey'));
 					request.setRequestHeader("formKey", local('g','formKey'));
+					$.xhrPool.push(request);
+				},
+				complete: function(jqXHR) {
+					var i = $.xhrPool.indexOf(jqXHR); //  get index for current connection completed
+					if (i > -1) $.xhrPool.splice(i, 1); //  removes from list by index
 				},
 				},
 				timeout: timeout,
 				timeout: timeout,
 			});
 			});
@@ -4061,6 +4140,11 @@ function organizrAPI2(type,path,data=null,asyncValue=true){
 				beforeSend: function(request) {
 				beforeSend: function(request) {
 					request.setRequestHeader("Token", activeInfo.token);
 					request.setRequestHeader("Token", activeInfo.token);
 					request.setRequestHeader("formKey", local('g','formKey'));
 					request.setRequestHeader("formKey", local('g','formKey'));
+					$.xhrPool.push(request);
+				},
+				complete: function(jqXHR) {
+					var i = $.xhrPool.indexOf(jqXHR); //  get index for current connection completed
+					if (i > -1) $.xhrPool.splice(i, 1); //  removes from list by index
 				},
 				},
 				data:data
 				data:data
 			});
 			});
@@ -4074,6 +4158,11 @@ function organizrAPI2(type,path,data=null,asyncValue=true){
 				beforeSend: function(request) {
 				beforeSend: function(request) {
 					request.setRequestHeader("Token", activeInfo.token);
 					request.setRequestHeader("Token", activeInfo.token);
 					request.setRequestHeader("formKey", local('g','formKey'));
 					request.setRequestHeader("formKey", local('g','formKey'));
+					$.xhrPool.push(request);
+				},
+				complete: function(jqXHR) {
+					var i = $.xhrPool.indexOf(jqXHR); //  get index for current connection completed
+					if (i > -1) $.xhrPool.splice(i, 1); //  removes from list by index
 				},
 				},
 				data:JSON.stringify(data),
 				data:JSON.stringify(data),
 				contentType: "application/json"
 				contentType: "application/json"
@@ -8576,24 +8665,6 @@ function tryUpdateNetdata(array){
     });
     });
     return existing;
     return existing;
 }
 }
-function getTautulliFriendlyNames()
-{
-    organizrAPI2('GET','api/v2/homepage/tautulli/names').success(function(data) {
-        try {
-            let response = data.response;
-            if(response.data !== null){
-                var string = JSON.stringify(response.data, null, 4);
-                jsonEditor = ace.edit("homepageCustomStreamNamesAce");
-                jsonEditor.setValue(string);
-                $('#homepage-Plex-form-save').removeClass('hidden');
-            }
-        }catch(e) {
-	        organizrCatchError(e,data);
-        }
-    }).fail(function(xhr) {
-	    OrganizrApiError(xhr);
-    });
-}
 function homepageJackett(){
 function homepageJackett(){
 	if(activeInfo.settings.homepage.options.alternateHomepageHeaders){
 	if(activeInfo.settings.homepage.options.alternateHomepageHeaders){
 		var header = `
 		var header = `

+ 7 - 0
js/version.json

@@ -397,5 +397,12 @@
     "new": "setResponse function|get config item to api|icons for TheLounge and Docsify services|small ui change to version section|change the way theme is set to fix loading ms issue",
     "new": "setResponse function|get config item to api|icons for TheLounge and Docsify services|small ui change to version section|change the way theme is set to fix loading ms issue",
     "fixed": "Redirect uri that was encoded (#1690)|IP info on settings page|Ombi Missing Image for TVShow Posters that do not come with Full URL|only check for blacklisted ip if not blank|error handling of messages on startup",
     "fixed": "Redirect uri that was encoded (#1690)|IP info on settings page|Ombi Missing Image for TVShow Posters that do not come with Full URL|only check for blacklisted ip if not blank|error handling of messages on startup",
     "notes": "upgrade phpmailer/phpmailer to 6.5.0"
     "notes": "upgrade phpmailer/phpmailer to 6.5.0"
+  },
+  "2.1.472": {
+    "date": "2021-08-20 19:10",
+    "title": "Weekly Update",
+    "new": "Pass variables through with URLs (FR#203150)|qbittorent socks (FR#196698)|framework for copy json button to homepage items|debug json to all homepage items|updated ui - change settings ui from horizontal to vertical|ui for Save homepage settings modal|make tautulli custom names dynamic|only update tautulli custom names config item if it doesn't match|prevent organizrHash from being displayed via API",
+    "fixed": "splashscreen scroll (#1692)|jellyin open tab (#1633)|test iframe|sonarr input box|plex invite with deleted library|catch error from socks api|clear ajax on reload|loop load config on load",
+    "notes": "update Adldap2/Adldap2"
   }
   }
 }
 }

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels