瀏覽代碼

update all functions files

CauseFX 5 年之前
父節點
當前提交
f5dea135c4

+ 98 - 68
api/functions/2fa-functions.php

@@ -1,78 +1,108 @@
 <?php
-function create2FA($type)
+
+trait TwoFAFunctions
 {
-	$result['type'] = $type;
-	switch ($type) {
-		case 'google':
-			try {
+	public function create2FA($type)
+	{
+		$result['type'] = $type;
+		switch ($type) {
+			case 'google':
+				try {
+					$google2fa = new PragmaRX\Google2FA\Google2FA();
+					$google2fa->setAllowInsecureCallToGoogleApis(true);
+					$result['secret'] = $google2fa->generateSecretKey();
+					$result['url'] = $google2fa->getQRCodeGoogleUrl(
+						$this->config['title'],
+						$this->user['username'],
+						$result['secret']
+					);
+				} catch (PragmaRX\Google2FA\Exceptions\InsecureCallException $e) {
+					$this->setAPIResponse('error', $e->getMessage(), 500);
+					return null;
+				}
+				break;
+			default:
+				$this->setAPIResponse('error', $type . ' is not an available to be setup', 404);
+				return null;
+		}
+		$this->setAPIResponse('success', '2FA code created - awaiting verification', 200);
+		return $result;
+	}
+	
+	public function verify2FA($secret, $code, $type)
+	{
+		if (!$secret || $secret == '') {
+			$this->setAPIResponse('error', 'Secret was not supplied or left blank', 422);
+			return false;
+		}
+		if (!$code || $code == '') {
+			$this->setAPIResponse('error', 'Code was not supplied or left blank', 422);
+			return false;
+		}
+		if (!$type || $type == '') {
+			$this->setAPIResponse('error', 'Type was not supplied or left blank', 422);
+			return false;
+		}
+		switch ($type) {
+			case 'google':
 				$google2fa = new PragmaRX\Google2FA\Google2FA();
-				$google2fa->setAllowInsecureCallToGoogleApis(true);
-				$result['secret'] = $google2fa->generateSecretKey();
-				$result['url'] = $google2fa->getQRCodeGoogleUrl(
-					$GLOBALS['title'],
-					$GLOBALS['organizrUser']['username'],
-					$result['secret']
-				);
-			} catch (PragmaRX\Google2FA\Exceptions\InsecureCallException $e) {
+				$google2fa->setWindow(5);
+				$valid = $google2fa->verifyKey($secret, $code);
+				break;
+			default:
+				$this->setAPIResponse('error', $type . ' is not an available to be setup', 404);
 				return false;
-			}
-			break;
-		default:
+		}
+		if ($valid) {
+			$this->setAPIResponse('success', 'Verification code verified', 200);
+			return true;
+		} else {
+			$this->setAPIResponse('success', 'Verification code invalid', 401);
 			return false;
+		}
 	}
-	return $result;
-}
-
-function save2FA($secret, $type)
-{
-	try {
-		$connect = new Dibi\Connection([
-			'driver' => 'sqlite3',
-			'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-		]);
-		$connect->query('
-            UPDATE users SET', [
-			'auth_service' => $type . '::' . $secret
-		], '
-            WHERE id=?', $GLOBALS['organizrUser']['userID']);
-		writeLog('success', 'User Management Function - User added 2FA', $GLOBALS['organizrUser']['username']);
-		return true;
-	} catch (Dibi\Exception $e) {
-		writeLog('error', 'User Management Function - Error Adding User 2FA', $GLOBALS['organizrUser']['username']);
-		return false;
-	}
-}
-
-function verify2FA($secret, $code, $type)
-{
-	switch ($type) {
-		case 'google':
-			$google2fa = new PragmaRX\Google2FA\Google2FA();
-			$google2fa->setWindow(5);
-			$valid = $google2fa->verifyKey($secret, $code);
-			break;
-		default:
+	
+	public function save2FA($secret, $type)
+	{
+		if (!$secret || $secret == '') {
+			$this->setAPIResponse('error', 'Secret was not supplied or left blank', 422);
 			return false;
+		}
+		if (!$type || $type == '') {
+			$this->setAPIResponse('error', 'Type was not supplied or left blank', 422);
+			return false;
+		}
+		$response = [
+			array(
+				'function' => 'query',
+				'query' => array(
+					'UPDATE users SET',
+					['auth_service' => $type . '::' . $secret],
+					'WHERE id = ?',
+					$this->user['userID']
+				)
+			),
+		];
+		$this->writeLog('success', 'User Management Function - User added 2FA', $this->user['username']);
+		$this->setAPIResponse('success', '2FA Added', 200);
+		return $this->processQueries($response);
 	}
-	return ($valid) ? true : false;
-}
-
-function remove2FA()
-{
-	try {
-		$connect = new Dibi\Connection([
-			'driver' => 'sqlite3',
-			'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-		]);
-		$connect->query('
-            UPDATE users SET', [
-			'auth_service' => 'internal'
-		], '
-            WHERE id=?', $GLOBALS['organizrUser']['userID']);
-		writeLog('success', 'User Management Function - User removed 2FA', $GLOBALS['organizrUser']['username']);
-		return true;
-	} catch (Dibi\Exception $e) {
-		writeLog('error', 'User Management Function - Error Removing User 2FA', $GLOBALS['organizrUser']['username']);
-		return false;
+	
+	public function remove2FA()
+	{
+		$response = [
+			array(
+				'function' => 'query',
+				'query' => array(
+					'UPDATE users SET',
+					['auth_service' => 'internal'],
+					'WHERE id = ?',
+					$this->user['userID']
+				)
+			),
+		];
+		$this->writeLog('success', 'User Management Function - User removed 2FA', $this->user['username']);
+		$this->setAPIResponse('success', '2FA deleted', 204);
+		return $this->processQueries($response);
 	}
 }

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

@@ -1,1345 +1,6 @@
-<?php /** @noinspection SqlResolve */
-/** @noinspection SqlResolve */
-/** @noinspection SqlResolve */
-/** @noinspection SqlResolve */
-/** @noinspection SyntaxError */
-function apiLogin()
-{
-	$array = array(
-		'data' => array(
-			array(
-				'name' => 'username',
-				'value' => (isset($_POST['username'])) ? $_POST['username'] : false
-			),
-			array(
-				'name' => 'password',
-				'value' => (isset($_POST['password'])) ? $_POST['password'] : false
-			),
-			array(
-				'name' => 'remember',
-				'value' => (isset($_POST['remember'])) ? true : false
-			),
-			array(
-				'name' => 'oAuth',
-				'value' => (isset($_POST['oAuth'])) ? $_POST['oAuth'] : false
-			),
-			array(
-				'name' => 'oAuthType',
-				'value' => (isset($_POST['oAuthType'])) ? $_POST['oAuthType'] : false
-			),
-			array(
-				'name' => 'tfaCode',
-				'value' => (isset($_POST['tfaCode'])) ? $_POST['tfaCode'] : false
-			),
-			array(
-				'name' => 'loginAttempts',
-				'value' => (isset($_POST['loginAttempts'])) ? $_POST['loginAttempts'] : false
-			),
-			array(
-				'name' => 'output',
-				'value' => true
-			),
-		)
-	);
-	return login($array);
-}
-
-function login($array)
-{
-	// Grab username and Password from login form
-	$username = $array['data']['username'] ?? null;
-	$password = $array['data']['password'] ?? null;
-	$oAuth = $array['data']['oAuth'] ?? null;
-	$oAuthType = $array['data']['oAuthType'] ?? null;
-	$remember = $array['data']['remember'] ?? null;
-	$tfaCode = $array['data']['tfaCode'] ?? null;
-	$loginAttempts = $array['data']['loginAttempts'] ?? null;
-	$output = $array['data']['output'] ?? null;
-	$username = (strpos($GLOBALS['authBackend'], 'emby') !== false) ? $username : strtolower($username);
-	$days = (isset($remember)) ? $GLOBALS['rememberMeDays'] : 1;
-	if ($loginAttempts > $GLOBALS['loginAttempts'] || isset($_COOKIE['lockout'])) {
-		coookieSeconds('set', 'lockout', $GLOBALS['loginLockout'], $GLOBALS['loginLockout']);
-		return 'lockout';
-	}
-	try {
-		$database = new Dibi\Connection([
-			'driver' => 'sqlite3',
-			'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-		]);
-		$authSuccess = false;
-		$authProxy = false;
-		if ($GLOBALS['authProxyEnabled'] && $GLOBALS['authProxyHeaderName'] !== '' && $GLOBALS['authProxyWhitelist'] !== '') {
-			if (isset(getallheaders()[$GLOBALS['authProxyHeaderName']])) {
-				$usernameHeader = isset(getallheaders()[$GLOBALS['authProxyHeaderName']]) ? getallheaders()[$GLOBALS['authProxyHeaderName']] : $username;
-				writeLog('success', 'Auth Proxy Function - Starting Verification for IP: ' . userIP() . ' for request on: ' . $_SERVER['REMOTE_ADDR'] . ' against IP/Subnet: ' . $GLOBALS['authProxyWhitelist'], $usernameHeader);
-				$whitelistRange = analyzeIP($GLOBALS['authProxyWhitelist']);
-				$from = $whitelistRange['from'];
-				$to = $whitelistRange['to'];
-				$authProxy = authProxyRangeCheck($from, $to);
-				$username = ($authProxy) ? $usernameHeader : $username;
-				if ($authProxy) {
-					writeLog('success', 'Auth Proxy Function - IP: ' . userIP() . ' has been verified', $usernameHeader);
-				} else {
-					writeLog('error', 'Auth Proxy Function - IP: ' . userIP() . ' has failed verification', $usernameHeader);
-				}
-			}
-		}
-		$function = 'plugin_auth_' . $GLOBALS['authBackend'];
-		if (!$oAuth) {
-			$result = $database->fetch('SELECT * FROM users WHERE username = ? COLLATE NOCASE OR email = ? COLLATE NOCASE', $username, $username);
-			$result['password'] = $result['password'] ?? '';
-			switch ($GLOBALS['authType']) {
-				case 'external':
-					if (function_exists($function)) {
-						$authSuccess = $function($username, $password);
-					}
-					break;
-				/** @noinspection PhpMissingBreakStatementInspection */
-				case 'both':
-					if (function_exists($function)) {
-						$authSuccess = $function($username, $password);
-					}
-				// no break
-				default: // Internal
-					if (!$authSuccess) {
-						// perform the internal authentication step
-						if (password_verify($password, $result['password'])) {
-							$authSuccess = true;
-						}
-					}
-			}
-			$authSuccess = ($authProxy) ? true : $authSuccess;
-		} else {
-			// Has oAuth Token!
-			switch ($oAuthType) {
-				case 'plex':
-					if ($GLOBALS['plexoAuth']) {
-						$tokenInfo = checkPlexToken($oAuth);
-						if ($tokenInfo) {
-							$authSuccess = array(
-								'username' => $tokenInfo['user']['username'],
-								'email' => $tokenInfo['user']['email'],
-								'image' => $tokenInfo['user']['thumb'],
-								'token' => $tokenInfo['user']['authToken']
-							);
-							coookie('set', 'oAuth', 'true', $GLOBALS['rememberMeDays']);
-							$authSuccess = ((!empty($GLOBALS['plexAdmin']) && strtolower($GLOBALS['plexAdmin']) == strtolower($tokenInfo['user']['username'])) || (!empty($GLOBALS['plexAdmin']) && strtolower($GLOBALS['plexAdmin']) == strtolower($tokenInfo['user']['email'])) || checkPlexUser($tokenInfo['user']['username'])) ? $authSuccess : false;
-						}
-					}
-					break;
-				default:
-					return ($output) ? 'No oAuthType defined' : 'error';
-					break;
-			}
-			$result = ($authSuccess) ? $database->fetch('SELECT * FROM users WHERE username = ? COLLATE NOCASE OR email = ? COLLATE NOCASE', $authSuccess['username'], $authSuccess['email']) : '';
-		}
-		if ($authSuccess) {
-			// Make sure user exists in database
-			$userExists = false;
-			$passwordMatches = ($oAuth || $authProxy) ? true : false;
-			$token = (is_array($authSuccess) && isset($authSuccess['token']) ? $authSuccess['token'] : '');
-			if ($result['username']) {
-				$userExists = true;
-				$username = $result['username'];
-				if ($passwordMatches == false) {
-					$passwordMatches = (password_verify($password, $result['password'])) ? true : false;
-				}
-			}
-			if ($userExists) {
-				//does org password need to be updated
-				if (!$passwordMatches) {
-					$database->query('
-                    	UPDATE users SET', [
-						'password' => password_hash($password, PASSWORD_BCRYPT)
-					], '
-                    	WHERE id=?', $result['id']);
-					writeLog('success', 'Login Function - User Password updated from backend', $username);
-				}
-				if ($token !== '') {
-					if ($token !== $result['plex_token']) {
-						$database->query('
-	                        UPDATE users SET', [
-							'plex_token' => $token
-						], '
-	                        WHERE id=?', $result['id']);
-						writeLog('success', 'Login Function - User Plex Token updated from backend', $username);
-					}
-				}
-				// 2FA might go here
-				if ($result['auth_service'] !== 'internal' && strpos($result['auth_service'], '::') !== false) {
-					$tfaProceed = true;
-					// Add check for local or not
-					if ($GLOBALS['ignoreTFALocal'] !== false) {
-						$tfaProceed = (isLocal()) ? false : true;
-					}
-					if ($tfaProceed) {
-						$TFA = explode('::', $result['auth_service']);
-						// Is code with login info?
-						if ($tfaCode == '') {
-							return '2FA';
-						} else {
-							if (!verify2FA($TFA[1], $tfaCode, $TFA[0])) {
-								writeLoginLog($username, 'error');
-								writeLog('error', 'Login Function - Wrong 2FA', $username);
-								return '2FA-incorrect';
-							}
-						}
-					}
-				}
-				// End 2FA
-				// authentication passed - 1) mark active and update token
-				$createToken = createToken($result['username'], $result['email'], $result['image'], $result['group'], $result['group_id'], $GLOBALS['organizrHash'], $days);
-				if ($createToken) {
-					writeLoginLog($username, 'success');
-					writeLog('success', 'Login Function - A User has logged in', $username);
-					$ssoUser = ((empty($result['email'])) ? $result['username'] : (strpos($result['email'], 'placeholder') !== false)) ? $result['username'] : $result['email'];
-					ssoCheck($ssoUser, $password, $token); //need to work on this
-					return ($output) ? array('name' => $GLOBALS['cookieName'], 'token' => (string)$createToken) : true;
-				} else {
-					return 'Token Creation Error';
-				}
-			} else {
-				// Create User
-				//ssoCheck($username, $password, $token);
-				return authRegister((is_array($authSuccess) && isset($authSuccess['username']) ? $authSuccess['username'] : $username), $password, defaultUserGroup(), (is_array($authSuccess) && isset($authSuccess['email']) ? $authSuccess['email'] : ''), $token);
-			}
-		} else {
-			// authentication failed
-			writeLoginLog($username, 'error');
-			writeLog('error', 'Login Function - Wrong Password', $username);
-			if ($loginAttempts >= $GLOBALS['loginAttempts']) {
-				coookieSeconds('set', 'lockout', $GLOBALS['loginLockout'], $GLOBALS['loginLockout']);
-				return 'lockout';
-			} else {
-				return 'mismatch';
-			}
-		}
-	} catch (Dibi\Exception $e) {
-		return $e;
-	}
-}
-
-function createDB($path, $filename)
-{
-	try {
-		if (!file_exists($path)) {
-			mkdir($path, 0777, true);
-		}
-		$createDB = new Dibi\Connection([
-			'driver' => 'sqlite3',
-			'database' => $path . $filename,
-		]);
-		// Create Users
-		$createDB->query('CREATE TABLE `users` (
-    		`id`	INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
-    		`username`	TEXT UNIQUE,
-    		`password`	TEXT,
-    		`email`	TEXT,
-    		`plex_token`	TEXT,
-            `group`	TEXT,
-            `group_id`	INTEGER,
-            `locked`	INTEGER,
-    		`image`	TEXT,
-            `register_date` DATE,
-    		`auth_service`	TEXT DEFAULT \'internal\'
-    	);');
-		// Create Tokens
-		$createDB->query('CREATE TABLE `chatroom` (
-    		`id`	INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
-    		`username`	TEXT,
-    		`gravatar`	TEXT,
-    		`uid`	TEXT,
-            `date` DATE,
-            `ip` TEXT,
-            `message` TEXT
-    	);');
-		$createDB->query('CREATE TABLE `tokens` (
-    		`id`	INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
-    		`token`	TEXT UNIQUE,
-    		`user_id`	INTEGER,
-    		`browser`	TEXT,
-    		`ip`	TEXT,
-            `created` DATE,
-            `expires` DATE
-    	);');
-		$createDB->query('CREATE TABLE `groups` (
-    		`id`	INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
-    		`group`	TEXT UNIQUE,
-            `group_id`	INTEGER,
-    		`image`	TEXT,
-            `default` INTEGER
-    	);');
-		$createDB->query('CREATE TABLE `categories` (
-    		`id`	INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
-            `order`	INTEGER,
-    		`category`	TEXT UNIQUE,
-            `category_id`	INTEGER,
-    		`image`	TEXT,
-            `default` INTEGER
-    	);');
-		// Create Tabs
-		$createDB->query('CREATE TABLE `tabs` (
-    		`id`	INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
-    		`order`	INTEGER,
-    		`category_id`	INTEGER,
-    		`name`	TEXT,
-            `url`	TEXT,
-    		`url_local`	TEXT,
-    		`default`	INTEGER,
-    		`enabled`	INTEGER,
-    		`group_id`	INTEGER,
-    		`image`	TEXT,
-    		`type`	INTEGER,
-    		`splash`	INTEGER,
-    		`ping`		INTEGER,
-    		`ping_url`	TEXT,
-    		`timeout`	INTEGER,
-    		`timeout_ms`	INTEGER,
-    		`preload`	INTEGER
-    	);');
-		// Create Options
-		$createDB->query('CREATE TABLE `options` (
-    		`id`	INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
-    		`name`	TEXT UNIQUE,
-    		`value`	TEXT
-    	);');
-		// Create Invites
-		$createDB->query('CREATE TABLE `invites` (
-    		`id`	INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
-    		`code`	TEXT UNIQUE,
-    		`date`	TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
-    		`email`	TEXT,
-    		`username`	TEXT,
-    		`dateused`	TIMESTAMP,
-    		`usedby`	TEXT,
-    		`ip`	TEXT,
-    		`valid`	TEXT,
-            `type` TEXT
-    	);');
-		return true;
-	} catch (Dibi\Exception $e) {
-		return false;
-	}
-}
-
-// Upgrade Database
-function updateDB($oldVerNum = false)
-{
-	$tempLock = $GLOBALS['dbLocation'] . 'DBLOCK.txt';
-	if (!file_exists($tempLock)) {
-		touch($tempLock);
-		// Create Temp DB First
-		$migrationDB = 'tempMigration.db';
-		$pathDigest = pathinfo($GLOBALS['dbLocation'] . $GLOBALS['dbName']);
-		if (file_exists($GLOBALS['dbLocation'] . $migrationDB)) {
-			unlink($GLOBALS['dbLocation'] . $migrationDB);
-		}
-		$backupDB = $pathDigest['dirname'] . '/' . $pathDigest['filename'] . '[' . date('Y-m-d_H-i-s') . ']' . ($oldVerNum ? '[' . $oldVerNum . ']' : '') . '.bak.db';
-		copy($GLOBALS['dbLocation'] . $GLOBALS['dbName'], $backupDB);
-		$success = createDB($GLOBALS['dbLocation'], $migrationDB);
-		if ($success) {
-			try {
-				$connectOldDB = new Dibi\Connection([
-					'driver' => 'sqlite3',
-					'database' => $backupDB,
-				]);
-				$connectNewDB = new Dibi\Connection([
-					'driver' => 'sqlite3',
-					'database' => $GLOBALS['dbLocation'] . $migrationDB,
-				]);
-				$tables = $connectOldDB->fetchAll('SELECT name FROM sqlite_master WHERE type="table"');
-				foreach ($tables as $table) {
-					$data = $connectOldDB->fetchAll('SELECT * FROM ' . $table['name']);
-					writeLog('success', 'Update Function -  Grabbed Table data for Table: ' . $table['name'], 'Database');
-					foreach ($data as $row) {
-						$connectNewDB->query('INSERT into ' . $table['name'], $row);
-					}
-					writeLog('success', 'Update Function -  Wrote Table data for Table: ' . $table['name'], 'Database');
-				}
-				writeLog('success', 'Update Function -  All Table data converted - Starting Movement', 'Database');
-				$connectOldDB->disconnect();
-				$connectNewDB->disconnect();
-				// Remove Current Database
-				if (file_exists($GLOBALS['dbLocation'] . $migrationDB)) {
-					$oldFileSize = filesize($GLOBALS['dbLocation'] . $GLOBALS['dbName']);
-					$newFileSize = filesize($GLOBALS['dbLocation'] . $migrationDB);
-					if ($newFileSize > 0) {
-						writeLog('success', 'Update Function -  Table Size of new DB ok..', 'Database');
-						@unlink($GLOBALS['dbLocation'] . $GLOBALS['dbName']);
-						copy($GLOBALS['dbLocation'] . $migrationDB, $GLOBALS['dbLocation'] . $GLOBALS['dbName']);
-						@unlink($GLOBALS['dbLocation'] . $migrationDB);
-						writeLog('success', 'Update Function -  Migrated Old Info to new Database', 'Database');
-						@unlink($tempLock);
-						return true;
-					}
-				}
-				@unlink($tempLock);
-				return false;
-			} catch (Dibi\Exception $e) {
-				writeLog('error', 'Update Function -  Error [' . $e . ']', 'Database');
-				@unlink($tempLock);
-				return false;
-			}
-		}
-		@unlink($tempLock);
-		return false;
-	}
-	return false;
-}
-
-function createFirstAdmin($path, $filename, $username, $password, $email)
-{
-	try {
-		$createDB = new Dibi\Connection([
-			'driver' => 'sqlite3',
-			'database' => $path . $filename,
-		]);
-		$userInfo = [
-			'username' => $username,
-			'password' => password_hash($password, PASSWORD_BCRYPT),
-			'email' => $email,
-			'group' => 'Admin',
-			'group_id' => 0,
-			'image' => gravatar($email),
-			'register_date' => $GLOBALS['currentTime'],
-		];
-		$groupInfo0 = [
-			'group' => 'Admin',
-			'group_id' => 0,
-			'default' => false,
-			'image' => 'plugins/images/groups/admin.png',
-		];
-		$groupInfo1 = [
-			'group' => 'Co-Admin',
-			'group_id' => 1,
-			'default' => false,
-			'image' => 'plugins/images/groups/coadmin.png',
-		];
-		$groupInfo2 = [
-			'group' => 'Super User',
-			'group_id' => 2,
-			'default' => false,
-			'image' => 'plugins/images/groups/superuser.png',
-		];
-		$groupInfo3 = [
-			'group' => 'Power User',
-			'group_id' => 3,
-			'default' => false,
-			'image' => 'plugins/images/groups/poweruser.png',
-		];
-		$groupInfo4 = [
-			'group' => 'User',
-			'group_id' => 4,
-			'default' => true,
-			'image' => 'plugins/images/groups/user.png',
-		];
-		$groupInfoGuest = [
-			'group' => 'Guest',
-			'group_id' => 999,
-			'default' => false,
-			'image' => 'plugins/images/groups/guest.png',
-		];
-		$settingsInfo = [
-			'order' => 1,
-			'category_id' => 0,
-			'name' => 'Settings',
-			'url' => 'api/?v1/settings/page',
-			'default' => false,
-			'enabled' => true,
-			'group_id' => 1,
-			'image' => 'fontawesome::cog',
-			'type' => 0
-		];
-		$homepageInfo = [
-			'order' => 2,
-			'category_id' => 0,
-			'name' => 'Homepage',
-			'url' => 'api/?v1/homepage/page',
-			'default' => false,
-			'enabled' => false,
-			'group_id' => 4,
-			'image' => 'fontawesome::home',
-			'type' => 0
-		];
-		$unsortedInfo = [
-			'order' => 1,
-			'category' => 'Unsorted',
-			'category_id' => 0,
-			'image' => 'plugins/images/categories/unsorted.png',
-			'default' => true
-		];
-		$createDB->query('INSERT INTO [users]', $userInfo);
-		$createDB->query('INSERT INTO [groups]', $groupInfo0);
-		$createDB->query('INSERT INTO [groups]', $groupInfo1);
-		$createDB->query('INSERT INTO [groups]', $groupInfo2);
-		$createDB->query('INSERT INTO [groups]', $groupInfo3);
-		$createDB->query('INSERT INTO [groups]', $groupInfo4);
-		$createDB->query('INSERT INTO [groups]', $groupInfoGuest);
-		$createDB->query('INSERT INTO [tabs]', $settingsInfo);
-		$createDB->query('INSERT INTO [tabs]', $homepageInfo);
-		$createDB->query('INSERT INTO [categories]', $unsortedInfo);
-		return true;
-	} catch (Dibi\Exception $e) {
-		writeLog('error', 'Wizard Function -  Error [' . $e . ']', 'Wizard');
-		return false;
-	}
-}
-
-function defaultUserGroup()
-{
-	try {
-		$connect = new Dibi\Connection([
-			'driver' => 'sqlite3',
-			'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-		]);
-		$all = $connect->fetch('SELECT * FROM groups WHERE `default` = 1');
-		return $all;
-	} catch (Dibi\Exception $e) {
-		return false;
-	}
-}
-
-function defaultTabCategory()
-{
-	try {
-		$connect = new Dibi\Connection([
-			'driver' => 'sqlite3',
-			'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-		]);
-		$all = $connect->fetch('SELECT * FROM categories WHERE `default` = 1');
-		return $all;
-	} catch (Dibi\Exception $e) {
-		return false;
-	}
-}
-
-function getGuest()
-{
-	if (isset($GLOBALS['dbLocation'])) {
-		try {
-			$connect = new Dibi\Connection([
-				'driver' => 'sqlite3',
-				'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-			]);
-			$all = $connect->fetch('SELECT * FROM groups WHERE `group_id` = 999');
-			return $all;
-		} catch (Dibi\Exception $e) {
-			return false;
-		}
-	} else {
-		return array(
-			'group' => 'Guest',
-			'group_id' => 999,
-			'image' => 'plugins/images/groups/guest.png'
-		);
-	}
-}
-
-function adminEditGroup($array)
-{
-	switch ($array['data']['action']) {
-		case 'changeDefaultGroup':
-			try {
-				$connect = new Dibi\Connection([
-					'driver' => 'sqlite3',
-					'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-				]);
-				$connect->query('UPDATE groups SET `default` = 0');
-				$connect->query('
-                	UPDATE groups SET', [
-					'default' => 1
-				], '
-                	WHERE id=?', $array['data']['id']);
-				writeLog('success', 'Group Management Function -  Changed Default Group from [' . $array['data']['oldGroupName'] . '] to [' . $array['data']['newGroupName'] . ']', $GLOBALS['organizrUser']['username']);
-				return true;
-			} catch (Dibi\Exception $e) {
-				return false;
-			}
-			break;
-		case 'deleteUserGroup':
-			try {
-				$connect = new Dibi\Connection([
-					'driver' => 'sqlite3',
-					'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-				]);
-				$connect->query('DELETE FROM groups WHERE id = ?', $array['data']['id']);
-				writeLog('success', 'Group Management Function -  Deleted Group [' . $array['data']['groupName'] . ']', $GLOBALS['organizrUser']['username']);
-				return true;
-			} catch (Dibi\Exception $e) {
-				return false;
-			}
-			break;
-		case 'addUserGroup':
-			try {
-				$connect = new Dibi\Connection([
-					'driver' => 'sqlite3',
-					'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-				]);
-				$newGroup = [
-					'group' => $array['data']['newGroupName'],
-					'group_id' => $array['data']['newGroupID'],
-					'default' => false,
-					'image' => $array['data']['newGroupImage'],
-				];
-				$connect->query('INSERT INTO [groups]', $newGroup);
-				writeLog('success', 'Group Management Function -  Added Group [' . $array['data']['newGroupName'] . ']', $GLOBALS['organizrUser']['username']);
-				return true;
-			} catch (Dibi\Exception $e) {
-				return false;
-			}
-			break;
-		case 'editUserGroup':
-			try {
-				$connect = new Dibi\Connection([
-					'driver' => 'sqlite3',
-					'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-				]);
-				$connect->query('
-                	UPDATE groups SET', [
-					'group' => $array['data']['groupName'],
-					'image' => $array['data']['groupImage'],
-				], '
-                	WHERE id=?', $array['data']['id']);
-				writeLog('success', 'Group Management Function -  Edited Group Info for [' . $array['data']['oldGroupName'] . ']', $GLOBALS['organizrUser']['username']);
-				return true;
-			} catch (Dibi\Exception $e) {
-				return false;
-			}
-			break;
-		default:
-			return false;
-			break;
-	}
-}
-
-function adminEditUser($array)
-{
-	switch ($array['data']['action']) {
-		case 'changeGroup':
-			if ($array['data']['newGroupID'] == 0) {
-				return false;
-			}
-			try {
-				$connect = new Dibi\Connection([
-					'driver' => 'sqlite3',
-					'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-				]);
-				$connect->query('
-                	UPDATE users SET', [
-					'group' => $array['data']['newGroupName'],
-					'group_id' => $array['data']['newGroupID'],
-				], '
-                	WHERE id=?', $array['data']['id']);
-				writeLog('success', 'User Management Function - User: ' . $array['data']['username'] . '\'s group was changed from [' . $array['data']['oldGroup'] . '] to [' . $array['data']['newGroupName'] . ']', $GLOBALS['organizrUser']['username']);
-				return true;
-			} catch (Dibi\Exception $e) {
-				writeLog('error', 'User Management Function - Error - User: ' . $array['data']['username'] . '\'s group was changed from [' . $array['data']['oldGroup'] . '] to [' . $array['data']['newGroupName'] . ']', $GLOBALS['organizrUser']['username']);
-				return false;
-			}
-			break;
-		case 'editUser':
-			try {
-				$connect = new Dibi\Connection([
-					'driver' => 'sqlite3',
-					'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-				]);
-				if (!usernameTakenExcept($array['data']['username'], $array['data']['email'], $array['data']['id'])) {
-					$connect->query('
-                        UPDATE users SET', [
-						'username' => $array['data']['username'],
-						'email' => $array['data']['email'],
-						'image' => gravatar($array['data']['email']),
-					], '
-                        WHERE id=?', $array['data']['id']);
-					if (!empty($array['data']['password'])) {
-						$connect->query('
-                            UPDATE users SET', [
-							'password' => password_hash($array['data']['password'], PASSWORD_BCRYPT)
-						], '
-                            WHERE id=?', $array['data']['id']);
-					}
-					writeLog('success', 'User Management Function - User: ' . $array['data']['username'] . '\'s info was changed', $GLOBALS['organizrUser']['username']);
-					return true;
-				} else {
-					return false;
-				}
-			} catch (Dibi\Exception $e) {
-				writeLog('error', 'User Management Function - Error - User: ' . $array['data']['username'] . '\'s group was changed from [' . $array['data']['oldGroup'] . '] to [' . $array['data']['newGroupName'] . ']', $GLOBALS['organizrUser']['username']);
-				return false;
-			}
-			break;
-		case 'addNewUser':
-			$defaults = defaultUserGroup();
-			if (createUser($array['data']['username'], $array['data']['password'], $defaults, $array['data']['email'])) {
-				writeLog('success', 'Create User Function - Account created for [' . $array['data']['username'] . ']', $GLOBALS['organizrUser']['username']);
-				return true;
-			} else {
-				writeLog('error', 'Registration Function - An error occurred', $GLOBALS['organizrUser']['username']);
-				return 'username taken';
-			}
-			break;
-		case 'deleteUser':
-			try {
-				$connect = new Dibi\Connection([
-					'driver' => 'sqlite3',
-					'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-				]);
-				$connect->query('DELETE FROM users WHERE id = ?', $array['data']['id']);
-				writeLog('success', 'User Management Function -  Deleted User [' . $array['data']['username'] . ']', $GLOBALS['organizrUser']['username']);
-				return true;
-			} catch (Dibi\Exception $e) {
-				return false;
-			}
-			break;
-		default:
-			return false;
-			break;
-	}
-}
-
-function editTabs($array)
-{
-	switch ($array['data']['action']) {
-		case 'changeGroup':
-			try {
-				$connect = new Dibi\Connection([
-					'driver' => 'sqlite3',
-					'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-				]);
-				$connect->query('
-                	UPDATE tabs SET', [
-					'group_id' => $array['data']['newGroupID'],
-				], '
-                	WHERE id=?', $array['data']['id']);
-				writeLog('success', 'Tab Editor Function - Tab: ' . $array['data']['tab'] . '\'s group was changed to [' . $array['data']['newGroupName'] . ']', $GLOBALS['organizrUser']['username']);
-				return true;
-			} catch (Dibi\Exception $e) {
-				return false;
-			}
-			break;
-		case 'changeCategory':
-			try {
-				$connect = new Dibi\Connection([
-					'driver' => 'sqlite3',
-					'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-				]);
-				$connect->query('
-                        UPDATE tabs SET', [
-					'category_id' => $array['data']['newCategoryID'],
-				], '
-                        WHERE id=?', $array['data']['id']);
-				writeLog('success', 'Tab Editor Function - Tab: ' . $array['data']['tab'] . '\'s category was changed to [' . $array['data']['newCategoryName'] . ']', $GLOBALS['organizrUser']['username']);
-				return true;
-			} catch (Dibi\Exception $e) {
-				return false;
-			}
-			break;
-		case 'changeType':
-			try {
-				$connect = new Dibi\Connection([
-					'driver' => 'sqlite3',
-					'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-				]);
-				$connect->query('
-                        UPDATE tabs SET', [
-					'type' => $array['data']['newTypeID'],
-				], '
-                        WHERE id=?', $array['data']['id']);
-				writeLog('success', 'Tab Editor Function - Tab: ' . $array['data']['tab'] . '\'s type was changed to [' . $array['data']['newTypeName'] . ']', $GLOBALS['organizrUser']['username']);
-				return true;
-			} catch (Dibi\Exception $e) {
-				return false;
-			}
-			break;
-		case 'changeEnabled':
-			try {
-				$connect = new Dibi\Connection([
-					'driver' => 'sqlite3',
-					'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-				]);
-				$connect->query('
-                        UPDATE tabs SET', [
-					'enabled' => $array['data']['tabEnabled'],
-				], '
-                        WHERE id=?', $array['data']['id']);
-				writeLog('success', 'Tab Editor Function - Tab: ' . $array['data']['tab'] . '\'s enable status was changed to [' . $array['data']['tabEnabledWord'] . ']', $GLOBALS['organizrUser']['username']);
-				return true;
-			} catch (Dibi\Exception $e) {
-				return false;
-			}
-			break;
-		case 'changeSplash':
-			try {
-				$connect = new Dibi\Connection([
-					'driver' => 'sqlite3',
-					'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-				]);
-				$connect->query('
-                        UPDATE tabs SET', [
-					'splash' => $array['data']['tabSplash'],
-				], '
-                        WHERE id=?', $array['data']['id']);
-				writeLog('success', 'Tab Editor Function - Tab: ' . $array['data']['tab'] . '\'s splash status was changed to [' . $array['data']['tabSplashWord'] . ']', $GLOBALS['organizrUser']['username']);
-				return true;
-			} catch (Dibi\Exception $e) {
-				return false;
-			}
-			break;
-		case 'changePing':
-			try {
-				$connect = new Dibi\Connection([
-					'driver' => 'sqlite3',
-					'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-				]);
-				$connect->query('
-                        UPDATE tabs SET', [
-					'ping' => $array['data']['tabPing'],
-				], '
-                        WHERE id=?', $array['data']['id']);
-				writeLog('success', 'Tab Editor Function - Tab: ' . $array['data']['tab'] . '\'s ping status was changed to [' . $array['data']['tabPingWord'] . ']', $GLOBALS['organizrUser']['username']);
-				return true;
-			} catch (Dibi\Exception $e) {
-				return false;
-			}
-			break;
-		case 'changePreload':
-			try {
-				$connect = new Dibi\Connection([
-					'driver' => 'sqlite3',
-					'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-				]);
-				$connect->query('
-                        UPDATE tabs SET', [
-					'preload' => $array['data']['tabPreload'],
-				], '
-                        WHERE id=?', $array['data']['id']);
-				writeLog('success', 'Tab Editor Function - Tab: ' . $array['data']['tab'] . '\'s preload status was changed to [' . $array['data']['tabPreloadWord'] . ']', $GLOBALS['organizrUser']['username']);
-				return true;
-			} catch (Dibi\Exception $e) {
-				return false;
-			}
-			break;
-		case 'changeDefault':
-			try {
-				$connect = new Dibi\Connection([
-					'driver' => 'sqlite3',
-					'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-				]);
-				$connect->query('UPDATE tabs SET `default` = 0');
-				$connect->query('
-                    UPDATE tabs SET', [
-					'default' => 1
-				], '
-                    WHERE id=?', $array['data']['id']);
-				writeLog('success', 'Tab Editor Function -  Changed Default Tab to [' . $array['data']['tab'] . ']', $GLOBALS['organizrUser']['username']);
-				return true;
-			} catch (Dibi\Exception $e) {
-				return false;
-			}
-			break;
-		case 'deleteTab':
-			try {
-				$connect = new Dibi\Connection([
-					'driver' => 'sqlite3',
-					'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-				]);
-				$connect->query('DELETE FROM tabs WHERE id = ?', $array['data']['id']);
-				writeLog('success', 'Tab Editor Function -  Deleted Tab [' . $array['data']['tab'] . ']', $GLOBALS['organizrUser']['username']);
-				return true;
-			} catch (Dibi\Exception $e) {
-				return false;
-			}
-			break;
-		case 'editTab':
-			try {
-				$connect = new Dibi\Connection([
-					'driver' => 'sqlite3',
-					'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-				]);
-				$connect->query('
-                    UPDATE tabs SET', [
-					'name' => $array['data']['tabName'],
-					'url' => $array['data']['tabURL'],
-					'url_local' => $array['data']['tabLocalURL'],
-					'ping_url' => $array['data']['pingURL'],
-					'image' => $array['data']['tabImage'],
-					'timeout' => $array['data']['tabActionType'],
-					'timeout_ms' => $array['data']['tabActionTime'],
-				], '
-                    WHERE id=?', $array['data']['id']);
-				writeLog('success', 'Tab Editor Function -  Edited Tab Info for [' . $array['data']['tabName'] . ']', $GLOBALS['organizrUser']['username']);
-				return true;
-			} catch (Dibi\Exception $e) {
-				return false;
-			}
-		case 'changeOrder':
-			try {
-				$connect = new Dibi\Connection([
-					'driver' => 'sqlite3',
-					'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-				]);
-				foreach ($array['data']['tabs']['tab'] as $key => $value) {
-					if ($value['order'] != $value['originalOrder']) {
-						$connect->query('
-                            UPDATE tabs SET', [
-							'order' => $value['order'],
-						], '
-                            WHERE id=?', $value['id']);
-						writeLog('success', 'Tab Editor Function - ' . $value['name'] . ' Order Changed From ' . $value['order'] . ' to ' . $value['originalOrder'], $GLOBALS['organizrUser']['username']);
-					}
-				}
-				writeLog('success', 'Tab Editor Function - Tab Order Changed', $GLOBALS['organizrUser']['username']);
-				return true;
-			} catch (Dibi\Exception $e) {
-				return false;
-			}
-			break;
-		case 'addNewTab':
-			try {
-				$default = defaultTabCategory()['category_id'];
-				$connect = new Dibi\Connection([
-					'driver' => 'sqlite3',
-					'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-				]);
-				$newTab = [
-					'order' => $array['data']['tabOrder'],
-					'category_id' => $default,
-					'name' => $array['data']['tabName'],
-					'url' => $array['data']['tabURL'],
-					'url_local' => $array['data']['tabLocalURL'],
-					'ping_url' => $array['data']['pingURL'],
-					'default' => $array['data']['tabDefault'],
-					'enabled' => 1,
-					'group_id' => $array['data']['tabGroupID'],
-					'image' => $array['data']['tabImage'],
-					'type' => $array['data']['tabType'],
-					'timeout' => $array['data']['tabActionType'],
-					'timeout_ms' => $array['data']['tabActionTime'],
-				];
-				$connect->query('INSERT INTO [tabs]', $newTab);
-				writeLog('success', 'Tab Editor Function - Created Tab for: ' . $array['data']['tabName'], $GLOBALS['organizrUser']['username']);
-				return true;
-			} catch (Dibi\Exception $e) {
-				return false;
-			}
-			break;
-		default:
-			return false;
-			break;
-	}
-}
-
-function editCategories($array)
-{
-	switch ($array['data']['action']) {
-		case 'changeDefault':
-			try {
-				$connect = new Dibi\Connection([
-					'driver' => 'sqlite3',
-					'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-				]);
-				$connect->query('UPDATE categories SET `default` = 0');
-				$connect->query('
-                	UPDATE categories SET', [
-					'default' => 1
-				], '
-                	WHERE id=?', $array['data']['id']);
-				writeLog('success', 'Category Editor Function -  Changed Default Category from [' . $array['data']['oldCategoryName'] . '] to [' . $array['data']['newCategoryName'] . ']', $GLOBALS['organizrUser']['username']);
-				return true;
-			} catch (Dibi\Exception $e) {
-				return false;
-			}
-			break;
-		case 'deleteCategory':
-			try {
-				$connect = new Dibi\Connection([
-					'driver' => 'sqlite3',
-					'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-				]);
-				$connect->query('DELETE FROM categories WHERE id = ?', $array['data']['id']);
-				writeLog('success', 'Category Editor Function -  Deleted Category [' . $array['data']['category'] . ']', $GLOBALS['organizrUser']['username']);
-				return true;
-			} catch (Dibi\Exception $e) {
-				return false;
-			}
-			break;
-		case 'addNewCategory':
-			try {
-				$connect = new Dibi\Connection([
-					'driver' => 'sqlite3',
-					'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-				]);
-				$newCategory = [
-					'category' => $array['data']['categoryName'],
-					'order' => $array['data']['categoryOrder'],
-					'category_id' => $array['data']['categoryID'],
-					'default' => false,
-					'image' => $array['data']['categoryImage'],
-				];
-				$connect->query('INSERT INTO [categories]', $newCategory);
-				writeLog('success', 'Category Editor Function -  Added Category [' . $array['data']['categoryName'] . ']', $GLOBALS['organizrUser']['username']);
-				return true;
-			} catch (Dibi\Exception $e) {
-				return $e;
-			}
-			break;
-		case 'editCategory':
-			try {
-				$connect = new Dibi\Connection([
-					'driver' => 'sqlite3',
-					'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-				]);
-				$connect->query('
-                	UPDATE categories SET', [
-					'category' => $array['data']['name'],
-					'image' => $array['data']['image'],
-				], '
-                	WHERE id=?', $array['data']['id']);
-				writeLog('success', 'Category Editor Function -  Edited Category Info for [' . $array['data']['name'] . ']', $GLOBALS['organizrUser']['username']);
-				return true;
-			} catch (Dibi\Exception $e) {
-				return false;
-			}
-			break;
-		case 'changeOrder':
-			try {
-				$connect = new Dibi\Connection([
-					'driver' => 'sqlite3',
-					'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-				]);
-				foreach ($array['data']['categories']['category'] as $key => $value) {
-					if ($value['order'] != $value['originalOrder']) {
-						$connect->query('
-                            UPDATE categories SET', [
-							'order' => $value['order'],
-						], '
-                            WHERE id=?', $value['id']);
-						writeLog('success', 'Category Editor Function - ' . $value['name'] . ' Order Changed From ' . $value['order'] . ' to ' . $value['originalOrder'], $GLOBALS['organizrUser']['username']);
-					}
-				}
-				writeLog('success', 'Category Editor Function - Category Order Changed', $GLOBALS['organizrUser']['username']);
-				return true;
-			} catch (Dibi\Exception $e) {
-				return false;
-			}
-			break;
-		default:
-			return false;
-			break;
-	}
-}
-
-function allUsers()
-{
-	try {
-		$connect = new Dibi\Connection([
-			'driver' => 'sqlite3',
-			'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-		]);
-		$users = $connect->fetchAll('SELECT * FROM users');
-		$groups = $connect->fetchAll('SELECT * FROM groups ORDER BY group_id ASC');
-		foreach ($users as $k => $v) {
-			// clear password from array
-			unset($users[$k]['password']);
-		}
-		$all['users'] = $users;
-		$all['groups'] = $groups;
-		return $all;
-	} catch (Dibi\Exception $e) {
-		return false;
-	}
-}
+<?php
 
-function usernameTaken($username, $email)
+trait ApiFunctions
 {
-	try {
-		$connect = new Dibi\Connection([
-			'driver' => 'sqlite3',
-			'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-		]);
-		$all = $connect->fetch('SELECT * FROM users WHERE username = ? COLLATE NOCASE OR email = ? COLLATE NOCASE', $username, $email);
-		return ($all) ? true : false;
-	} catch (Dibi\Exception $e) {
-		return false;
-	}
-}
 
-function usernameTakenExcept($username, $email, $id)
-{
-	try {
-		$connect = new Dibi\Connection([
-			'driver' => 'sqlite3',
-			'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-		]);
-		$all = $connect->fetch('SELECT * FROM users WHERE id IS NOT ? AND username = ? COLLATE NOCASE OR id IS NOT ? AND email = ? COLLATE NOCASE', $id, $username, $id, $email);
-		return ($all) ? true : false;
-	} catch (Dibi\Exception $e) {
-		return false;
-	}
-}
-
-function createUser($username, $password, $defaults, $email = null)
-{
-	$email = ($email) ? $email : random_ascii_string(10) . '@placeholder.eml';
-	try {
-		if (!usernameTaken($username, $email)) {
-			$createDB = new Dibi\Connection([
-				'driver' => 'sqlite3',
-				'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-			]);
-			$userInfo = [
-				'username' => $username,
-				'password' => password_hash($password, PASSWORD_BCRYPT),
-				'email' => $email,
-				'group' => $defaults['group'],
-				'group_id' => $defaults['group_id'],
-				'image' => gravatar($email),
-				'register_date' => $GLOBALS['currentTime'],
-			];
-			$createDB->query('INSERT INTO [users]', $userInfo);
-			return true;
-		} else {
-			return false;
-		}
-	} catch (Dibi\Exception $e) {
-		return false;
-	}
-}
-
-function importUsers($array)
-{
-	$imported = 0;
-	$defaults = defaultUserGroup();
-	foreach ($array as $user) {
-		$password = random_ascii_string(30);
-		if ($user['username'] !== '' && $user['email'] !== '' && $password !== '' && $defaults !== '') {
-			$newUser = createUser($user['username'], $password, $defaults, $user['email']);
-			if (!$newUser) {
-				writeLog('error', 'Import Function - Error', $user['username']);
-			} else {
-				$imported++;
-			}
-		}
-	}
-	return $imported;
-}
-
-function importUsersType($array)
-{
-	$type = $array['data']['type'];
-	if ($type !== '') {
-		switch ($type) {
-			case 'plex':
-				return importUsers(allPlexUsers(true));
-				break;
-			case 'jellyfin':
-				return importUsers(allJellyfinUsers(true));
-				break;
-			case 'emby':
-				return importUsers(allEmbyUsers(true));
-				break;
-			default:
-				return false;
-		}
-	}
-	return false;
-}
-
-function allTabs()
-{
-	if (file_exists($GLOBALS['userConfigPath'])) {
-		try {
-			$connect = new Dibi\Connection([
-				'driver' => 'sqlite3',
-				'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-			]);
-			$all['tabs'] = $connect->fetchAll('SELECT * FROM tabs ORDER BY `order` ASC');
-			$all['categories'] = $connect->fetchAll('SELECT * FROM categories ORDER BY `order` ASC');
-			$all['groups'] = $connect->fetchAll('SELECT * FROM groups ORDER BY `group_id` ASC');
-			return $all;
-		} catch (Dibi\Exception $e) {
-			return false;
-		}
-	}
-	return false;
-}
-
-function allGroups()
-{
-	if (file_exists($GLOBALS['userConfigPath'])) {
-		try {
-			$connect = new Dibi\Connection([
-				'driver' => 'sqlite3',
-				'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-			]);
-			$all = $connect->fetchAll('SELECT * FROM groups ORDER BY `group_id` ASC');
-			return $all;
-		} catch (Dibi\Exception $e) {
-			return false;
-		}
-	}
-	return false;
-}
-
-function loadTabs($type = null)
-{
-	if (file_exists($GLOBALS['userConfigPath']) && $type) {
-		try {
-			$connect = new Dibi\Connection([
-				'driver' => 'sqlite3',
-				'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-			]);
-			$sort = ($GLOBALS['unsortedTabs'] == 'top') ? 'DESC' : 'ASC';
-			$tabs = $connect->fetchAll('SELECT * FROM tabs WHERE `group_id` >= ? AND `enabled` = 1 ORDER BY `order` ' . $sort, $GLOBALS['organizrUser']['groupID']);
-			$categories = $connect->fetchAll('SELECT * FROM categories ORDER BY `order` ASC');
-			$all['tabs'] = $tabs;
-			foreach ($tabs as $k => $v) {
-				$v['access_url'] = (!empty($v['url_local']) && ($v['url_local'] !== null) && ($v['url_local'] !== 'null') && isLocal() && $v['type'] !== 0) ? $v['url_local'] : $v['url'];
-			}
-			$count = array_map(function ($element) {
-				return $element['category_id'];
-			}, $tabs);
-			$count = (array_count_values($count));
-			foreach ($categories as $k => $v) {
-				$v['count'] = isset($count[$v['category_id']]) ? $count[$v['category_id']] : 0;
-			}
-			$all['categories'] = $categories;
-			switch ($type) {
-				case 'categories':
-					return $all['categories'];
-				case 'tabs':
-					return $all['tabs'];
-				default:
-					return $all;
-			}
-		} catch (Dibi\Exception $e) {
-			return false;
-		}
-	}
-	return false;
-}
-
-function getActiveTokens()
-{
-	try {
-		$connect = new Dibi\Connection([
-			'driver' => 'sqlite3',
-			'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-		]);
-		$all = $connect->fetchAll('SELECT * FROM `tokens` WHERE `user_id` = ? AND `expires` > ?', $GLOBALS['organizrUser']['userID'], $GLOBALS['currentTime']);
-		return $all;
-	} catch (Dibi\Exception $e) {
-		return false;
-	}
-}
-
-function revokeToken($array)
-{
-	if ($array['data']['token']) {
-		try {
-			$connect = new Dibi\Connection([
-				'driver' => 'sqlite3',
-				'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-			]);
-			$connect->query('DELETE FROM tokens WHERE user_id = ? AND token = ?', $GLOBALS['organizrUser']['userID'], $array['data']['token']);
-			return true;
-		} catch (Dibi\Exception $e) {
-			return false;
-		}
-	}
-}
-
-function getSchema()
-{
-	if (file_exists($GLOBALS['userConfigPath'])) {
-		try {
-			$connect = new Dibi\Connection([
-				'driver' => 'sqlite3',
-				'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-			]);
-			$result = $connect->fetchAll(' SELECT name, sql FROM sqlite_master WHERE type=\'table\' ORDER BY name');
-			return $result;
-		} catch (Dibi\Exception $e) {
-			return false;
-		}
-	} else {
-		return 'DB not set yet...';
-	}
-}
-
-function youtubeSearch($query)
-{
-	if (!$query) {
-		return 'no query provided!';
-	}
-	$keys = array('AIzaSyBsdt8nLJRMTwOq5PY5A5GLZ2q7scgn01w', 'AIzaSyD-8SHutB60GCcSM8q_Fle38rJUV7ujd8k', 'AIzaSyBzOpVBT6VII-b-8gWD0MOEosGg4hyhCsQ', 'AIzaSyBKnRe1P8fpfBHgooJpmT0WOsrdUtZ4cpk');
-	$randomKeyIndex = array_rand($keys);
-	$key = $keys[$randomKeyIndex];
-	$apikey = ($GLOBALS['youtubeAPI'] !== '') ? $GLOBALS['youtubeAPI'] : $key;
-	$results = false;
-	$url = "https://www.googleapis.com/youtube/v3/search?part=snippet&q=$query+official+trailer&part=snippet&maxResults=1&type=video&videoDuration=short&key=$apikey";
-	$response = Requests::get($url);
-	if ($response->success) {
-		$results = json_decode($response->body, true);
-	}
-	return ($results) ? $results : false;
-}
-
-function scrapePage($array)
-{
-	try {
-		$url = $array['data']['url'] ?? false;
-		$type = $array['data']['type'] ?? false;
-		if (!$url) return array(
-			'result' => 'Error',
-			'data' => 'No URL'
-		);
-		$url = qualifyURL($url);
-		$data = array(
-			'full_url' => $url,
-			'drill_url' => qualifyURL($url, true)
-		);
-		$options = array('verify' => false);
-		$response = Requests::get($url, array(), $options);
-		$data['response_code'] = $response->status_code;
-		if ($response->success) {
-			$data['result'] = 'Success';
-			switch ($type) {
-				case 'html':
-					$data['data'] = html_entity_decode($response->body);
-					break;
-				case 'json':
-					$data['data'] = json_decode($response->body);
-					break;
-				default:
-					$data['data'] = $response->body;
-			}
-			return $data;
-		}
-	} catch (Requests_Exception $e) {
-		return array(
-			'result' => 'Error',
-			'data' => $e->getMessage()
-		);
-	};
-	return array('result' => 'Error');
-}
-
-function searchCityForCoordinates($array)
-{
-	try {
-		$query = $array['data']['query'] ?? false;
-		$url = qualifyURL('https://api.mapbox.com/geocoding/v5/mapbox.places/' . urlencode($query) . '.json?access_token=pk.eyJ1IjoiY2F1c2VmeCIsImEiOiJjazhyeGxqeXgwMWd2M2ZydWQ4YmdjdGlzIn0.R50iYuMewh1CnUZ7sFPdHA&limit=5&fuzzyMatch=true');
-		$options = array('verify' => false);
-		$response = Requests::get($url, array(), $options);
-		if ($response->success) {
-			return json_decode($response->body);
-		}
-	} catch (Requests_Exception $e) {
-		return array(
-			'result' => 'Error',
-			'data' => $e->getMessage()
-		);
-	};
-	return array('result' => 'Error');
 }

+ 259 - 429
api/functions/auth-functions.php

@@ -1,299 +1,127 @@
 <?php
-function authRegister($username, $password, $defaults, $email, $token = null)
-{
-	if ($GLOBALS['authBackend'] !== '') {
-		ombiImport($GLOBALS['authBackend']);
-	}
-	ssoCheck($username, $password, $token);
-	if (createUser($username, $password, $defaults, $email)) {
-		writeLog('success', 'Registration Function - A User has registered', $username);
-		if ($GLOBALS['PHPMAILER-enabled'] && $email !== '') {
-			$emailTemplate = array(
-				'type' => 'registration',
-				'body' => $GLOBALS['PHPMAILER-emailTemplateRegisterUser'],
-				'subject' => $GLOBALS['PHPMAILER-emailTemplateRegisterUserSubject'],
-				'user' => $username,
-				'password' => null,
-				'inviteCode' => null,
-			);
-			$emailTemplate = phpmEmailTemplate($emailTemplate);
-			$sendEmail = array(
-				'to' => $email,
-				'user' => $username,
-				'subject' => $emailTemplate['subject'],
-				'body' => phpmBuildEmail($emailTemplate),
-			);
-			phpmSendEmail($sendEmail);
-		}
-		if (createToken($username, $email, gravatar($email), $defaults['group'], $defaults['group_id'], $GLOBALS['organizrHash'], $GLOBALS['rememberMeDays'])) {
-			writeLoginLog($username, 'success');
-			writeLog('success', 'Login Function - A User has logged in', $username);
-			return true;
-		}
-	} else {
-		writeLog('error', 'Registration Function - An error occurred', $username);
-		return 'username taken';
-	}
-	return false;
-}
 
-function checkPlexToken($token = '')
+trait AuthFunctions
 {
-	try {
-		if (($token !== '')) {
-			$url = 'https://plex.tv/users/account.json';
-			$headers = array(
-				'X-Plex-Token' => $token,
-				'Content-Type' => 'application/json',
-				'Accept' => 'application/json'
-			);
-			$response = Requests::get($url, $headers);
-			if ($response->success) {
-				return json_decode($response->body, true);
-			}
-		} else {
-			return false;
-		}
-		
-	} catch (Requests_Exception $e) {
-		writeLog('success', 'Plex Token Check Function - Error: ' . $e->getMessage(), SYSTEM);
+	public function testing()
+	{
+		return 'wasssup';
 	}
-	return false;
-}
-
-function checkPlexUser($username)
-{
-	try {
-		if (!empty($GLOBALS['plexToken'])) {
-			$url = 'https://plex.tv/api/users';
-			$headers = array(
-				'X-Plex-Token' => $GLOBALS['plexToken'],
-			);
-			$response = Requests::get($url, $headers);
-			if ($response->success) {
-				libxml_use_internal_errors(true);
-				$userXML = simplexml_load_string($response->body);
-				if (is_array($userXML) || is_object($userXML)) {
-					$usernameLower = strtolower($username);
-					foreach ($userXML as $child) {
-						if (isset($child['username']) && strtolower($child['username']) == $usernameLower || isset($child['email']) && strtolower($child['email']) == $usernameLower) {
-							writeLog('success', 'Plex User Check - Found User on Friends List', $username);
-							$machineMatches = false;
-							if ($GLOBALS['plexStrictFriends']) {
-								foreach ($child->Server as $server) {
-									if ((string)$server['machineIdentifier'] == $GLOBALS['plexID']) {
-										$machineMatches = true;
-									}
-								}
-							} else {
-								$machineMatches = true;
-							}
-							if ($machineMatches) {
-								writeLog('success', 'Plex User Check - User Approved for Login', $username);
-								return true;
-							} else {
-								writeLog('error', 'Plex User Check - User not Approved User', $username);
-							}
-						}
-					}
+	
+	public function checkPlexToken($token = '')
+	{
+		try {
+			if (($token !== '')) {
+				$url = 'https://plex.tv/users/account.json';
+				$headers = array(
+					'X-Plex-Token' => $token,
+					'Content-Type' => 'application/json',
+					'Accept' => 'application/json'
+				);
+				$response = Requests::get($url, $headers);
+				if ($response->success) {
+					return json_decode($response->body, true);
 				}
+			} else {
+				return false;
 			}
+			
+		} catch (Requests_Exception $e) {
+			$this->writeLog('success', 'Plex Token Check Function - Error: ' . $e->getMessage(), SYSTEM);
 		}
 		return false;
-	} catch (Requests_Exception $e) {
-		writeLog('error', 'Plex User Check Function - Error: ' . $e->getMessage(), $username);
 	}
-	return false;
-}
-
-function allPlexUsers($newOnly = false, $friendsOnly = false)
-{
-	try {
-		if (!empty($GLOBALS['plexToken'])) {
-			$url = 'https://plex.tv/api/users';
-			$headers = array(
-				'X-Plex-Token' => $GLOBALS['plexToken'],
-			);
-			$response = Requests::get($url, $headers);
-			if ($response->success) {
-				libxml_use_internal_errors(true);
-				$userXML = simplexml_load_string($response->body);
-				if (is_array($userXML) || is_object($userXML)) {
-					$results = array();
-					foreach ($userXML as $child) {
-						if (((string)$child['restricted'] == '0')) {
-							if ($newOnly) {
-								$taken = usernameTaken((string)$child['username'], (string)$child['email']);
-								if (!$taken) {
-									$results[] = array(
-										'username' => (string)$child['username'],
-										'email' => (string)$child['email'],
-										'id' => (string)$child['id'],
-									);
-								}
-							} elseif ($friendsOnly) {
+	
+	public function checkPlexUser($username)
+	{
+		try {
+			if (!empty($this->config['plexToken'])) {
+				$url = 'https://plex.tv/api/users';
+				$headers = array(
+					'X-Plex-Token' => $this->config['plexToken'],
+				);
+				$response = Requests::get($url, $headers);
+				if ($response->success) {
+					libxml_use_internal_errors(true);
+					$userXML = simplexml_load_string($response->body);
+					if (is_array($userXML) || is_object($userXML)) {
+						$usernameLower = strtolower($username);
+						foreach ($userXML as $child) {
+							if (isset($child['username']) && strtolower($child['username']) == $usernameLower || isset($child['email']) && strtolower($child['email']) == $usernameLower) {
+								$this->writeLog('success', 'Plex User Check - Found User on Friends List', $username);
 								$machineMatches = false;
-								foreach ($child->Server as $server) {
-									if ((string)$server['machineIdentifier'] == $GLOBALS['plexID']) {
-										$machineMatches = true;
+								if ($this->config['plexStrictFriends']) {
+									foreach ($child->Server as $server) {
+										if ((string)$server['machineIdentifier'] == $this->config['plexID']) {
+											$machineMatches = true;
+										}
 									}
+								} else {
+									$machineMatches = true;
 								}
-								if($machineMatches){
-									$results[] = array(
-										'username' => (string)$child['username'],
-										'email' => (string)$child['email'],
-										'id' => (string)$child['id'],
-									);
+								if ($machineMatches) {
+									$this->writeLog('success', 'Plex User Check - User Approved for Login', $username);
+									return true;
+								} else {
+									$this->writeLog('error', 'Plex User Check - User not Approved User', $username);
 								}
-							}else{
-								$results[] = array(
-									'username' => (string)$child['username'],
-									'email' => (string)$child['email'],
-									'id' => (string)$child['id'],
-								);
 							}
-							
 						}
 					}
-					return $results;
-				}
-			}
-		}
-		return false;
-	} catch (Requests_Exception $e) {
-		writeLog('success', 'Plex Import User Function - Error: ' . $e->getMessage(), 'SYSTEM');
-	}
-	return false;
-}
-
-function allJellyfinUsers($newOnly = false)
-{
-	try {
-		if (!empty($GLOBALS['embyURL']) && !empty($GLOBALS['embyToken'])) {
-			$url = qualifyURL($GLOBALS['embyURL']) . '/Users?api_key=' . $GLOBALS['embyToken'];
-			$headers = array();
-			$response = Requests::get($url, $headers);
-			if ($response->success) {
-				$users = json_decode($response->body, true);
-				if (is_array($users) || is_object($users)) {
-					$results = array();
-					foreach ($users as $child) {
-						// Jellyfin doesn't list emails for some reason
-						$email = random_ascii_string(10) . '@placeholder.eml';
-						if ($newOnly) {
-							$taken = usernameTaken((string)$child['Name'], $email);
-							if (!$taken) {
-								$results[] = array(
-									'username' => (string)$child['Name'],
-									'email' => $email
-								);
-							}
-						} else {
-							$results[] = array(
-								'username' => (string)$child['Name'],
-								'email' => $email,
-							);
-						}
-					}
-					return $results;
 				}
 			}
+			return false;
+		} catch (Requests_Exception $e) {
+			$this->writeLog('error', 'Plex User Check Function - Error: ' . $e->getMessage(), $username);
 		}
 		return false;
-	} catch (Requests_Exception $e) {
-		writeLog('success', 'Jellyfin Import User Function - Error: ' . $e->getMessage(), 'SYSTEM');
 	}
-	return false;
-}
-
-function allEmbyUsers($newOnly = false)
-{
-	try {
-		if (!empty($GLOBALS['embyURL']) && !empty($GLOBALS['embyToken'])) {
-			$url = qualifyURL($GLOBALS['embyURL']) . '/Users?api_key=' . $GLOBALS['embyToken'];
-			$headers = array();
-			$response = Requests::get($url, $headers);
+	
+	public function plugin_auth_plex($username, $password)
+	{
+		try {
+			$usernameLower = strtolower($username);
+			//Login User
+			$url = 'https://plex.tv/users/sign_in.json';
+			$headers = array(
+				'Accept' => 'application/json',
+				'Content-Type' => 'application/x-www-form-urlencoded',
+				'X-Plex-Product' => 'Organizr',
+				'X-Plex-Version' => '2.0',
+				'X-Plex-Client-Identifier' => $this->config['uuid'],
+			);
+			$data = array(
+				'user[login]' => $username,
+				'user[password]' => $password,
+			);
+			$options = array('timeout' => 30);
+			$response = Requests::post($url, $headers, $data, $options);
 			if ($response->success) {
-				$users = json_decode($response->body, true);
-				if (is_array($users) || is_object($users)) {
-					$results = array();
-					foreach ($users as $child) {
-						// Emby doesn't list emails for some reason
-						$email = random_ascii_string(10) . '@placeholder.eml';
-						if ($newOnly) {
-							$taken = usernameTaken((string)$child['Name'], $email);
-							if (!$taken) {
-								$results[] = array(
-									'username' => (string)$child['Name'],
-									'email' => $email
-								);
-							}
-						} else {
-							$results[] = array(
-								'username' => (string)$child['Name'],
-								'email' => $email,
-							);
-						}
+				$json = json_decode($response->body, true);
+				if ((is_array($json) && isset($json['user']) && isset($json['user']['username'])) && strtolower($json['user']['username']) == $usernameLower || strtolower($json['user']['email']) == $usernameLower) {
+					if ((!empty($this->config['plexAdmin']) && (strtolower($this->config['plexAdmin']) == strtolower($json['user']['username']) || strtolower($this->config['plexAdmin']) == strtolower($json['user']['email']))) || $this->checkPlexUser($json['user']['username'])) {
+						return array(
+							'username' => $json['user']['username'],
+							'email' => $json['user']['email'],
+							'image' => $json['user']['thumb'],
+							'token' => $json['user']['authToken']
+						);
 					}
-					return $results;
-				}
-			}
-		}
-		return false;
-	} catch (Requests_Exception $e) {
-		writeLog('success', 'Emby Import User Function - Error: ' . $e->getMessage(), 'SYSTEM');
-	}
-	return false;
-}
-
-function plugin_auth_plex($username, $password)
-{
-	try {
-		$usernameLower = strtolower($username);
-		//Login User
-		$url = 'https://plex.tv/users/sign_in.json';
-		$headers = array(
-			'Accept' => 'application/json',
-			'Content-Type' => 'application/x-www-form-urlencoded',
-			'X-Plex-Product' => 'Organizr',
-			'X-Plex-Version' => '2.0',
-			'X-Plex-Client-Identifier' => $GLOBALS['uuid'],
-		);
-		$data = array(
-			'user[login]' => $username,
-			'user[password]' => $password,
-		);
-		$options = array('timeout' => 30);
-		$response = Requests::post($url, $headers, $data, $options);
-		if ($response->success) {
-			$json = json_decode($response->body, true);
-			if ((is_array($json) && isset($json['user']) && isset($json['user']['username'])) && strtolower($json['user']['username']) == $usernameLower || strtolower($json['user']['email']) == $usernameLower) {
-				//writeLog("success", $json['user']['username']." was logged into organizr using plex credentials");
-				if ((!empty($GLOBALS['plexAdmin']) && (strtolower($GLOBALS['plexAdmin']) == strtolower($json['user']['username']) || strtolower($GLOBALS['plexAdmin']) == strtolower($json['user']['email']))) || checkPlexUser($json['user']['username'])) {
-					return array(
-						'username' => $json['user']['username'],
-						'email' => $json['user']['email'],
-						'image' => $json['user']['thumb'],
-						'token' => $json['user']['authToken']
-					);
 				}
 			}
+			return false;
+		} catch (Requests_Exception $e) {
+			$this->writeLog('success', 'Plex Auth Function - Error: ' . $e->getMessage(), $username);
 		}
 		return false;
-	} catch (Requests_Exception $e) {
-		writeLog('success', 'Plex Auth Function - Error: ' . $e->getMessage(), $username);
 	}
-	return false;
-}
-
-if (function_exists('ldap_connect')) {
+	
 	// Pass credentials to LDAP backend
-	function plugin_auth_ldap($username, $password)
+	public function plugin_auth_ldap($username, $password)
 	{
-		if (!empty($GLOBALS['authBaseDN']) && !empty($GLOBALS['authBackendHost'])) {
+		if (!empty($this->config['authBaseDN']) && !empty($this->config['authBackendHost'])) {
 			$ad = new \Adldap\Adldap();
 			// Create a configuration array.
-			$ldapServers = explode(',', $GLOBALS['authBackendHost']);
+			$ldapServers = explode(',', $this->config['authBackendHost']);
 			$i = 0;
 			foreach ($ldapServers as $key => $value) {
 				// Calculate parts
@@ -312,17 +140,17 @@ if (function_exists('ldap_connect')) {
 			$config = [
 				// Mandatory Configuration Options
 				'hosts' => $ldapHosts,
-				'base_dn' => $GLOBALS['authBaseDN'],
-				'username' => (empty($GLOBALS['ldapBindUsername'])) ? null : $GLOBALS['ldapBindUsername'],
-				'password' => (empty($GLOBALS['ldapBindPassword'])) ? null : decrypt($GLOBALS['ldapBindPassword']),
+				'base_dn' => $this->config['authBaseDN'],
+				'username' => (empty($this->config['ldapBindUsername'])) ? null : $this->config['ldapBindUsername'],
+				'password' => (empty($this->config['ldapBindPassword'])) ? null : $this->decrypt($this->config['ldapBindPassword']),
 				// Optional Configuration Options
 				'schema' => (($GLOBALS['ldapType'] == '1') ? Adldap\Schemas\ActiveDirectory::class : (($GLOBALS['ldapType'] == '2') ? Adldap\Schemas\OpenLDAP::class : Adldap\Schemas\FreeIPA::class)),
-				'account_prefix' => (empty($GLOBALS['authBackendHostPrefix'])) ? null : $GLOBALS['authBackendHostPrefix'],
-				'account_suffix' => (empty($GLOBALS['authBackendHostSuffix'])) ? null : $GLOBALS['authBackendHostSuffix'],
+				'account_prefix' => (empty($this->config['authBackendHostPrefix'])) ? null : $this->config['authBackendHostPrefix'],
+				'account_suffix' => (empty($this->config['authBackendHostSuffix'])) ? null : $this->config['authBackendHostSuffix'],
 				'port' => $ldapPort,
 				'follow_referrals' => false,
-				'use_ssl' => $GLOBALS['ldapSSL'],
-				'use_tls' => $GLOBALS['ldapTLS'],
+				'use_ssl' => $this->config['ldapSSL'],
+				'use_tls' => $this->config['ldapTLS'],
 				'version' => 3,
 				'timeout' => 5,
 				// Custom LDAP Options
@@ -340,7 +168,7 @@ if (function_exists('ldap_connect')) {
 				if ($provider->auth()->attempt($username, $password)) {
 					try {
 						// Try and get email from LDAP server
-						$accountDN = ((empty($GLOBALS['authBackendHostPrefix'])) ? null : $GLOBALS['authBackendHostPrefix']) . $username . ((empty($GLOBALS['authBackendHostSuffix'])) ? null : $GLOBALS['authBackendHostSuffix']);
+						$accountDN = ((empty($this->config['authBackendHostPrefix'])) ? null : $this->config['authBackendHostPrefix']) . $username . ((empty($this->config['authBackendHostSuffix'])) ? null : $this->config['authBackendHostSuffix']);
 						$record = $provider->search()->findByDnOrFail($accountDN);
 						$email = $record->getFirstAttribute('mail');
 					} catch (Adldap\Models\ModelNotFoundException $e) {
@@ -356,198 +184,200 @@ if (function_exists('ldap_connect')) {
 					return false;
 				}
 			} catch (\Adldap\Auth\BindException $e) {
-				writeLog('error', 'LDAP Function - Error: ' . $e->getMessage(), $username);
+				$this->writeLog('error', 'LDAP Function - Error: ' . $e->getMessage(), $username);
 				// There was an issue binding / connecting to the server.
 			} catch (Adldap\Auth\UsernameRequiredException $e) {
-				writeLog('error', 'LDAP Function - Error: ' . $e->getMessage(), $username);
+				$this->writeLog('error', 'LDAP Function - Error: ' . $e->getMessage(), $username);
 				// The user didn't supply a username.
 			} catch (Adldap\Auth\PasswordRequiredException $e) {
-				writeLog('error', 'LDAP Function - Error: ' . $e->getMessage(), $username);
+				$this->writeLog('error', 'LDAP Function - Error: ' . $e->getMessage(), $username);
 				// The user didn't supply a password.
 			}
 		}
 		return false;
 	}
-} else {
+	
 	// Ldap Auth Missing Dependency
-	function plugin_auth_ldap_disabled()
+	public function plugin_auth_ldap_disabled()
 	{
 		return 'LDAP - Disabled (Dependency: php-ldap missing!)';
 	}
-}
-// Pass credentials to FTP backend
-function plugin_auth_ftp($username, $password)
-{
-	// Calculate parts
-	$digest = parse_url($GLOBALS['authBackendHost']);
-	$scheme = strtolower((isset($digest['scheme']) ? $digest['scheme'] : (function_exists('ftp_ssl_connect') ? 'ftps' : 'ftp')));
-	$host = (isset($digest['host']) ? $digest['host'] : (isset($digest['path']) ? $digest['path'] : ''));
-	$port = (isset($digest['port']) ? $digest['port'] : 21);
-	// Determine Connection Type
-	if ($scheme == 'ftps') {
-		$conn_id = ftp_ssl_connect($host, $port, 20);
-	} elseif ($scheme == 'ftp') {
-		$conn_id = ftp_connect($host, $port, 20);
-	} else {
-		return false;
-	}
-	// Check if valid FTP connection
-	if ($conn_id) {
-		// Attempt login
-		@$login_result = ftp_login($conn_id, $username, $password);
-		ftp_close($conn_id);
-		// Return Result
-		if ($login_result) {
-			return true;
+	
+	// Pass credentials to FTP backend
+	public function plugin_auth_ftp($username, $password)
+	{
+		// Calculate parts
+		$digest = parse_url($this->config['authBackendHost']);
+		$scheme = strtolower((isset($digest['scheme']) ? $digest['scheme'] : (function_exists('ftp_ssl_connect') ? 'ftps' : 'ftp')));
+		$host = (isset($digest['host']) ? $digest['host'] : (isset($digest['path']) ? $digest['path'] : ''));
+		$port = (isset($digest['port']) ? $digest['port'] : 21);
+		// Determine Connection Type
+		if ($scheme == 'ftps') {
+			$conn_id = ftp_ssl_connect($host, $port, 20);
+		} elseif ($scheme == 'ftp') {
+			$conn_id = ftp_connect($host, $port, 20);
+		} else {
+			return false;
+		}
+		// Check if valid FTP connection
+		if ($conn_id) {
+			// Attempt login
+			@$login_result = ftp_login($conn_id, $username, $password);
+			ftp_close($conn_id);
+			// Return Result
+			if ($login_result) {
+				return true;
+			} else {
+				return false;
+			}
 		} else {
 			return false;
 		}
-	} else {
-		return false;
 	}
-}
-
-// Pass credentials to Emby Backend
-function plugin_auth_emby_local($username, $password)
-{
-	try {
-		$url = qualifyURL($GLOBALS['embyURL']) . '/Users/AuthenticateByName';
-		$headers = array(
-			'Authorization' => 'Emby UserId="e8837bc1-ad67-520e-8cd2-f629e3155721", Client="None", Device="Organizr", DeviceId="xxx", Version="1.0.0.0"',
-			'Content-Type' => 'application/json',
-		);
-		$data = array(
-			'Username' => $username,
-			'pw' => $password,
-			'Password' => sha1($password),
-			'PasswordMd5' => md5($password),
-		);
-		$response = Requests::post($url, $headers, json_encode($data));
-		if ($response->success) {
-			$json = json_decode($response->body, true);
-			if (is_array($json) && isset($json['SessionInfo']) && isset($json['User']) && $json['User']['HasPassword'] == true) {
-				// Login Success - Now Logout Emby Session As We No Longer Need It
-				$headers = array(
-					'X-Emby-Token' => $json['AccessToken'],
-					'X-Mediabrowser-Token' => $json['AccessToken'],
-				);
-				$response = Requests::post(qualifyURL($GLOBALS['embyURL']) . '/Sessions/Logout', $headers, array());
-				if ($response->success) {
-					return true;
+	
+	// Pass credentials to Emby Backend
+	public function plugin_auth_emby_local($username, $password)
+	{
+		try {
+			$url = $this->qualifyURL($this->config['embyURL']) . '/Users/AuthenticateByName';
+			$headers = array(
+				'Authorization' => 'Emby UserId="e8837bc1-ad67-520e-8cd2-f629e3155721", Client="None", Device="Organizr", DeviceId="xxx", Version="1.0.0.0"',
+				'Content-Type' => 'application/json',
+			);
+			$data = array(
+				'Username' => $username,
+				'pw' => $password,
+				'Password' => sha1($password),
+				'PasswordMd5' => md5($password),
+			);
+			$response = Requests::post($url, $headers, json_encode($data));
+			if ($response->success) {
+				$json = json_decode($response->body, true);
+				if (is_array($json) && isset($json['SessionInfo']) && isset($json['User']) && $json['User']['HasPassword'] == true) {
+					// Login Success - Now Logout Emby Session As We No Longer Need It
+					$headers = array(
+						'X-Emby-Token' => $json['AccessToken'],
+						'X-Mediabrowser-Token' => $json['AccessToken'],
+					);
+					$response = Requests::post($this->qualifyURL($this->config['embyURL']) . '/Sessions/Logout', $headers, array());
+					if ($response->success) {
+						return true;
+					}
 				}
 			}
+			return false;
+		} catch (Requests_Exception $e) {
+			$this->writeLog('error', 'Emby Local Auth Function - Error: ' . $e->getMessage(), $username);
 		}
 		return false;
-	} catch (Requests_Exception $e) {
-		writeLog('error', 'Emby Local Auth Function - Error: ' . $e->getMessage(), $username);
 	}
-	return false;
-}
-
-// Pass credentials to JellyFin Backend
-function plugin_auth_jellyfin($username, $password)
-{
-	try {
-		$url = qualifyURL($GLOBALS['embyURL']) . '/Users/authenticatebyname';
-		$headers = array(
-			'X-Emby-Authorization' => 'MediaBrowser Client="Organizr Auth", Device="Organizr", DeviceId="orgv2", Version="2.0"',
-			'Content-Type' => 'application/json',
-		);
-		$data = array(
-			'Username' => $username,
-			'Pw' => $password
-		);
-		$response = Requests::post($url, $headers, json_encode($data));
-		if ($response->success) {
-			$json = json_decode($response->body, true);
-			if (is_array($json) && isset($json['SessionInfo']) && isset($json['User']) && $json['User']['HasPassword'] == true) {
-				writeLog('success', 'JellyFin Auth Function - Found User and Logged In', $username);
-				// Login Success - Now Logout JellyFin Session As We No Longer Need It
-				$headers = array(
-					'X-Emby-Authorization' => 'MediaBrowser Client="Organizr Auth", Device="Organizr", DeviceId="orgv2", Version="2.0", Token="' . $json['AccessToken'] . '"',
-					'Content-Type' => 'application/json',
-				);
-				$response = Requests::post(qualifyURL($GLOBALS['embyURL']) . '/Sessions/Logout', $headers, array());
-				if ($response->success) {
-					return true;
+	
+	// Pass credentials to JellyFin Backend
+	public function plugin_auth_jellyfin($username, $password)
+	{
+		try {
+			$url = $this->qualifyURL($this->config['embyURL']) . '/Users/authenticatebyname';
+			$headers = array(
+				'X-Emby-Authorization' => 'MediaBrowser Client="Organizr Auth", Device="Organizr", DeviceId="orgv2", Version="2.0"',
+				'Content-Type' => 'application/json',
+			);
+			$data = array(
+				'Username' => $username,
+				'Pw' => $password
+			);
+			$response = Requests::post($url, $headers, json_encode($data));
+			if ($response->success) {
+				$json = json_decode($response->body, true);
+				if (is_array($json) && isset($json['SessionInfo']) && isset($json['User']) && $json['User']['HasPassword'] == true) {
+					$this->writeLog('success', 'JellyFin Auth Function - Found User and Logged In', $username);
+					// Login Success - Now Logout JellyFin Session As We No Longer Need It
+					$headers = array(
+						'X-Emby-Authorization' => 'MediaBrowser Client="Organizr Auth", Device="Organizr", DeviceId="orgv2", Version="2.0", Token="' . $json['AccessToken'] . '"',
+						'Content-Type' => 'application/json',
+					);
+					$response = Requests::post($this->qualifyURL($this->config['embyURL']) . '/Sessions/Logout', $headers, array());
+					if ($response->success) {
+						return true;
+					}
 				}
 			}
+			return false;
+		} catch (Requests_Exception $e) {
+			$this->writeLog('error', 'JellyFin Auth Function - Error: ' . $e->getMessage(), $username);
 		}
 		return false;
-	} catch (Requests_Exception $e) {
-		writeLog('error', 'JellyFin Auth Function - Error: ' . $e->getMessage(), $username);
 	}
-	return false;
-}
-
-// Authenticate against emby connect
-function plugin_auth_emby_connect($username, $password)
-{
-	// Emby disabled EmbyConnect on their API
-	// https://github.com/MediaBrowser/Emby/issues/3553
-	//return plugin_auth_emby_local($username, $password);
-	try {
-		// Get A User
-		$connectUserName = '';
-		$url = qualifyURL($GLOBALS['embyURL']) . '/Users?api_key=' . $GLOBALS['embyToken'];
-		$response = Requests::get($url);
-		if ($response->success) {
-			$json = json_decode($response->body, true);
-			if (is_array($json)) {
-				foreach ($json as $key => $value) { // Scan for this user
-					if (isset($value['ConnectUserName']) && isset($value['ConnectLinkType'])) { // Qualify as connect account
-						if (strtolower($value['ConnectUserName']) == $username || strtolower($value['Name']) == $username) {
-							$connectUserName = $value['ConnectUserName'];
-							writeLog('success', 'Emby Connect Auth Function - Found User', $username);
-							break;
+	
+	// Authenticate against emby connect
+	public function plugin_auth_emby_connect($username, $password)
+	{
+		// Emby disabled EmbyConnect on their API
+		// https://github.com/MediaBrowser/Emby/issues/3553
+		//return plugin_auth_emby_local($username, $password);
+		try {
+			// Get A User
+			$connectUserName = '';
+			$url = $this->qualifyURL($this->config['embyURL']) . '/Users?api_key=' . $this->config['embyToken'];
+			$response = Requests::get($url);
+			if ($response->success) {
+				$json = json_decode($response->body, true);
+				if (is_array($json)) {
+					foreach ($json as $key => $value) { // Scan for this user
+						if (isset($value['ConnectUserName']) && isset($value['ConnectLinkType'])) { // Qualify as connect account
+							if (strtolower($value['ConnectUserName']) == $username || strtolower($value['Name']) == $username) {
+								$connectUserName = $value['ConnectUserName'];
+								$this->writeLog('success', 'Emby Connect Auth Function - Found User', $username);
+								break;
+							}
 						}
 					}
-				}
-				if ($connectUserName) {
-					writeLog('success', 'Emby Connect Auth Function - Attempting to Login with Emby ID: ' . $connectUserName, $username);
-					$connectURL = 'https://connect.emby.media/service/user/authenticate';
-					$headers = array(
-						'Accept' => 'application/json',
-						'X-Application' => 'Organizr/2.0'
-					);
-					$data = array(
-						'nameOrEmail' => $username,
-						'rawpw' => $password,
-					);
-					$response = Requests::post($connectURL, $headers, $data);
-					if ($response->success) {
-						$json = json_decode($response->body, true);
-						if (is_array($json) && isset($json['AccessToken']) && isset($json['User']) && $json['User']['Name'] == $connectUserName) {
-							return array(
-								'email' => $json['User']['Email'],
-								//'image' => $json['User']['ImageUrl'],
-							);
+					if ($connectUserName) {
+						$this->writeLog('success', 'Emby Connect Auth Function - Attempting to Login with Emby ID: ' . $connectUserName, $username);
+						$connectURL = 'https://connect.emby.media/service/user/authenticate';
+						$headers = array(
+							'Accept' => 'application/json',
+							'X-Application' => 'Organizr/2.0'
+						);
+						$data = array(
+							'nameOrEmail' => $username,
+							'rawpw' => $password,
+						);
+						$response = Requests::post($connectURL, $headers, $data);
+						if ($response->success) {
+							$json = json_decode($response->body, true);
+							if (is_array($json) && isset($json['AccessToken']) && isset($json['User']) && $json['User']['Name'] == $connectUserName) {
+								return array(
+									'email' => $json['User']['Email'],
+									//'image' => $json['User']['ImageUrl'],
+								);
+							} else {
+								$this->writeLog('error', 'Emby Connect Auth Function - Bad Response', $username);
+							}
 						} else {
-							writeLog('error', 'Emby Connect Auth Function - Bad Response', $username);
+							$this->writeLog('error', 'Emby Connect Auth Function - 401 From Emby Connect', $username);
 						}
-					} else {
-						writeLog('error', 'Emby Connect Auth Function - 401 From Emby Connect', $username);
 					}
 				}
 			}
+			return false;
+		} catch (Requests_Exception $e) {
+			$this->writeLog('error', 'Emby Connect Auth Function - Error: ' . $e->getMessage(), $username);
+			return false;
 		}
-		return false;
-	} catch (Requests_Exception $e) {
-		writeLog('error', 'Emby Connect Auth Function - Error: ' . $e->getMessage(), $username);
-		return false;
 	}
-}
-
-// Authenticate Against Emby Local (first) and Emby Connect
-function plugin_auth_emby_all($username, $password)
-{
-	// Emby disabled EmbyConnect on their API
-	// https://github.com/MediaBrowser/Emby/issues/3553
-	$localResult = plugin_auth_emby_local($username, $password);
-	//return $localResult;
-	if ($localResult) {
-		return $localResult;
-	} else {
-		return plugin_auth_emby_connect($username, $password);
+	
+	// Authenticate Against Emby Local (first) and Emby Connect
+	public function plugin_auth_emby_all($username, $password)
+	{
+		// Emby disabled EmbyConnect on their API
+		// https://github.com/MediaBrowser/Emby/issues/3553
+		$localResult = $this->plugin_auth_emby_local($username, $password);
+		//return $localResult;
+		if ($localResult) {
+			return $localResult;
+		} else {
+			return $this->plugin_auth_emby_connect($username, $password);
+		}
 	}
+	
 }

+ 53 - 47
api/functions/backup-functions.php

@@ -1,56 +1,62 @@
 <?php
-function fileArray($files)
+
+trait BackupFunctions
 {
-	foreach ($files as $file) {
-		if (file_exists($file)) {
-			$list[] = $file;
+	
+	public function fileArray($files)
+	{
+		foreach ($files as $file) {
+			if (file_exists($file)) {
+				$list[] = $file;
+			}
+		}
+		if (!empty($list)) {
+			return $list;
 		}
 	}
-	if (!empty($list)) {
-		return $list;
-	}
-}
-
-function backupDB($type = 'config')
-{
-	$directory = $GLOBALS['dbLocation'] . 'backups' . DIRECTORY_SEPARATOR;
-	@mkdir($directory, 0770, true);
-	switch ($type) {
-		case 'config':
-			break;
-		case 'full':
-			break;
-		default:
+	
+	public function backupDB($type = 'config')
+	{
+		$directory = $this->config['dbLocation'] . 'backups' . DIRECTORY_SEPARATOR;
+		@mkdir($directory, 0770, true);
+		switch ($type) {
+			case 'config':
+				break;
+			case 'full':
+				break;
+			default:
+			
+		}
+		$orgFiles = array(
+			'orgLog' => $this->organizrLog,
+			'loginLog' => $this->organizrLoginLog,
+			'config' => $this->userConfigPath,
+			'database' => $this->config['dbLocation'] . $this->config['dbName']
+		);
+		$files = $this->fileArray($orgFiles);
+		if (!empty($files)) {
+			$this->writeLog('success', 'BACKUP: backup process started', 'SYSTEM');
+			$zipname = $directory . 'backup[' . date('Y-m-d_H-i') . '][' . $this->version . '].zip';
+			$zip = new ZipArchive;
+			$zip->open($zipname, ZipArchive::CREATE);
+			foreach ($files as $file) {
+				$zip->addFile($file);
+			}
+			$zip->close();
+			$this->writeLog('success', 'BACKUP: backup process finished', 'SYSTEM');
+			return true;
+		} else {
+			return false;
+		}
 		
 	}
-	$orgFiles = array(
-		'orgLog' => $GLOBALS['organizrLog'],
-		'loginLog' => $GLOBALS['organizrLoginLog'],
-		'config' => $GLOBALS['userConfigPath'],
-		'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName']
-	);
-	$files = fileArray($orgFiles);
-	if (!empty($files)) {
-		writeLog('success', 'BACKUP: backup process started', 'SYSTEM');
-		$zipname = $directory . 'backup[' . date('Y-m-d_H-i') . '][' . $GLOBALS['installedVersion'] . '].zip';
-		$zip = new ZipArchive;
-		$zip->open($zipname, ZipArchive::CREATE);
-		foreach ($files as $file) {
-			$zip->addFile($file);
-		}
-		$zip->close();
-		writeLog('success', 'BACKUP: backup process finished', 'SYSTEM');
-		return true;
-	} else {
-		return false;
+	
+	public function getBackups()
+	{
+		$path = $this->config['dbLocation'] . 'backups' . DIRECTORY_SEPARATOR;
+		@mkdir($path, 0770, true);
+		$files = array_diff(scandir($path), array('.', '..'));
+		return array_reverse($files);
 	}
 	
 }
-
-function getBackups()
-{
-	$path = $GLOBALS['dbLocation'] . 'backups' . DIRECTORY_SEPARATOR;
-	@mkdir($path, 0770, true);
-	$files = array_diff(scandir($path), array('.', '..'));
-	return array_reverse($files);
-}

+ 2 - 141
api/functions/config-functions.php

@@ -1,145 +1,6 @@
 <?php
-// Create config file in the return syntax
-function createConfig($array, $path = null, $nest = 0)
-{
-	$path = ($path) ? $path : $GLOBALS['userConfigPath'];
-	// Define Initial Value
-	$output = array();
-	// Sort Items
-	ksort($array);
-	// Update the current config version
-	if (!$nest) {
-		// Inject Current Version
-		$output[] = "\t'configVersion' => '" . (isset($array['apply_CONFIG_VERSION']) ? $array['apply_CONFIG_VERSION'] : $GLOBALS['installedVersion']) . "'";
-	}
-	unset($array['configVersion']);
-	unset($array['apply_CONFIG_VERSION']);
-	// Process Settings
-	foreach ($array as $k => $v) {
-		$allowCommit = true;
-		$item = '';
-		switch (gettype($v)) {
-			case 'boolean':
-				$item = ($v ? 'true' : 'false');
-				break;
-			case 'integer':
-			case 'double':
-			case 'NULL':
-				$item = $v;
-				break;
-			case 'string':
-				$item = "'" . str_replace(array('\\', "'"), array('\\\\', "\'"), $v) . "'";
-				break;
-			case 'array':
-				$item = createConfig($v, false, $nest + 1);
-				break;
-			default:
-				$allowCommit = false;
-		}
-		if ($allowCommit) {
-			$output[] = str_repeat("\t", $nest + 1) . "'$k' => $item";
-		}
-	}
-	// Build output
-	$output = (!$nest ? "<?php\nreturn " : '') . "array(\n" . implode(",\n", $output) . "\n" . str_repeat("\t", $nest) . ')' . (!$nest ? ';' : '');
-	if (!$nest && $path) {
-		$pathDigest = pathinfo($path);
-		@mkdir($pathDigest['dirname'], 0770, true);
-		if (file_exists($path)) {
-			rename($path, $pathDigest['dirname'] . '/' . $pathDigest['filename'] . '.bak.php');
-		}
-		$file = fopen($path, 'w');
-		fwrite($file, $output);
-		fclose($file);
-		if (file_exists($path)) {
-			return true;
-		}
-		// writeLog("error", "config was unable to write");
-		return false;
-	} else {
-		// writeLog("success", "config was updated with new values");
-		return $output;
-	}
-}
-
-// Commit new values to the configuration
-function updateConfig($new, $current = false)
-{
-	// Get config if not supplied
-	if ($current === false) {
-		$current = loadConfig();
-	} elseif (is_string($current) && is_file($current)) {
-		$current = loadConfig($current);
-	}
-	// Inject Parts
-	foreach ($new as $k => $v) {
-		$current[$k] = $v;
-	}
-	// Return Create
-	return createConfig($current);
-}
-
-function configLazy()
-{
-	// Load config or default
-	if (file_exists($GLOBALS['userConfigPath'])) {
-		$config = fillDefaultConfig(loadConfig($GLOBALS['userConfigPath']));
-	} else {
-		$config = fillDefaultConfig(loadConfig($GLOBALS['defaultConfigPath']));
-	}
-	if (is_array($config)) {
-		defineConfig($config);
-	}
-	return $config;
-}
 
-function loadConfig($path = null)
+trait ConfigFunctions
 {
-	$path = ($path) ? $path : $GLOBALS['userConfigPath'];
-	if (!is_file($path)) {
-		return null;
-	} else {
-		return (array)call_user_func(function () use ($path) {
-			return include($path);
-		});
-	}
-}
 
-function fillDefaultConfig($array)
-{
-	$path = $GLOBALS['defaultConfigPath'];
-	if (is_string($path)) {
-		$loadedDefaults = loadConfig($path);
-	} else {
-		$loadedDefaults = $path;
-	}
-	// Include all plugin config files
-	foreach (glob(dirname(__DIR__, 1) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . "*.php") as $filename) {
-		$loadedDefaults = array_merge($loadedDefaults, loadConfig($filename));
-	}
-	return (is_array($loadedDefaults) ? fillDefaultConfig_recurse($array, $loadedDefaults) : false);
-}
-
-function fillDefaultConfig_recurse($current, $defaults)
-{
-	foreach ($defaults as $k => $v) {
-		if (!isset($current[$k])) {
-			$current[$k] = $v;
-		} elseif (is_array($current[$k]) && is_array($v)) {
-			$current[$k] = fillDefaultConfig_recurse($current[$k], $v);
-		}
-	}
-	return $current;
-}
-
-function defineConfig($array, $anyCase = true, $nest_prefix = false)
-{
-	foreach ($array as $k => $v) {
-		if (is_scalar($v) && !defined($nest_prefix . $k)) {
-			$GLOBALS[$nest_prefix . $k] = $v;
-		} elseif (is_array($v)) {
-			$GLOBALS[$nest_prefix . $k] = $v;
-			defineConfig($v, $anyCase, $nest_prefix . $k . '_');
-		}
-	}
-}
+}

+ 1 - 12
api/functions/custom-class.php

@@ -1,13 +1,2 @@
 <?php
-class Requests_Auth_Digest extends Requests_Auth_Basic {
-	
-	/**
-	 * Set cURL parameters before the data is sent
-	 *
-	 * @param resource $handle cURL resource
-	 */
-	public function curl_before_send( &$handle ) {
-		curl_setopt( $handle, CURLOPT_USERPWD, $this->getAuthString() );
-		curl_setopt( $handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY ); //CURLAUTH_ANY work with Wowza RESTful
-	}
-}
+/* Depreciated */

+ 1 - 571
api/functions/deluge.class.php

@@ -1,572 +1,2 @@
 <?php
-
-class deluge
-{
-	private $ch;
-	private $url;
-	private $request_id;
-	
-	public function __construct($host, $password)
-	{
-		$this->url = $host . (substr($host, -1) == "/" ? "" : "/") . "json";
-		$this->request_id = 0;
-		$this->ch = curl_init($this->url);
-		$curl_options = array(
-			CURLOPT_RETURNTRANSFER => true,
-			CURLOPT_HTTPHEADER => array("Accept: application/json", "Content-Type: application/json"),
-			CURLOPT_ENCODING => "",
-			CURLOPT_COOKIEJAR => "",
-			CURLOPT_CONNECTTIMEOUT => 10,
-			CURLOPT_TIMEOUT => 10,
-			CURLOPT_CAINFO => getCert(),
-			CURLOPT_SSL_VERIFYHOST => localURL($host) ? 0 : 2,
-			CURLOPT_SSL_VERIFYPEER => localURL($host) ? 0 : 2,
-			//CURLOPT_SSL_VERIFYPEER => false, THIS IS INSECURE!! However, deluge appears not to send intermediate certificates, so it can be necessary. Use with caution!
-		);
-		curl_setopt_array($this->ch, $curl_options);
-		//Log in and get cookies
-		$result = $this->makeRequest("auth.login", array($password));
-		if (gettype($result) == 'boolean' && $result !== true) {
-			throw new Exception("Login failed");
-		} elseif (gettype($result) == 'string') {
-			throw new Exception($result);
-		}
-	}
-	
-	/////////////////////////////
-	//
-	//webapi functions (https://github.com/idlesign/deluge-webapi)
-	//
-	/////////////////////////////
-	//ids is an array of hashes, or null for all
-	//params is an array of params:
-	//active_time, all_time_download, comment, compact, distributed_copies, download_payload_rate, eta, file_priorities, file_progress, files, hash, is_auto_managed, is_finished, is_seed, max_connections, max_download_speed, max_upload_slots, max_upload_speed, message, move_completed, move_completed_path, move_on_completed, move_on_completed_path, name, next_announce, num_files, num_peers, num_pieces, num_seeds, paused, peers, piece_length, prioritize_first_last, private, progress, queue, ratio, remove_at_ratio, save_path, seed_rank, seeding_time, seeds_peers_ratio, state, stop_at_ratio, stop_ratio, time_added, total_done, total_payload_download, total_payload_upload, total_peers, total_seeds, total_size, total_uploaded, total_wanted, tracker, tracker_host, tracker_status, trackers, upload_payload_rate
-	//or an empty array for all (or null, which returns comment, hash, name, save_path)
-	public function getTorrents($ids, $params)
-	{
-		//if (isset($this->makeRequest("webapi.get_torrents", array($ids, $params))->torrents)) {
-		return $this->makeRequest("webapi.get_torrents", array($ids, $params))->torrents;
-		//} else {
-		//throw new Exception("Login failed");
-		//}
-	}
-	
-	//metainfo is base64 encoded torrent data or a magnet url
-	//returns a torrent hash or null if the torrent wasn't aded
-	//params are torrent addition parameters like download_location?
-	public function addTorrent($metaInfo, $params)
-	{
-		return $this->makeRequest("webapi.add_torrent", array($metaInfo, $params));
-	}
-	
-	/*implemented in core
-	public function removeTorrent($hash, $removeData) {
-		return $this->makeRequest("webapi.remove_torrent", array($hash, $removeData));
-	}*/
-	public function getWebAPIVersion()
-	{
-		return $this->makeRequest("webapi.get_api_version", array());
-	}
-	
-	/////////////////////////////
-	//
-	//core functions
-	//
-	//parsed from https://web.archive.org/web/20150423162855/http://deluge-torrent.org:80/docs/master/core/rpc.html
-	//
-	/////////////////////////////
-	//Adds a torrent file to the session.
-	//Parameters:
-	//filename (string) – the filename of the torrent
-	//filedump (string) – a base64 encoded string of the torrent file contents
-	//options (dict) – the options to apply to the torrent on add
-	public function addTorrentFile($filename, $filedump, $options)
-	{
-		return $this->makeRequest("core.add_torrent_file", array($filename, $filedump, $options));
-	}
-	
-	//Adds a torrent from a magnet link.
-	//Parameters:
-	//uri (string) – the magnet link
-	//options (dict) – the options to apply to the torrent on add
-	public function addTorrentMagnet($uri, $options)
-	{
-		return $this->makeRequest("core.add_torrent_magnet", array($uri, $options));
-	}
-	
-	//Adds a torrent from a url. Deluge will attempt to fetch the torrentfrom url prior to adding it to the session.
-	//Parameters:
-	//url (string) – the url pointing to the torrent file
-	//options (dict) – the options to apply to the torrent on add
-	//headers (dict) – any optional headers to send
-	public function addTorrentUrl($url, $options, $headers)
-	{
-		return $this->makeRequest("core.add_torrent_url", array($url, $options, $headers));
-	}
-	
-	public function connectPeer($torrentId, $ip, $port)
-	{
-		return $this->makeRequest("core.connect_peer", array($torrentId, $ip, $port));
-	}
-	
-	public function createTorrent($path, $tracker, $pieceLength, $comment, $target, $webseeds, $private, $createdBy, $trackers, $addToSession)
-	{
-		return $this->makeRequest("core.create_torrent", array($path, $tracker, $pieceLength, $comment, $target, $webseeds, $private, $createdBy, $trackers, $addToSession));
-	}
-	
-	public function disablePlugin($plugin)
-	{
-		return $this->makeRequest("core.disable_plugin", array($plugin));
-	}
-	
-	public function enablePlugin($plugin)
-	{
-		return $this->makeRequest("core.enable_plugin", array($plugin));
-	}
-	
-	public function forceReannounce($torrentIds)
-	{
-		return $this->makeRequest("core.force_reannounce", array($torrentIds));
-	}
-	
-	//Forces a data recheck on torrent_ids
-	public function forceRecheck($torrentIds)
-	{
-		return $this->makeRequest("core.force_recheck", array($torrentIds));
-	}
-	
-	//Returns a list of plugins available in the core
-	public function getAvailablePlugins()
-	{
-		return $this->makeRequest("core.get_available_plugins", array());
-	}
-	
-	//Returns a dictionary of the session’s cache status.
-	public function getCacheStatus()
-	{
-		return $this->makeRequest("core.get_cache_status", array());
-	}
-	
-	//Get all the preferences as a dictionary
-	public function getConfig()
-	{
-		return $this->makeRequest("core.get_config", array());
-	}
-	
-	//Get the config value for key
-	public function getConfigValue($key)
-	{
-		return $this->makeRequest("core.get_config_value", array($key));
-	}
-	
-	//Get the config values for the entered keys
-	public function getConfigValues($keys)
-	{
-		return $this->makeRequest("core.get_config_values", array($keys));
-	}
-	
-	//Returns a list of enabled plugins in the core
-	public function getEnabledPlugins()
-	{
-		return $this->makeRequest("core.get_enabled_plugins", array());
-	}
-	
-	//returns {field: [(value,count)] }for use in sidebar(s)
-	public function getFilterTree($showZeroHits, $hideCat)
-	{
-		return $this->makeRequest("core.get_filter_tree", array($showZeroHits, $hideCat));
-	}
-	
-	//Returns the number of free bytes at path
-	public function getFreeSpace($path)
-	{
-		return $this->makeRequest("core.get_free_space", array($path));
-	}
-	
-	//Returns the libtorrent version.
-	public function getLibtorrentVersion()
-	{
-		return $this->makeRequest("core.get_libtorrent_version", array());
-	}
-	
-	//Returns the active listen port
-	public function getListenPort()
-	{
-		return $this->makeRequest("core.get_listen_port", array());
-	}
-	
-	//Returns the current number of connections
-	public function getNumConnections()
-	{
-		return $this->makeRequest("core.get_num_connections", array());
-	}
-	
-	public function getPathSize($path)
-	{
-		return $this->makeRequest("core.get_path_size", array($path));
-	}
-	
-	//Returns a list of torrent_ids in the session.
-	public function getSessionState()
-	{
-		return $this->makeRequest("core.get_session_state", array());
-	}
-	
-	//Gets the session status values for ‘keys’, these keys are takingfrom libtorrent’s session status.
-	public function getSessionStatus($keys)
-	{
-		return $this->makeRequest("core.get_session_status", array($keys));
-	}
-	
-	public function getTorrentStatus($torrentId, $keys, $diff)
-	{
-		return $this->makeRequest("core.get_torrent_status", array($torrentId, $keys, $diff));
-	}
-	
-	//returns all torrents , optionally filtered by filter_dict.
-	public function getTorrentsStatus($filterDict, $keys, $diff)
-	{
-		return $this->makeRequest("core.get_torrents_status", array($filterDict, $keys, $diff));
-	}
-	
-	public function glob($path)
-	{
-		return $this->makeRequest("core.glob", array($path));
-	}
-	
-	public function moveStorage($torrentIds, $dest)
-	{
-		return $this->makeRequest("core.move_storage", array($torrentIds, $dest));
-	}
-	
-	//Pause all torrents in the session
-	public function pauseAllTorrents()
-	{
-		return $this->makeRequest("core.pause_all_torrents", array());
-	}
-	
-	public function pauseTorrent($torrentIds)
-	{
-		return $this->makeRequest("core.pause_torrent", array($torrentIds));
-	}
-	
-	public function queueBottom($torrentIds)
-	{
-		return $this->makeRequest("core.queue_bottom", array($torrentIds));
-	}
-	
-	public function queueDown($torrentIds)
-	{
-		return $this->makeRequest("core.queue_down", array($torrentIds));
-	}
-	
-	public function queueTop($torrentIds)
-	{
-		return $this->makeRequest("core.queue_top", array($torrentIds));
-	}
-	
-	public function queueUp($torrentIds)
-	{
-		return $this->makeRequest("core.queue_up", array($torrentIds));
-	}
-	
-	//Removes a torrent from the session.
-	//Parameters:
-	//torrentId (string) – the torrentId of the torrent to remove
-	//removeData (boolean) – if True, remove the data associated with this torrent
-	public function removeTorrent($torrentId, $removeData)
-	{
-		return $this->makeRequest("core.remove_torrent", array($torrentId, $removeData));
-	}
-	
-	//Rename files in torrent_id.  Since this is an asynchronous operation bylibtorrent, watch for the TorrentFileRenamedEvent to know when thefiles have been renamed.
-	//Parameters:
-	//torrentId (string) – the torrentId to rename files
-	//filenames (((index, filename), ...)) – a list of index, filename pairs
-	public function renameFiles($torrentId, $filenames)
-	{
-		return $this->makeRequest("core.rename_files", array($torrentId, $filenames));
-	}
-	
-	//Renames the ‘folder’ to ‘new_folder’ in ‘torrent_id’.  Watch for theTorrentFolderRenamedEvent which is emitted when the folder has beenrenamed successfully.
-	//Parameters:
-	//torrentId (string) – the torrent to rename folder in
-	//folder (string) – the folder to rename
-	//newFolder (string) – the new folder name
-	public function renameFolder($torrentId, $folder, $newFolder)
-	{
-		return $this->makeRequest("core.rename_folder", array($torrentId, $folder, $newFolder));
-	}
-	
-	//Rescans the plugin folders for new plugins
-	public function rescanPlugins()
-	{
-		return $this->makeRequest("core.rescan_plugins", array());
-	}
-	
-	//Resume all torrents in the session
-	public function resumeAllTorrents()
-	{
-		return $this->makeRequest("core.resume_all_torrents", array());
-	}
-	
-	public function resumeTorrent($torrentIds)
-	{
-		return $this->makeRequest("core.resume_torrent", array($torrentIds));
-	}
-	
-	//Set the config with values from dictionary
-	public function setConfig($config)
-	{
-		return $this->makeRequest("core.set_config", array($config));
-	}
-	
-	//Sets the auto managed flag for queueing purposes
-	public function setTorrentAutoManaged($torrentId, $value)
-	{
-		return $this->makeRequest("core.set_torrent_auto_managed", array($torrentId, $value));
-	}
-	
-	//Sets a torrents file priorities
-	public function setTorrentFilePriorities($torrentId, $priorities)
-	{
-		return $this->makeRequest("core.set_torrent_file_priorities", array($torrentId, $priorities));
-	}
-	
-	//Sets a torrents max number of connections
-	public function setTorrentMaxConnections($torrentId, $value)
-	{
-		return $this->makeRequest("core.set_torrent_max_connections", array($torrentId, $value));
-	}
-	
-	//Sets a torrents max download speed
-	public function setTorrentMaxDownloadSpeed($torrentId, $value)
-	{
-		return $this->makeRequest("core.set_torrent_max_download_speed", array($torrentId, $value));
-	}
-	
-	//Sets a torrents max number of upload slots
-	public function setTorrentMaxUploadSlots($torrentId, $value)
-	{
-		return $this->makeRequest("core.set_torrent_max_upload_slots", array($torrentId, $value));
-	}
-	
-	//Sets a torrents max upload speed
-	public function setTorrentMaxUploadSpeed($torrentId, $value)
-	{
-		return $this->makeRequest("core.set_torrent_max_upload_speed", array($torrentId, $value));
-	}
-	
-	//Sets the torrent to be moved when completed
-	public function setTorrentMoveCompleted($torrentId, $value)
-	{
-		return $this->makeRequest("core.set_torrent_move_completed", array($torrentId, $value));
-	}
-	
-	//Sets the path for the torrent to be moved when completed
-	public function setTorrentMoveCompletedPath($torrentId, $value)
-	{
-		return $this->makeRequest("core.set_torrent_move_completed_path", array($torrentId, $value));
-	}
-	
-	//Sets the torrent options for torrent_ids
-	public function setTorrentOptions($torrentIds, $options)
-	{
-		return $this->makeRequest("core.set_torrent_options", array($torrentIds, $options));
-	}
-	
-	//Sets a higher priority to the first and last pieces
-	public function setTorrentPrioritizeFirstLast($torrentId, $value)
-	{
-		return $this->makeRequest("core.set_torrent_prioritize_first_last", array($torrentId, $value));
-	}
-	
-	//Sets the torrent to be removed at ‘stop_ratio’
-	public function setTorrentRemoveAtRatio($torrentId, $value)
-	{
-		return $this->makeRequest("core.set_torrent_remove_at_ratio", array($torrentId, $value));
-	}
-	
-	//Sets the torrent to stop at ‘stop_ratio’
-	public function setTorrentStopAtRatio($torrentId, $value)
-	{
-		return $this->makeRequest("core.set_torrent_stop_at_ratio", array($torrentId, $value));
-	}
-	
-	//Sets the ratio when to stop a torrent if ‘stop_at_ratio’ is set
-	public function setTorrentStopRatio($torrentId, $value)
-	{
-		return $this->makeRequest("core.set_torrent_stop_ratio", array($torrentId, $value));
-	}
-	
-	//Sets a torrents tracker list.  trackers will be [{“url”, “tier”}]
-	public function setTorrentTrackers($torrentId, $trackers)
-	{
-		return $this->makeRequest("core.set_torrent_trackers", array($torrentId, $trackers));
-	}
-	
-	//Checks if the active port is open
-	public function testListenPort()
-	{
-		return $this->makeRequest("core.test_listen_port", array());
-	}
-	
-	public function uploadPlugin($filename, $filedump)
-	{
-		return $this->makeRequest("core.upload_plugin", array($filename, $filedump));
-	}
-	
-	//Returns a list of the exported methods.
-	public function getMethodList()
-	{
-		return $this->makeRequest("daemon.get_method_list", array());
-	}
-	
-	//Returns some info from the daemon.
-	public function info()
-	{
-		return $this->makeRequest("daemon.info", array());
-	}
-	
-	public function shutdown(...$params)
-	{
-		return $this->makeRequest("daemon.shutdown", $params);
-	}
-	
-	/////////////////////////////
-	//
-	//web ui functions
-	//
-	//parsed from https://web.archive.org/web/20150423143401/http://deluge-torrent.org:80/docs/master/modules/ui/web/json_api.html#module-deluge.ui.web.json_api
-	//
-	/////////////////////////////
-	//Parameters:
-	//host (string) – the hostname
-	//port (int) – the port
-	//username (string) – the username to login as
-	//password (string) – the password to login with
-	public function addHost($host, $port, $username, $password)
-	{
-		return $this->makeRequest("web.add_host", array($host, $port, $username, $password));
-	}
-	
-	//Usage
-	public function addTorrents($torrents)
-	{
-		return $this->makeRequest("web.add_torrents", array($torrents));
-	}
-	
-	public function connect($hostId)
-	{
-		return $this->makeRequest("web.connect", array($hostId));
-	}
-	
-	public function connected()
-	{
-		return $this->makeRequest("web.connected", array());
-	}
-	
-	public function deregisterEventListener($event)
-	{
-		return $this->makeRequest("web.deregister_event_listener", array($event));
-	}
-	
-	public function disconnect()
-	{
-		return $this->makeRequest("web.disconnect", array());
-	}
-	
-	public function downloadTorrentFromUrl($url, $cookie)
-	{
-		return $this->makeRequest("web.download_torrent_from_url", array($url, $cookie));
-	}
-	
-	/* in core
-	public function getConfig() {
-		return $this->makeRequest("web.get_config", array());
-	}*/
-	public function getEvents()
-	{
-		return $this->makeRequest("web.get_events", array());
-	}
-	
-	public function getHost($hostId)
-	{
-		return $this->makeRequest("web.get_host", array($hostId));
-	}
-	
-	public function getHostStatus($hostId)
-	{
-		return $this->makeRequest("web.get_host_status", array($hostId));
-	}
-	
-	public function getHosts()
-	{
-		return $this->makeRequest("web.get_hosts", array());
-	}
-	
-	public function getTorrentFiles($torrentId)
-	{
-		return $this->makeRequest("web.get_torrent_files", array($torrentId));
-	}
-	
-	public function getTorrentInfo($filename)
-	{
-		return $this->makeRequest("web.get_torrent_info", array($filename));
-	}
-	
-	public function registerEventListener($event)
-	{
-		return $this->makeRequest("web.register_event_listener", array($event));
-	}
-	
-	public function removeHost($connectionId)
-	{
-		return $this->makeRequest("web.remove_host", array($connectionId));
-	}
-	
-	/*in core
-	public function setConfig($config) {
-		return $this->makeRequest("web.set_config", array($config));
-	}*/
-	public function startDaemon($port)
-	{
-		return $this->makeRequest("web.start_daemon", array($port));
-	}
-	
-	public function stopDaemon($hostId)
-	{
-		return $this->makeRequest("web.stop_daemon", array($hostId));
-	}
-	
-	//Parameters:
-	//keys (list) – the information about the torrents to gather
-	//filterDict (dictionary) – the filters to apply when selecting torrents.
-	public function updateUi($keys, $filterDict)
-	{
-		return $this->makeRequest("web.update_ui", array($keys, $filterDict));
-	}
-	
-	private function makeRequest($method, $params)
-	{
-		$post_data = array("id" => $this->request_id, "method" => $method, "params" => $params);
-		curl_setopt($this->ch, CURLOPT_POSTFIELDS, json_encode($post_data));
-		$result = curl_exec($this->ch);
-		if ($result === false) {
-			throw new Exception("Could not log in due to curl error (no. " . curl_errno($this->ch) . "): " . curl_error($this->ch));
-		}
-		$http_code = curl_getinfo($this->ch, CURLINFO_HTTP_CODE);
-		if ($http_code != 200) {
-			throw new Exception("Request for method $method returned http code $http_code");
-		}
-		$result = json_decode($result);
-		if (!is_null($result->error)) {
-			throw new Exception("Method request returned an error (no. " . $result->error->code . "): " . $result->error->message);
-		}
-		if ($result->id != $this->request_id) {
-			throw new Exception("Response id did not match request id");
-		}
-		$this->request_id++;
-		return $result->result;
-	}
-}
+/* Depreciated */

+ 72 - 3267
api/functions/homepage-connect-functions.php

@@ -1,3296 +1,101 @@
 <?php
 /** @noinspection PhpUndefinedFieldInspection */
-function homepageConnect($array)
-{
-	switch ($array['data']['action']) {
-		case 'getSonarrQueue':
-			return (qualifyRequest($GLOBALS['homepageSonarrQueueAuth'])) ? sonarrConnectQueue() : false;
-			break;
-		case 'getRadarrQueue':
-			return (qualifyRequest($GLOBALS['homepageRadarrQueueAuth'])) ? radarrConnectQueue() : false;
-			break;
-		case 'getPlexStreams':
-			return (qualifyRequest($GLOBALS['homepagePlexStreamsAuth'])) ? plexConnect('streams') : false;
-			break;
-		case 'getPlexRecent':
-			return (qualifyRequest($GLOBALS['homepagePlexRecentAuth'])) ? plexConnect('recent') : false;
-			break;
-		case 'getPlexMetadata':
-			return (qualifyRequest($GLOBALS['homepagePlexAuth'])) ? plexConnect('metadata', $array['data']['key']) : false;
-			break;
-		case 'getPlexSearch':
-			return (qualifyRequest($GLOBALS['mediaSearchAuth'])) ? plexConnect('search', $array['data']['query']) : false;
-			break;
-		case 'getPlexPlaylists':
-			return (qualifyRequest($GLOBALS['homepagePlexPlaylistAuth'])) ? getPlexPlaylists() : false;
-			break;
-		case 'getEmbyStreams':
-			return (qualifyRequest($GLOBALS['homepageEmbyStreamsAuth']) && $GLOBALS['homepageEmbyEnabled']) ? embyConnect('streams') : false;
-			break;
-		case 'getEmbyRecent':
-			return (qualifyRequest($GLOBALS['homepageEmbyRecentAuth']) && $GLOBALS['homepageEmbyEnabled']) ? embyConnect('recent') : false;
-			break;
-		case 'getEmbyMetadata':
-			return (qualifyRequest($GLOBALS['homepageEmbyAuth']) && $GLOBALS['homepageEmbyEnabled']) ? embyConnect('metadata', $array['data']['key'], true) : false;
-			break;
-		case 'getJdownloader':
-			return jdownloaderConnect();
-			break;
-		case 'getSabnzbd':
-			return sabnzbdConnect();
-			break;
-		case 'getNzbget':
-			return nzbgetConnect();
-			break;
-		case 'getTransmission':
-			return transmissionConnect();
-			break;
-		case 'getqBittorrent':
-			return qBittorrentConnect();
-			break;
-		case 'getrTorrent':
-			return rTorrentConnect();
-			break;
-		case 'getDeluge':
-			return delugeConnect();
-			break;
-		case 'getCalendar':
-			return getCalendar();
-			break;
-		case 'getRequests':
-			return getOmbiRequests('both', $GLOBALS['ombiLimit']);
-			break;
-		case 'getHealthChecks':
-			return (qualifyRequest($GLOBALS['homepageHealthChecksAuth'])) ? getHealthChecks($array['data']['tags']) : false;
-			break;
-		case 'getUnifi':
-			return unifiConnect();
-			break;
-		case 'getTautulli':
-			return getTautulli();
-		case 'getPihole':
-			return getPihole();
-			break;
-		case 'getMonitorr':
-			return getMonitorr();
-			break;
-		case 'getWeatherAndAir':
-			return getWeatherAndAir();
-			break;
-		case 'getSpeedtest':
-			return getSpeedtest();
-			break;
-		case 'getNetdata':
-			return getNetdata();
-			break;
-		case 'getOctoprint':
-			return getOctoprint();
-			break;
-		default:
-			# code...
-			break;
-	}
-	return false;
-}
-
-function healthChecksTags($tags)
-{
-	$return = '?tag=';
-	if (!$tags) {
-		return '';
-	} elseif ($tags == '*') {
-		return '';
-	} else {
-		if (strpos($tags, ',') !== false) {
-			$list = explode(',', $tags);
-			return $return . implode("&tag=", $list);
-		} else {
-			return $return . $tags;
-		}
-	}
-}
-
-function getWeatherAndAir()
-{
-	if ($GLOBALS['homepageWeatherAndAirEnabled'] && !empty($GLOBALS['homepageWeatherAndAirLatitude']) && !empty($GLOBALS['homepageWeatherAndAirLongitude']) && qualifyRequest($GLOBALS['homepageWeatherAndAirAuth'])) {
-		$api['content'] = array(
-			'weather' => false,
-			'air' => false,
-			'pollen' => false
-		);
-		$apiURL = qualifyURL('https://api.breezometer.com/');
-		$info = '&lat=' . $GLOBALS['homepageWeatherAndAirLatitude'] . '&lon=' . $GLOBALS['homepageWeatherAndAirLongitude'] . '&units=' . $GLOBALS['homepageWeatherAndAirUnits'] . '&key=b7401295888443538a7ebe04719c8394';
-		try {
-			$headers = array();
-			$options = array();
-			if ($GLOBALS['homepageWeatherAndAirWeatherEnabled']) {
-				$endpoint = '/weather/v1/forecast/hourly?hours=120&metadata=true';
-				$response = Requests::get($apiURL . $endpoint . $info, $headers, $options);
-				if ($response->success) {
-					$apiData = json_decode($response->body, true);
-					$api['content']['weather'] = ($apiData['error'] === null) ? $apiData : false;
-					unset($apiData);
-				}
-			}
-			if ($GLOBALS['homepageWeatherAndAirAirQualityEnabled']) {
-				$endpoint = '/air-quality/v2/current-conditions?features=breezometer_aqi,local_aqi,health_recommendations,sources_and_effects,dominant_pollutant_concentrations,pollutants_concentrations,pollutants_aqi_information&metadata=true';
-				$response = Requests::get($apiURL . $endpoint . $info, $headers, $options);
-				if ($response->success) {
-					$apiData = json_decode($response->body, true);
-					$api['content']['air'] = ($apiData['error'] === null) ? $apiData : false;
-					unset($apiData);
-				}
-			}
-			if ($GLOBALS['homepageWeatherAndAirPollenEnabled']) {
-				$endpoint = '/pollen/v2/forecast/daily?features=plants_information,types_information&days=1&metadata=true';
-				$response = Requests::get($apiURL . $endpoint . $info, $headers, $options);
-				if ($response->success) {
-					$apiData = json_decode($response->body, true);
-					$api['content']['pollen'] = ($apiData['error'] === null) ? $apiData : false;
-					unset($apiData);
-				}
-			}
-		} catch (Requests_Exception $e) {
-			writeLog('error', 'Weather And Air Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
-		};
-		$api['content'] = isset($api['content']) ? $api['content'] : false;
-		return $api;
-	}
-	return false;
-}
-
-function getHealthChecks($tags = null)
-{
-	if ($GLOBALS['homepageHealthChecksEnabled'] && !empty($GLOBALS['healthChecksToken']) && !empty($GLOBALS['healthChecksURL']) && qualifyRequest($GLOBALS['homepageHealthChecksAuth'])) {
-		$api['content']['checks'] = array();
-		$tags = ($tags) ? healthChecksTags($tags) : '';
-		$healthChecks = explode(',', $GLOBALS['healthChecksToken']);
-		foreach ($healthChecks as $token) {
-			$url = qualifyURL($GLOBALS['healthChecksURL']) . '/' . $tags;
-			try {
-				$headers = array('X-Api-Key' => $token);
-				$options = (localURL($url)) ? array('verify' => false) : array();
-				$response = Requests::get($url, $headers, $options);
-				if ($response->success) {
-					$healthResults = json_decode($response->body, true);
-					$api['content']['checks'] = array_merge($api['content']['checks'], $healthResults['checks']);
-				}
-			} catch (Requests_Exception $e) {
-				writeLog('error', 'HealthChecks Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
-			};
-		}
-		usort($api['content']['checks'], function ($a, $b) {
-			return $a['status'] <=> $b['status'];
-		});
-		$api['content']['checks'] = isset($api['content']['checks']) ? $api['content']['checks'] : false;
-		return $api;
-	}
-	return false;
-}
-
-function getPihole()
-{
-	if ($GLOBALS['homepagePiholeEnabled'] && !empty($GLOBALS['piholeURL']) && qualifyRequest($GLOBALS['homepagePiholeAuth'])) {
-		$api = array();
-		$urls = explode(',', $GLOBALS['piholeURL']);
-		foreach ($urls as $url) {
-			$url = $url . '/api.php?';
-			try {
-				$response = Requests::get($url, [], []);
-				if ($response->success) {
-					$piholeResults = json_decode($response->body, true);
-					$ip = qualifyURL($url, true)['host'];
-					$api['data'][$ip] = $piholeResults;
-				}
-			} catch (Requests_Exception $e) {
-				writeLog('error', 'Pi-hole Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
-			};
-		}
-		$api['options']['combine'] = $GLOBALS['homepagePiholeCombine'];
-		$api['options']['title'] = $GLOBALS['piholeHeaderToggle'];
-		$api = isset($api) ? $api : false;
-		return $api;
-	}
-	return false;
-}
-
-function streamType($value)
-{
-	if ($value == "transcode" || $value == "Transcode") {
-		return "Transcode";
-	} elseif ($value == "copy" || $value == "DirectStream") {
-		return "Direct Stream";
-	} elseif ($value == "directplay" || $value == "DirectPlay") {
-		return "Direct Play";
-	} else {
-		return "Direct Play";
-	}
-}
-
-function resolveEmbyItem($itemDetails)
-{
-	/*
-	// Grab Each item info from Emby (extra call)
-	$id = isset($itemDetails['NowPlayingItem']['Id']) ? $itemDetails['NowPlayingItem']['Id'] : $itemDetails['Id'];
-	$url = qualifyURL($GLOBALS['embyURL']);
-	$url = $url . '/Items?Ids=' . $id . '&api_key=' . $GLOBALS['embyToken'] . '&Fields=Overview,People,Genres,CriticRating,Studios,Taglines';
-	try {
-		$options = (localURL($url)) ? array('verify' => false) : array();
-		$response = Requests::get($url, array(), $options);
-		if ($response->success) {
-			$item = json_decode($response->body, true)['Items'][0];
-		}
-	} catch (Requests_Exception $e) {
-		return false;
-	};
-	*/
-	$item = isset($itemDetails['NowPlayingItem']['Id']) ? $itemDetails['NowPlayingItem'] : $itemDetails;
-	// Static Height & Width
-	$height = getCacheImageSize('h');
-	$width = getCacheImageSize('w');
-	$nowPlayingHeight = getCacheImageSize('nph');
-	$nowPlayingWidth = getCacheImageSize('npw');
-	$actorHeight = 450;
-	$actorWidth = 300;
-	// Cache Directories
-	$cacheDirectory = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR;
-	$cacheDirectoryWeb = 'plugins/images/cache/';
-	// Types
-	$embyItem['array-item'] = $item;
-	$embyItem['array-itemdetails'] = $itemDetails;
-	switch (@$item['Type']) {
-		case 'Series':
-			$embyItem['type'] = 'tv';
-			$embyItem['title'] = $item['Name'];
-			$embyItem['secondaryTitle'] = '';
-			$embyItem['summary'] = '';
-			$embyItem['ratingKey'] = $item['Id'];
-			$embyItem['thumb'] = $item['Id'];
-			$embyItem['key'] = $item['Id'] . "-list";
-			$embyItem['nowPlayingThumb'] = $item['Id'];
-			$embyItem['nowPlayingKey'] = $item['Id'] . "-np";
-			$embyItem['metadataKey'] = $item['Id'];
-			$embyItem['nowPlayingImageType'] = isset($item['ImageTags']['Thumb']) ? 'Thumb' : (isset($item['BackdropImageTags'][0]) ? 'Backdrop' : '');
-			break;
-		case 'Episode':
-			$embyItem['type'] = 'tv';
-			$embyItem['title'] = $item['SeriesName'];
-			$embyItem['secondaryTitle'] = '';
-			$embyItem['summary'] = '';
-			$embyItem['ratingKey'] = $item['Id'];
-			$embyItem['thumb'] = (isset($item['SeriesId']) ? $item['SeriesId'] : $item['Id']);
-			$embyItem['key'] = (isset($item['SeriesId']) ? $item['SeriesId'] : $item['Id']) . "-list";
-			$embyItem['nowPlayingThumb'] = isset($item['ParentThumbItemId']) ? $item['ParentThumbItemId'] : (isset($item['ParentBackdropItemId']) ? $item['ParentBackdropItemId'] : false);
-			$embyItem['nowPlayingKey'] = isset($item['ParentThumbItemId']) ? $item['ParentThumbItemId'] . '-np' : (isset($item['ParentBackdropItemId']) ? $item['ParentBackdropItemId'] . '-np' : false);
-			$embyItem['metadataKey'] = $item['Id'];
-			$embyItem['nowPlayingImageType'] = isset($item['ImageTags']['Thumb']) ? 'Thumb' : (isset($item['ParentBackdropImageTags'][0]) ? 'Backdrop' : '');
-			$embyItem['nowPlayingTitle'] = @$item['SeriesName'] . ' - ' . @$item['Name'];
-			$embyItem['nowPlayingBottom'] = 'S' . @$item['ParentIndexNumber'] . ' · E' . @$item['IndexNumber'];
-			break;
-		case 'MusicAlbum':
-		case 'Audio':
-			$embyItem['type'] = 'music';
-			$embyItem['title'] = $item['Name'];
-			$embyItem['secondaryTitle'] = '';
-			$embyItem['summary'] = '';
-			$embyItem['ratingKey'] = $item['Id'];
-			$embyItem['thumb'] = $item['Id'];
-			$embyItem['key'] = $item['Id'] . "-list";
-			$embyItem['nowPlayingThumb'] = (isset($item['AlbumId']) ? $item['AlbumId'] : @$item['ParentBackdropItemId']);
-			$embyItem['nowPlayingKey'] = $item['Id'] . "-np";
-			$embyItem['metadataKey'] = isset($item['AlbumId']) ? $item['AlbumId'] : $item['Id'];
-			$embyItem['nowPlayingImageType'] = (isset($item['ParentBackdropItemId']) ? "Primary" : "Backdrop");
-			$embyItem['nowPlayingTitle'] = @$item['AlbumArtist'] . ' - ' . @$item['Name'];
-			$embyItem['nowPlayingBottom'] = @$item['Album'];
-			break;
-		case 'Movie':
-			$embyItem['type'] = 'movie';
-			$embyItem['title'] = $item['Name'];
-			$embyItem['secondaryTitle'] = '';
-			$embyItem['summary'] = '';
-			$embyItem['ratingKey'] = $item['Id'];
-			$embyItem['thumb'] = $item['Id'];
-			$embyItem['key'] = $item['Id'] . "-list";
-			$embyItem['nowPlayingThumb'] = $item['Id'];
-			$embyItem['nowPlayingKey'] = $item['Id'] . "-np";
-			$embyItem['metadataKey'] = $item['Id'];
-			$embyItem['nowPlayingImageType'] = isset($item['ImageTags']['Thumb']) ? "Thumb" : (isset($item['BackdropImageTags']) ? "Backdrop" : false);
-			$embyItem['nowPlayingTitle'] = @$item['Name'];
-			$embyItem['nowPlayingBottom'] = @$item['ProductionYear'];
-			break;
-		case 'Video':
-			$embyItem['type'] = 'video';
-			$embyItem['title'] = $item['Name'];
-			$embyItem['secondaryTitle'] = '';
-			$embyItem['summary'] = '';
-			$embyItem['ratingKey'] = $item['Id'];
-			$embyItem['thumb'] = $item['Id'];
-			$embyItem['key'] = $item['Id'] . "-list";
-			$embyItem['nowPlayingThumb'] = $item['Id'];
-			$embyItem['nowPlayingKey'] = $item['Id'] . "-np";
-			$embyItem['metadataKey'] = $item['Id'];
-			$embyItem['nowPlayingImageType'] = isset($item['ImageTags']['Thumb']) ? "Thumb" : (isset($item['BackdropImageTags']) ? "Backdrop" : false);
-			$embyItem['nowPlayingTitle'] = @$item['Name'];
-			$embyItem['nowPlayingBottom'] = @$item['ProductionYear'];
-			break;
-		default:
-			return false;
-	}
-	$embyItem['uid'] = $item['Id'];
-	$embyItem['imageType'] = (isset($item['ImageTags']['Primary']) ? "Primary" : false);
-	$embyItem['elapsed'] = isset($itemDetails['PlayState']['PositionTicks']) && $itemDetails['PlayState']['PositionTicks'] !== '0' ? (int)$itemDetails['PlayState']['PositionTicks'] : null;
-	$embyItem['duration'] = isset($itemDetails['NowPlayingItem']['RunTimeTicks']) ? (int)$itemDetails['NowPlayingItem']['RunTimeTicks'] : (int)(isset($item['RunTimeTicks']) ? $item['RunTimeTicks'] : '');
-	$embyItem['watched'] = ($embyItem['elapsed'] && $embyItem['duration'] ? floor(($embyItem['elapsed'] / $embyItem['duration']) * 100) : 0);
-	$embyItem['transcoded'] = isset($itemDetails['TranscodingInfo']['CompletionPercentage']) ? floor((int)$itemDetails['TranscodingInfo']['CompletionPercentage']) : 100;
-	$embyItem['stream'] = @$itemDetails['PlayState']['PlayMethod'];
-	$embyItem['id'] = $item['ServerId'];
-	$embyItem['session'] = @$itemDetails['DeviceId'];
-	$embyItem['bandwidth'] = isset($itemDetails['TranscodingInfo']['Bitrate']) ? $itemDetails['TranscodingInfo']['Bitrate'] / 1000 : '';
-	$embyItem['bandwidthType'] = 'wan';
-	$embyItem['sessionType'] = (@$itemDetails['PlayState']['PlayMethod'] == 'Transcode') ? 'Transcoding' : 'Direct Playing';
-	$embyItem['state'] = ((@(string)$itemDetails['PlayState']['IsPaused'] == '1') ? "pause" : "play");
-	$embyItem['user'] = ($GLOBALS['homepageShowStreamNames'] && qualifyRequest($GLOBALS['homepageShowStreamNamesAuth'])) ? @(string)$itemDetails['UserName'] : "";
-	$embyItem['userThumb'] = '';
-	$embyItem['userAddress'] = (isset($itemDetails['RemoteEndPoint']) ? $itemDetails['RemoteEndPoint'] : "x.x.x.x");
-	$embyURL = ($GLOBALS['homepageJellyfinInstead']) ? $GLOBALS['embyURL'] . '/web/index.html#!/itemdetails.html?id=' : 'https://app.emby.media/#!/item/item.html?id=';
-	$embyItem['address'] = $GLOBALS['embyTabURL'] ? rtrim($GLOBALS['embyTabURL'], '/') . "/web/#!/item/item.html?id=" . $embyItem['uid'] : $embyURL . $embyItem['uid'] . "&serverId=" . $embyItem['id'];
-	$embyItem['nowPlayingOriginalImage'] = 'api/?v1/image&source=emby&type=' . $embyItem['nowPlayingImageType'] . '&img=' . $embyItem['nowPlayingThumb'] . '&height=' . $nowPlayingHeight . '&width=' . $nowPlayingWidth . '&key=' . $embyItem['nowPlayingKey'] . '$' . randString();
-	$embyItem['originalImage'] = 'api/?v1/image&source=emby&type=' . $embyItem['imageType'] . '&img=' . $embyItem['thumb'] . '&height=' . $height . '&width=' . $width . '&key=' . $embyItem['key'] . '$' . randString();
-	$embyItem['openTab'] = $GLOBALS['embyTabURL'] && $GLOBALS['embyTabName'] ? true : false;
-	$embyItem['tabName'] = $GLOBALS['embyTabName'] ? $GLOBALS['embyTabName'] : '';
-	// Stream info
-	$embyItem['userStream'] = array(
-		'platform' => @(string)$itemDetails['Client'],
-		'product' => @(string)$itemDetails['Client'],
-		'device' => @(string)$itemDetails['DeviceName'],
-		'stream' => @$itemDetails['PlayState']['PlayMethod'],
-		'videoResolution' => isset($itemDetails['NowPlayingItem']['MediaStreams'][0]['Width']) ? $itemDetails['NowPlayingItem']['MediaStreams'][0]['Width'] : '',
-		'throttled' => false,
-		'sourceVideoCodec' => isset($itemDetails['NowPlayingItem']['MediaStreams'][0]) ? $itemDetails['NowPlayingItem']['MediaStreams'][0]['Codec'] : '',
-		'videoCodec' => @$itemDetails['TranscodingInfo']['VideoCodec'],
-		'audioCodec' => @$itemDetails['TranscodingInfo']['AudioCodec'],
-		'sourceAudioCodec' => isset($itemDetails['NowPlayingItem']['MediaStreams'][1]) ? $itemDetails['NowPlayingItem']['MediaStreams'][1]['Codec'] : (isset($itemDetails['NowPlayingItem']['MediaStreams'][0]) ? $itemDetails['NowPlayingItem']['MediaStreams'][0]['Codec'] : ''),
-		'videoDecision' => streamType(@$itemDetails['PlayState']['PlayMethod']),
-		'audioDecision' => streamType(@$itemDetails['PlayState']['PlayMethod']),
-		'container' => isset($itemDetails['NowPlayingItem']['Container']) ? $itemDetails['NowPlayingItem']['Container'] : '',
-		'audioChannels' => @$itemDetails['TranscodingInfo']['AudioChannels']
-	);
-	// Genre catch all
-	if (isset($item['Genres'])) {
-		$genres = array();
-		foreach ($item['Genres'] as $genre) {
-			$genres[] = $genre;
-		}
-	}
-	// Actor catch all
-	if (isset($item['People'])) {
-		$actors = array();
-		foreach ($item['People'] as $key => $value) {
-			if (@$value['PrimaryImageTag'] && @$value['Role']) {
-				if (file_exists($cacheDirectory . (string)$value['Id'] . '-cast.jpg')) {
-					$actorImage = $cacheDirectoryWeb . (string)$value['Id'] . '-cast.jpg';
-				}
-				if (file_exists($cacheDirectory . (string)$value['Id'] . '-cast.jpg') && (time() - 604800) > filemtime($cacheDirectory . (string)$value['Id'] . '-cast.jpg') || !file_exists($cacheDirectory . (string)$value['Id'] . '-cast.jpg')) {
-					$actorImage = 'api/?v1/image&source=emby&type=Primary&img=' . (string)$value['Id'] . '&height=' . $actorHeight . '&width=' . $actorWidth . '&key=' . (string)$value['Id'] . '-cast';
-				}
-				$actors[] = array(
-					'name' => (string)$value['Name'],
-					'role' => (string)$value['Role'],
-					'thumb' => $actorImage
-				);
-			}
-		}
-	}
-	// Metadata information
-	$embyItem['metadata'] = array(
-		'guid' => $item['Id'],
-		'summary' => @(string)$item['Overview'],
-		'rating' => @(string)$item['CommunityRating'],
-		'duration' => @(string)$item['RunTimeTicks'],
-		'originallyAvailableAt' => @(string)$item['PremiereDate'],
-		'year' => (string)isset($item['ProductionYear']) ? $item['ProductionYear'] : '',
-		//'studio' => (string)$item['studio'],
-		'tagline' => @(string)$item['Taglines'][0],
-		'genres' => (isset($item['Genres'])) ? $genres : '',
-		'actors' => (isset($item['People'])) ? $actors : ''
-	);
-	if (file_exists($cacheDirectory . $embyItem['nowPlayingKey'] . '.jpg')) {
-		$embyItem['nowPlayingImageURL'] = $cacheDirectoryWeb . $embyItem['nowPlayingKey'] . '.jpg';
-	}
-	if (file_exists($cacheDirectory . $embyItem['key'] . '.jpg')) {
-		$embyItem['imageURL'] = $cacheDirectoryWeb . $embyItem['key'] . '.jpg';
-	}
-	if (file_exists($cacheDirectory . $embyItem['nowPlayingKey'] . '.jpg') && (time() - 604800) > filemtime($cacheDirectory . $embyItem['nowPlayingKey'] . '.jpg') || !file_exists($cacheDirectory . $embyItem['nowPlayingKey'] . '.jpg')) {
-		$embyItem['nowPlayingImageURL'] = 'api/?v1/image&source=emby&type=' . $embyItem['nowPlayingImageType'] . '&img=' . $embyItem['nowPlayingThumb'] . '&height=' . $nowPlayingHeight . '&width=' . $nowPlayingWidth . '&key=' . $embyItem['nowPlayingKey'] . '';
-	}
-	if (file_exists($cacheDirectory . $embyItem['key'] . '.jpg') && (time() - 604800) > filemtime($cacheDirectory . $embyItem['key'] . '.jpg') || !file_exists($cacheDirectory . $embyItem['key'] . '.jpg')) {
-		$embyItem['imageURL'] = 'api/?v1/image&source=emby&type=' . $embyItem['imageType'] . '&img=' . $embyItem['thumb'] . '&height=' . $height . '&width=' . $width . '&key=' . $embyItem['key'] . '';
-	}
-	if (!$embyItem['nowPlayingThumb']) {
-		$embyItem['nowPlayingOriginalImage'] = $embyItem['nowPlayingImageURL'] = "plugins/images/cache/no-np.png";
-		$embyItem['nowPlayingKey'] = "no-np";
-	}
-	if (!$embyItem['thumb']) {
-		$embyItem['originalImage'] = $embyItem['imageURL'] = "plugins/images/cache/no-list.png";
-		$embyItem['key'] = "no-list";
-	}
-	if (isset($useImage)) {
-		$embyItem['useImage'] = $useImage;
-	}
-	return $embyItem;
-}
-
-function getCacheImageSize($type)
-{
-	switch ($type) {
-		case 'height':
-		case 'h':
-			return 300 * $GLOBALS['cacheImageSize'];
-			break;
-		case 'width':
-		case 'w':
-			return 200 * $GLOBALS['cacheImageSize'];
-			break;
-		case 'nowPlayingHeight':
-		case 'nph':
-			return 675 * $GLOBALS['cacheImageSize'];
-			break;
-		case 'nowPlayingWidth':
-		case 'npw':
-			return 1200 * $GLOBALS['cacheImageSize'];
-			break;
-	}
-}
-
-function resolvePlexItem($item)
-{
-	// Static Height & Width
-	$height = getCacheImageSize('h');
-	$width = getCacheImageSize('w');
-	$nowPlayingHeight = getCacheImageSize('nph');
-	$nowPlayingWidth = getCacheImageSize('npw');
-	// Cache Directories
-	$cacheDirectory = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR;
-	$cacheDirectoryWeb = 'plugins/images/cache/';
-	// Types
-	switch ($item['type']) {
-		case 'show':
-			$plexItem['type'] = 'tv';
-			$plexItem['title'] = (string)$item['title'];
-			$plexItem['secondaryTitle'] = (string)$item['year'];
-			$plexItem['summary'] = (string)$item['summary'];
-			$plexItem['ratingKey'] = (string)$item['ratingKey'];
-			$plexItem['thumb'] = (string)$item['thumb'];
-			$plexItem['key'] = (string)$item['ratingKey'] . "-list";
-			$plexItem['nowPlayingThumb'] = (string)$item['art'];
-			$plexItem['nowPlayingKey'] = (string)$item['ratingKey'] . "-np";
-			$plexItem['nowPlayingTitle'] = (string)$item['title'];
-			$plexItem['nowPlayingBottom'] = (string)$item['year'];
-			$plexItem['metadataKey'] = (string)$item['ratingKey'];
-			break;
-		case 'season':
-			$plexItem['type'] = 'tv';
-			$plexItem['title'] = (string)$item['parentTitle'];
-			$plexItem['secondaryTitle'] = (string)$item['title'];
-			$plexItem['summary'] = (string)$item['parentSummary'];
-			$plexItem['ratingKey'] = (string)$item['parentRatingKey'];
-			$plexItem['thumb'] = (string)$item['thumb'];
-			$plexItem['key'] = (string)$item['ratingKey'] . "-list";
-			$plexItem['nowPlayingThumb'] = (string)$item['art'];
-			$plexItem['nowPlayingKey'] = (string)$item['ratingKey'] . "-np";
-			$plexItem['metadataKey'] = (string)$item['parentRatingKey'];
-			break;
-		case 'episode':
-			$plexItem['type'] = 'tv';
-			$plexItem['title'] = (string)$item['grandparentTitle'];
-			$plexItem['secondaryTitle'] = (string)$item['parentTitle'];
-			$plexItem['summary'] = (string)$item['title'];
-			$plexItem['ratingKey'] = (string)$item['parentRatingKey'];
-			$plexItem['thumb'] = ($item['parentThumb'] ? (string)$item['parentThumb'] : (string)$item['grandparentThumb']);
-			$plexItem['key'] = (string)$item['ratingKey'] . "-list";
-			$plexItem['nowPlayingThumb'] = (string)$item['grandparentArt'];
-			$plexItem['nowPlayingKey'] = (string)$item['grandparentRatingKey'] . "-np";
-			$plexItem['nowPlayingTitle'] = (string)$item['grandparentTitle'] . ' - ' . (string)$item['title'];
-			$plexItem['nowPlayingBottom'] = 'S' . (string)$item['parentIndex'] . ' · E' . (string)$item['index'];
-			$plexItem['metadataKey'] = (string)$item['grandparentRatingKey'];
-			break;
-		case 'clip':
-			$useImage = (isset($item['live']) ? "plugins/images/cache/livetv.png" : null);
-			$plexItem['type'] = 'clip';
-			$plexItem['title'] = (isset($item['live']) ? 'Live TV' : (string)$item['title']);
-			$plexItem['secondaryTitle'] = '';
-			$plexItem['summary'] = (string)$item['summary'];
-			$plexItem['ratingKey'] = (string)$item['parentRatingKey'];
-			$plexItem['thumb'] = (string)$item['thumb'];
-			$plexItem['key'] = (string)$item['ratingKey'] . "-list";
-			$plexItem['nowPlayingThumb'] = (string)$item['art'];
-			$plexItem['nowPlayingKey'] = isset($item['ratingKey']) ? (string)$item['ratingKey'] . "-np" : (isset($item['live']) ? "livetv.png" : ":)");
-			$plexItem['nowPlayingTitle'] = $plexItem['title'];
-			$plexItem['nowPlayingBottom'] = isset($item['extraType']) ? "Trailer" : (isset($item['live']) ? "Live TV" : ":)");
-			break;
-		case 'album':
-		case 'track':
-			$plexItem['type'] = 'music';
-			$plexItem['title'] = (string)$item['parentTitle'];
-			$plexItem['secondaryTitle'] = (string)$item['title'];
-			$plexItem['summary'] = (string)$item['title'];
-			$plexItem['ratingKey'] = (string)$item['parentRatingKey'];
-			$plexItem['thumb'] = (string)$item['thumb'];
-			$plexItem['key'] = (string)$item['ratingKey'] . "-list";
-			$plexItem['nowPlayingThumb'] = ($item['parentThumb']) ? (string)$item['parentThumb'] : (string)$item['art'];
-			$plexItem['nowPlayingKey'] = (string)$item['parentRatingKey'] . "-np";
-			$plexItem['nowPlayingTitle'] = (string)$item['grandparentTitle'] . ' - ' . (string)$item['title'];
-			$plexItem['nowPlayingBottom'] = (string)$item['parentTitle'];
-			$plexItem['metadataKey'] = isset($item['grandparentRatingKey']) ? (string)$item['grandparentRatingKey'] : (string)$item['parentRatingKey'];
-			break;
-		default:
-			$plexItem['type'] = 'movie';
-			$plexItem['title'] = (string)$item['title'];
-			$plexItem['secondaryTitle'] = (string)$item['year'];
-			$plexItem['summary'] = (string)$item['summary'];
-			$plexItem['ratingKey'] = (string)$item['ratingKey'];
-			$plexItem['thumb'] = (string)$item['thumb'];
-			$plexItem['key'] = (string)$item['ratingKey'] . "-list";
-			$plexItem['nowPlayingThumb'] = (string)$item['art'];
-			$plexItem['nowPlayingKey'] = (string)$item['ratingKey'] . "-np";
-			$plexItem['nowPlayingTitle'] = (string)$item['title'];
-			$plexItem['nowPlayingBottom'] = (string)$item['year'];
-			$plexItem['metadataKey'] = (string)$item['ratingKey'];
-	}
-	$plexItem['originalType'] = $item['type'];
-	$plexItem['uid'] = (string)$item['ratingKey'];
-	$plexItem['elapsed'] = isset($item['viewOffset']) && $item['viewOffset'] !== '0' ? (int)$item['viewOffset'] : null;
-	$plexItem['duration'] = isset($item['duration']) ? (int)$item['duration'] : (int)$item->Media['duration'];
-	$plexItem['addedAt'] = isset($item['addedAt']) ? (int)$item['addedAt'] : null;
-	$plexItem['watched'] = ($plexItem['elapsed'] && $plexItem['duration'] ? floor(($plexItem['elapsed'] / $plexItem['duration']) * 100) : 0);
-	$plexItem['transcoded'] = isset($item->TranscodeSession['progress']) ? floor((int)$item->TranscodeSession['progress'] - $plexItem['watched']) : '';
-	$plexItem['stream'] = isset($item->Media->Part->Stream['decision']) ? (string)$item->Media->Part->Stream['decision'] : '';
-	$plexItem['id'] = str_replace('"', '', (string)$item->Player['machineIdentifier']);
-	$plexItem['session'] = (string)$item->Session['id'];
-	$plexItem['bandwidth'] = (string)$item->Session['bandwidth'];
-	$plexItem['bandwidthType'] = (string)$item->Session['location'];
-	$plexItem['sessionType'] = isset($item->TranscodeSession['progress']) ? 'Transcoding' : 'Direct Playing';
-	$plexItem['state'] = (((string)$item->Player['state'] == "paused") ? "pause" : "play");
-	$plexItem['user'] = ($GLOBALS['homepageShowStreamNames'] && qualifyRequest($GLOBALS['homepageShowStreamNamesAuth'])) ? (string)$item->User['title'] : "";
-	$plexItem['userThumb'] = ($GLOBALS['homepageShowStreamNames'] && qualifyRequest($GLOBALS['homepageShowStreamNamesAuth'])) ? (string)$item->User['thumb'] : "";
-	$plexItem['userAddress'] = ($GLOBALS['homepageShowStreamNames'] && qualifyRequest($GLOBALS['homepageShowStreamNamesAuth'])) ? (string)$item->Player['address'] : "x.x.x.x";
-	$plexItem['address'] = $GLOBALS['plexTabURL'] ? $GLOBALS['plexTabURL'] . "/web/index.html#!/server/" . $GLOBALS['plexID'] . "/details?key=/library/metadata/" . $item['ratingKey'] : "https://app.plex.tv/web/app#!/server/" . $GLOBALS['plexID'] . "/details?key=/library/metadata/" . $item['ratingKey'];
-	$plexItem['nowPlayingOriginalImage'] = 'api/?v1/image&source=plex&img=' . $plexItem['nowPlayingThumb'] . '&height=' . $nowPlayingHeight . '&width=' . $nowPlayingWidth . '&key=' . $plexItem['nowPlayingKey'] . '$' . randString();
-	$plexItem['originalImage'] = 'api/?v1/image&source=plex&img=' . $plexItem['thumb'] . '&height=' . $height . '&width=' . $width . '&key=' . $plexItem['key'] . '$' . randString();
-	$plexItem['openTab'] = $GLOBALS['plexTabURL'] && $GLOBALS['plexTabName'] ? true : false;
-	$plexItem['tabName'] = $GLOBALS['plexTabName'] ? $GLOBALS['plexTabName'] : '';
-	// Stream info
-	$plexItem['userStream'] = array(
-		'platform' => (string)$item->Player['platform'],
-		'product' => (string)$item->Player['product'],
-		'device' => (string)$item->Player['device'],
-		'stream' => isset($item->Media) ? (string)$item->Media->Part['decision'] . ($item->TranscodeSession['throttled'] == '1' ? ' (Throttled)' : '') : '',
-		'videoResolution' => (string)$item->Media['videoResolution'],
-		'throttled' => ($item->TranscodeSession['throttled'] == 1) ? true : false,
-		'sourceVideoCodec' => (string)$item->TranscodeSession['sourceVideoCodec'],
-		'videoCodec' => (string)$item->TranscodeSession['videoCodec'],
-		'audioCodec' => (string)$item->TranscodeSession['audioCodec'],
-		'sourceAudioCodec' => (string)$item->TranscodeSession['sourceAudioCodec'],
-		'videoDecision' => streamType((string)$item->TranscodeSession['videoDecision']),
-		'audioDecision' => streamType((string)$item->TranscodeSession['audioDecision']),
-		'container' => (string)$item->TranscodeSession['container'],
-		'audioChannels' => (string)$item->TranscodeSession['audioChannels']
-	);
-	// Genre catch all
-	if ($item->Genre) {
-		$genres = array();
-		foreach ($item->Genre as $key => $value) {
-			$genres[] = (string)$value['tag'];
-		}
-	}
-	// Actor catch all
-	if ($item->Role) {
-		$actors = array();
-		foreach ($item->Role as $key => $value) {
-			if ($value['thumb']) {
-				$actors[] = array(
-					'name' => (string)$value['tag'],
-					'role' => (string)$value['role'],
-					'thumb' => (string)$value['thumb']
-				);
-			}
-		}
-	}
-	// Metadata information
-	$plexItem['metadata'] = array(
-		'guid' => (string)$item['guid'],
-		'summary' => (string)$item['summary'],
-		'rating' => (string)$item['rating'],
-		'duration' => (string)$item['duration'],
-		'originallyAvailableAt' => (string)$item['originallyAvailableAt'],
-		'year' => (string)$item['year'],
-		'studio' => (string)$item['studio'],
-		'tagline' => (string)$item['tagline'],
-		'genres' => ($item->Genre) ? $genres : '',
-		'actors' => ($item->Role) ? $actors : ''
-	);
-	if (file_exists($cacheDirectory . $plexItem['nowPlayingKey'] . '.jpg')) {
-		$plexItem['nowPlayingImageURL'] = $cacheDirectoryWeb . $plexItem['nowPlayingKey'] . '.jpg';
-	}
-	if (file_exists($cacheDirectory . $plexItem['key'] . '.jpg')) {
-		$plexItem['imageURL'] = $cacheDirectoryWeb . $plexItem['key'] . '.jpg';
-	}
-	if (file_exists($cacheDirectory . $plexItem['nowPlayingKey'] . '.jpg') && (time() - 604800) > filemtime($cacheDirectory . $plexItem['nowPlayingKey'] . '.jpg') || !file_exists($cacheDirectory . $plexItem['nowPlayingKey'] . '.jpg')) {
-		$plexItem['nowPlayingImageURL'] = 'api/?v1/image&source=plex&img=' . $plexItem['nowPlayingThumb'] . '&height=' . $nowPlayingHeight . '&width=' . $nowPlayingWidth . '&key=' . $plexItem['nowPlayingKey'] . '';
-	}
-	if (file_exists($cacheDirectory . $plexItem['key'] . '.jpg') && (time() - 604800) > filemtime($cacheDirectory . $plexItem['key'] . '.jpg') || !file_exists($cacheDirectory . $plexItem['key'] . '.jpg')) {
-		$plexItem['imageURL'] = 'api/?v1/image&source=plex&img=' . $plexItem['thumb'] . '&height=' . $height . '&width=' . $width . '&key=' . $plexItem['key'] . '';
-	}
-	if (!$plexItem['nowPlayingThumb']) {
-		$plexItem['nowPlayingOriginalImage'] = $plexItem['nowPlayingImageURL'] = "plugins/images/cache/no-np.png";
-		$plexItem['nowPlayingKey'] = "no-np";
-	}
-	if (!$plexItem['thumb'] || $plexItem['addedAt'] >= (time() - 300)) {
-		$plexItem['originalImage'] = $plexItem['imageURL'] = "plugins/images/cache/no-list.png";
-		$plexItem['key'] = "no-list";
-	}
-	if (isset($useImage)) {
-		$plexItem['useImage'] = $useImage;
-	}
-	return $plexItem;
-}
-
-function plexConnect($action, $key = null)
-{
-	if ($GLOBALS['homepagePlexEnabled'] && !empty($GLOBALS['plexURL']) && !empty($GLOBALS['plexToken']) && !empty($GLOBALS['plexID'] && qualifyRequest($GLOBALS['homepagePlexAuth']))) {
-		$url = qualifyURL($GLOBALS['plexURL']);
-		$multipleURL = false;
-		$ignore = array();
-		$resolve = true;
-		switch ($action) {
-			case 'streams':
-				$url = $url . "/status/sessions?X-Plex-Token=" . $GLOBALS['plexToken'];
-				break;
-			case 'libraries':
-				$url = $url . "/library/sections?X-Plex-Token=" . $GLOBALS['plexToken'];
-				$resolve = false;
-				break;
-			case 'recent':
-				//$url = $url . "/library/recentlyAdded?X-Plex-Token=" . $GLOBALS['plexToken'] . "&limit=" . $GLOBALS['homepageRecentLimit'];
-				$urls['movie'] = $url . "/hubs/home/recentlyAdded?X-Plex-Token=" . $GLOBALS['plexToken'] . "&X-Plex-Container-Start=0&X-Plex-Container-Size=" . $GLOBALS['homepageRecentLimit'] . "&type=1";
-				$urls['tv'] = $url . "/hubs/home/recentlyAdded?X-Plex-Token=" . $GLOBALS['plexToken'] . "&X-Plex-Container-Start=0&X-Plex-Container-Size=" . $GLOBALS['homepageRecentLimit'] . "&type=2";
-				$urls['music'] = $url . "/hubs/home/recentlyAdded?X-Plex-Token=" . $GLOBALS['plexToken'] . "&X-Plex-Container-Start=0&X-Plex-Container-Size=" . $GLOBALS['homepageRecentLimit'] . "&type=8";
-				$multipleURL = true;
-				break;
-			case 'metadata':
-				$url = $url . "/library/metadata/" . $key . "?X-Plex-Token=" . $GLOBALS['plexToken'];
-				break;
-			case 'playlists':
-				$url = $url . "/playlists?X-Plex-Token=" . $GLOBALS['plexToken'];
-				break;
-			case 'search':
-				$url = $url . "/search?query=" . rawurlencode($key) . "&X-Plex-Token=" . $GLOBALS['plexToken'];
-				$ignore = array('artist', 'episode');
-				break;
-			default:
-				# code...
-				break;
-		}
-		try {
-			if (!$multipleURL) {
-				$options = (localURL($url)) ? array('verify' => false) : array();
-				$response = Requests::get($url, array(), $options);
-				libxml_use_internal_errors(true);
-				if ($response->success) {
-					$items = array();
-					$plex = simplexml_load_string($response->body);
-					foreach ($plex as $child) {
-						if (!in_array($child['type'], $ignore) && isset($child['librarySectionID'])) {
-							$items[] = resolvePlexItem($child);
-						}
-					}
-					$api['content'] = ($resolve) ? $items : $plex;
-					$api['plexID'] = $GLOBALS['plexID'];
-					$api['showNames'] = true;
-					$api['group'] = '1';
-					return $api;
-				}
-			} else {
-				foreach ($urls as $k => $v) {
-					$options = (localURL($v)) ? array('verify' => false) : array();
-					$response = Requests::get($v, array(), $options);
-					libxml_use_internal_errors(true);
-					if ($response->success) {
-						$items = array();
-						$plex = simplexml_load_string($response->body);
-						foreach ($plex as $child) {
-							if (!in_array($child['type'], $ignore) && isset($child['librarySectionID'])) {
-								$items[] = resolvePlexItem($child);
-							}
-						}
-						if (isset($api)) {
-							$api['content'] = array_merge($api['content'], ($resolve) ? $items : $plex);
-						} else {
-							$api['content'] = ($resolve) ? $items : $plex;
-						}
-					}
-				}
-				if (isset($api['content'])) {
-					usort($api['content'], function ($a, $b) {
-						return $b['addedAt'] <=> $a['addedAt'];
-					});
-				}
-				$api['plexID'] = $GLOBALS['plexID'];
-				$api['showNames'] = true;
-				$api['group'] = '1';
-				return $api;
-			}
-		} catch (Requests_Exception $e) {
-			writeLog('error', 'Plex Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
-		};
-	}
-	return false;
-}
-
-function getPlexPlaylists()
-{
-	if ($GLOBALS['homepagePlexEnabled'] && !empty($GLOBALS['plexURL']) && !empty($GLOBALS['plexToken']) && !empty($GLOBALS['plexID'] && qualifyRequest($GLOBALS['homepagePlexAuth']) && qualifyRequest($GLOBALS['homepagePlexPlaylistAuth']) && $GLOBALS['homepagePlexPlaylist'])) {
-		$url = qualifyURL($GLOBALS['plexURL']);
-		$url = $url . "/playlists?X-Plex-Token=" . $GLOBALS['plexToken'];
-		try {
-			$options = (localURL($url)) ? array('verify' => false) : array();
-			$response = Requests::get($url, array(), $options);
-			libxml_use_internal_errors(true);
-			if ($response->success) {
-				$items = array();
-				$plex = simplexml_load_string($response->body);
-				foreach ($plex as $child) {
-					if ($child['playlistType'] == "video" && strpos(strtolower($child['title']), 'private') === false) {
-						$playlistTitleClean = preg_replace("/(\W)+/", "", (string)$child['title']);
-						$playlistURL = qualifyURL($GLOBALS['plexURL']);
-						$playlistURL = $playlistURL . $child['key'] . "?X-Plex-Token=" . $GLOBALS['plexToken'];
-						$options = (localURL($url)) ? array('verify' => false) : array();
-						$playlistResponse = Requests::get($playlistURL, array(), $options);
-						if ($playlistResponse->success) {
-							$playlistResponse = simplexml_load_string($playlistResponse->body);
-							$items[$playlistTitleClean]['title'] = (string)$child['title'];
-							foreach ($playlistResponse->Video as $playlistItem) {
-								$items[$playlistTitleClean][] = resolvePlexItem($playlistItem);
-							}
-						}
-					}
-				}
-				$api['content'] = $items;
-				$api['plexID'] = $GLOBALS['plexID'];
-				$api['showNames'] = true;
-				$api['group'] = '1';
-				return $api;
-			}
-		} catch (Requests_Exception $e) {
-			writeLog('error', 'Plex Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
-		};
-	}
-	return false;
-}
-
-function embyConnect($action, $key = 'Latest', $skip = false)
-{
-	if ($GLOBALS['homepageEmbyEnabled'] && !empty($GLOBALS['embyURL']) && !empty($GLOBALS['embyToken']) && qualifyRequest($GLOBALS['homepageEmbyAuth'])) {
-		$url = qualifyURL($GLOBALS['embyURL']);
-		switch ($action) {
-			case 'streams':
-				$url = $url . '/Sessions?api_key=' . $GLOBALS['embyToken'] . '&Fields=Overview,People,Genres,CriticRating,Studios,Taglines';
-				break;
-			case 'recent':
-			case 'metadata':
-				$username = false;
-				if (isset($GLOBALS['organizrUser']['username'])) {
-					$username = strtolower($GLOBALS['organizrUser']['username']);
-				}
-				// Get A User
-				$userIds = $url . "/Users?api_key=" . $GLOBALS['embyToken'];
-				$showPlayed = false;
-				try {
-					$options = (localURL($userIds)) ? array('verify' => false) : array();
-					$response = Requests::get($userIds, array(), $options);
-					if ($response->success) {
-						$emby = json_decode($response->body, true);
-						foreach ($emby as $value) { // Scan for admin user
-							if (isset($value['Policy']) && isset($value['Policy']['IsAdministrator']) && $value['Policy']['IsAdministrator']) {
-								$userId = $value['Id'];
-							}
-							if ($username && strtolower($value['Name']) == $username) {
-								$userId = $value['Id'];
-								$showPlayed = false;
-								break;
-							}
-						}
-						$url = $url . '/Users/' . $userId . '/Items/' . $key . '?EnableImages=true&Limit=' . $GLOBALS['homepageRecentLimit'] . '&api_key=' . $GLOBALS['embyToken'] . ($showPlayed ? '' : '&IsPlayed=false') . '&Fields=Overview,People,Genres,CriticRating,Studios,Taglines';
-					}
-				} catch (Requests_Exception $e) {
-					writeLog('error', 'Emby Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
-				};
-				break;
-			default:
-				# code...
-				break;
-		}
-		try {
-			$options = (localURL($url)) ? array('verify' => false) : array();
-			$response = Requests::get($url, array(), $options);
-			if ($response->success) {
-				$items = array();
-				$emby = json_decode($response->body, true);
-				if ($key !== 'Latest') {
-					if (isset($emby['NowPlayingItem']) || isset($emby['Name'])) {
-						$items[] = resolveEmbyItem($emby);
-					}
-				} else {
-					foreach ($emby as $child) {
-						if (isset($child['NowPlayingItem']) || isset($child['Name'])) {
-							$items[] = resolveEmbyItem($child);
-						}
-					}
-				}
-				$api['content'] = array_filter($items);
-				return $api;
-			}
-		} catch (Requests_Exception $e) {
-			writeLog('error', 'Emby Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
-		};
-	}
-	return false;
-}
-
-function jdownloaderConnect()
-{
-	if ($GLOBALS['homepageJdownloaderEnabled'] && !empty($GLOBALS['jdownloaderURL']) && qualifyRequest($GLOBALS['homepageJdownloaderAuth'])) {
-		$url = qualifyURL($GLOBALS['jdownloaderURL']);
-		try {
-			$options = (localURL($url)) ? array('verify' => false, 'timeout' => 30) : array('timeout' => 30);
-			$response = Requests::get($url, array(), $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) {
-			writeLog('error', 'JDownloader Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
-		};
-		$api['content'] = isset($api['content']) ? $api['content'] : false;
-		return $api;
-	}
-	return false;
-}
-
-function sabnzbdConnect()
-{
-	if ($GLOBALS['homepageSabnzbdEnabled'] && !empty($GLOBALS['sabnzbdURL']) && !empty($GLOBALS['sabnzbdToken']) && qualifyRequest($GLOBALS['homepageSabnzbdAuth'])) {
-		$url = qualifyURL($GLOBALS['sabnzbdURL']);
-		$url = $url . '/api?mode=queue&output=json&apikey=' . $GLOBALS['sabnzbdToken'];
-		try {
-			$options = (localURL($url)) ? array('verify' => false) : array();
-			$response = Requests::get($url, array(), $options);
-			if ($response->success) {
-				$api['content']['queueItems'] = json_decode($response->body, true);
-			}
-		} catch (Requests_Exception $e) {
-			writeLog('error', 'SabNZBd Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
-		};
-		$url = qualifyURL($GLOBALS['sabnzbdURL']);
-		$url = $url . '/api?mode=history&output=json&limit=100&apikey=' . $GLOBALS['sabnzbdToken'];
-		try {
-			$options = (localURL($url)) ? array('verify' => false) : array();
-			$response = Requests::get($url, array(), $options);
-			if ($response->success) {
-				$api['content']['historyItems'] = json_decode($response->body, true);
-			}
-		} catch (Requests_Exception $e) {
-			writeLog('error', 'SabNZBd Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
-		};
-		$api['content'] = isset($api['content']) ? $api['content'] : false;
-		return $api;
-	}
-	return false;
-}
-
-function nzbgetConnect()
-{
-	if ($GLOBALS['homepageNzbgetEnabled'] && !empty($GLOBALS['nzbgetURL']) && qualifyRequest($GLOBALS['homepageNzbgetAuth'])) {
-		$url = qualifyURL($GLOBALS['nzbgetURL']);
-		$url = $url . '/jsonrpc/listgroups';
-		try {
-			$options = (localURL($url)) ? array('verify' => false) : array();
-			if ($GLOBALS['nzbgetUsername'] !== '' && decrypt($GLOBALS['nzbgetPassword']) !== '') {
-				$credentials = array('auth' => new Requests_Auth_Basic(array($GLOBALS['nzbgetUsername'], decrypt($GLOBALS['nzbgetPassword']))));
-				$options = array_merge($options, $credentials);
-			}
-			$response = Requests::get($url, array(), $options);
-			if ($response->success) {
-				$api['content']['queueItems'] = json_decode($response->body, true);
-			}
-		} catch (Requests_Exception $e) {
-			writeLog('error', 'NZBGet Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
-		};
-		$url = qualifyURL($GLOBALS['nzbgetURL']);
-		$url = $url . '/jsonrpc/history';
-		try {
-			$options = (localURL($url)) ? array('verify' => false) : array();
-			if ($GLOBALS['nzbgetUsername'] !== '' && decrypt($GLOBALS['nzbgetPassword']) !== '') {
-				$credentials = array('auth' => new Requests_Auth_Basic(array($GLOBALS['nzbgetUsername'], decrypt($GLOBALS['nzbgetPassword']))));
-				$options = array_merge($options, $credentials);
-			}
-			$response = Requests::get($url, array(), $options);
-			if ($response->success) {
-				$api['content']['historyItems'] = json_decode($response->body, true);
-			}
-		} catch (Requests_Exception $e) {
-			writeLog('error', 'NZBGet Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
-		};
-		$api['content'] = isset($api['content']) ? $api['content'] : false;
-		return $api;
-	}
-	return false;
-}
-
-function transmissionConnect()
-{
-	if ($GLOBALS['homepageTransmissionEnabled'] && !empty($GLOBALS['transmissionURL']) && qualifyRequest($GLOBALS['homepageTransmissionAuth'])) {
-		$digest = qualifyURL($GLOBALS['transmissionURL'], true);
-		$passwordInclude = ($GLOBALS['transmissionUsername'] != '' && $GLOBALS['transmissionPassword'] != '') ? $GLOBALS['transmissionUsername'] . ':' . decrypt($GLOBALS['transmissionPassword']) . "@" : '';
-		$url = $digest['scheme'] . '://' . $passwordInclude . $digest['host'] . $digest['port'] . $digest['path'] . '/rpc';
-		try {
-			$options = (localURL($GLOBALS['transmissionURL'])) ? array('verify' => false) : array();
-			$response = Requests::get($url, array(), $options);
-			if ($response->headers['x-transmission-session-id']) {
-				$headers = array(
-					'X-Transmission-Session-Id' => $response->headers['x-transmission-session-id'],
-					'Content-Type' => 'application/json'
-				);
-				$data = array(
-					'method' => 'torrent-get',
-					'arguments' => array(
-						'fields' => array(
-							"id", "name", "totalSize", "eta", "isFinished", "isStalled", "percentDone", "rateDownload", "status", "downloadDir", "errorString"
-						),
-					),
-					'tags' => ''
-				);
-				$response = Requests::post($url, $headers, json_encode($data), $options);
-				if ($response->success) {
-					$torrentList = json_decode($response->body, true)['arguments']['torrents'];
-					if ($GLOBALS['transmissionHideSeeding'] || $GLOBALS['transmissionHideCompleted']) {
-						$filter = array();
-						$torrents['arguments']['torrents'] = array();
-						if ($GLOBALS['transmissionHideSeeding']) {
-							array_push($filter, 6, 5);
-						}
-						if ($GLOBALS['transmissionHideCompleted']) {
-							array_push($filter, 0);
-						}
-						foreach ($torrentList as $key => $value) {
-							if (!in_array($value['status'], $filter)) {
-								$torrents['arguments']['torrents'][] = $value;
-							}
-						}
-					} else {
-						$torrents = json_decode($response->body, true);
-					}
-					$api['content']['queueItems'] = $torrents;
-					$api['content']['historyItems'] = false;
-				}
-			} else {
-				writeLog('error', 'Transmission Connect Function - Error: Could not get session ID', 'SYSTEM');
-			}
-		} catch (Requests_Exception $e) {
-			writeLog('error', 'Transmission Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
-		};
-		$api['content'] = isset($api['content']) ? $api['content'] : false;
-		return $api;
-	}
-	return false;
-}
-
-function rTorrentStatus($completed, $state, $status)
-{
-	if ($completed && $state && $status == 'seed') {
-		$state = 'Seeding';
-	} elseif (!$completed && !$state && $status == 'leech') {
-		$state = 'Stopped';
-	} elseif (!$completed && $state && $status == 'leech') {
-		$state = 'Downloading';
-	} elseif ($completed && !$state && $status == 'seed') {
-		$state = 'Finished';
-	}
-	return ($state) ? $state : $status;
-}
-
-function rTorrentConnect()
-{
-	if ($GLOBALS['homepagerTorrentEnabled'] && (!empty($GLOBALS['rTorrentURL']) || !empty($GLOBALS['rTorrentURLOverride'])) && qualifyRequest($GLOBALS['homepagerTorrentAuth'])) {
-		try {
-			if ($GLOBALS['rTorrentLimit'] == '0') {
-				$GLOBALS['rTorrentLimit'] = '1000';
-			}
-			$torrents = array();
-			$digest = (empty($GLOBALS['rTorrentURLOverride'])) ? qualifyURL($GLOBALS['rTorrentURL'], true) : qualifyURL(checkOverrideURL($GLOBALS['rTorrentURL'], $GLOBALS['rTorrentURLOverride']), true);
-			$passwordInclude = ($GLOBALS['rTorrentUsername'] !== '' && $GLOBALS['rTorrentPassword'] !== '') ? $GLOBALS['rTorrentUsername'] . ':' . decrypt($GLOBALS['rTorrentPassword']) . "@" : '';
-			$extraPath = (strpos($GLOBALS['rTorrentURL'], '.php') !== false) ? '' : '/RPC2';
-			$extraPath = (empty($GLOBALS['rTorrentURLOverride'])) ? $extraPath : '';
-			$url = $digest['scheme'] . '://' . $passwordInclude . $digest['host'] . $digest['port'] . $digest['path'] . $extraPath;
-			$options = (localURL($url, $GLOBALS['rTorrentDisableCertCheck'])) ? array('verify' => false) : array();
-			if ($GLOBALS['rTorrentUsername'] !== '' && decrypt($GLOBALS['rTorrentPassword']) !== '') {
-				$credentials = array('auth' => new Requests_Auth_Digest(array($GLOBALS['rTorrentUsername'], decrypt($GLOBALS['rTorrentPassword']))));
-				$options = array_merge($options, $credentials);
-			}
-			$data = xmlrpc_encode_request("d.multicall2", array(
-				"",
-				"main",
-				"d.name=",
-				"d.base_path=",
-				"d.up.total=",
-				"d.size_bytes=",
-				"d.down.total=",
-				"d.completed_bytes=",
-				"d.connection_current=",
-				"d.down.rate=",
-				"d.up.rate=",
-				"d.timestamp.started=",
-				"d.state=",
-				"d.group.name=",
-				"d.hash=",
-				"d.complete=",
-				"d.ratio=",
-				"d.chunk_size=",
-				"f.size_bytes=",
-				"f.size_chunks=",
-				"f.completed_chunks=",
-				"d.custom=",
-				"d.custom1=",
-				"d.custom2=",
-				"d.custom3=",
-				"d.custom4=",
-				"d.custom5=",
-			), array());
-			$response = Requests::post($url, array(), $data, $options);
-			if ($response->success) {
-				$torrentList = xmlrpc_decode(str_replace('i8>', 'string>', $response->body));
-				foreach ($torrentList as $key => $value) {
-					$tempStatus = rTorrentStatus($value[13], $value[10], $value[6]);
-					if ($tempStatus == 'Seeding' && $GLOBALS['rTorrentHideSeeding']) {
-						//do nothing
-					} elseif ($tempStatus == 'Finished' && $GLOBALS['rTorrentHideCompleted']) {
-						//do nothing
-					} else {
-						$torrents[$key] = array(
-							'name' => $value[0],
-							'base' => $value[1],
-							'upTotal' => $value[2],
-							'size' => $value[3],
-							'downTotal' => $value[4],
-							'downloaded' => $value[5],
-							'connectionState' => $value[6],
-							'leech' => $value[7],
-							'seed' => $value[8],
-							'date' => $value[9],
-							'state' => ($value[10]) ? 'on' : 'off',
-							'group' => $value[11],
-							'hash' => $value[12],
-							'complete' => ($value[13]) ? 'yes' : 'no',
-							'ratio' => $value[14],
-							'label' => $value[20],
-							'status' => $tempStatus,
-							'temp' => $value[16] . ' - ' . $value[17] . ' - ' . $value[18],
-							'custom' => $value[19] . ' - ' . $value[20] . ' - ' . $value[21],
-							'custom2' => $value[22] . ' - ' . $value[23] . ' - ' . $value[24],
-						);
-					}
-				}
-				if (count($torrents) !== 0) {
-					usort($torrents, function ($a, $b) {
-						$direction = substr($GLOBALS['rTorrentSortOrder'], -1);
-						$sort = substr($GLOBALS['rTorrentSortOrder'], 0, strlen($GLOBALS['rTorrentSortOrder']) - 1);
-						switch ($direction) {
-							case 'a':
-								return $a[$sort] <=> $b[$sort];
-								break;
-							case 'd':
-								return $b[$sort] <=> $a[$sort];
-								break;
-							default:
-								return $b['date'] <=> $a['date'];
-						}
-					});
-					$torrents = array_slice($torrents, 0, $GLOBALS['rTorrentLimit']);
-				}
-				$api['content']['queueItems'] = $torrents;
-				$api['content']['historyItems'] = false;
-			}
-		} catch
-		(Requests_Exception $e) {
-			writeLog('error', 'rTorrent Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
-		};
-		$api['content'] = isset($api['content']) ? $api['content'] : false;
-		return $api;
-	}
-	return false;
-}
-
-function qBittorrentConnect()
-{
-	if ($GLOBALS['homepageqBittorrentEnabled'] && !empty($GLOBALS['qBittorrentURL']) && qualifyRequest($GLOBALS['homepageqBittorrentAuth'])) {
-		$digest = qualifyURL($GLOBALS['qBittorrentURL'], true);
-		$data = array('username' => $GLOBALS['qBittorrentUsername'], 'password' => decrypt($GLOBALS['qBittorrentPassword']));
-		$apiVersionLogin = ($GLOBALS['qBittorrentApiVersion'] == '1') ? '/login' : '/api/v2/auth/login';
-		$apiVersionQuery = ($GLOBALS['qBittorrentApiVersion'] == '1') ? '/query/torrents?sort=' : '/api/v2/torrents/info?sort=';
-		$url = $digest['scheme'] . '://' . $digest['host'] . $digest['port'] . $digest['path'] . $apiVersionLogin;
-		try {
-			$options = (localURL($GLOBALS['qBittorrentURL'])) ? array('verify' => false) : array();
-			$response = Requests::post($url, array(), $data, $options);
-			$reflection = new ReflectionClass($response->cookies);
-			$cookie = $reflection->getProperty("cookies");
-			$cookie->setAccessible(true);
-			$cookie = $cookie->getValue($response->cookies);
-			if ($cookie) {
-				$headers = array(
-					'Cookie' => 'SID=' . $cookie['SID']->value
-				);
-				$reverse = $GLOBALS['qBittorrentReverseSorting'] ? 'true' : 'false';
-				$url = $digest['scheme'] . '://' . $digest['host'] . $digest['port'] . $digest['path'] . $apiVersionQuery . $GLOBALS['qBittorrentSortOrder'] . '&reverse=' . $reverse;
-				$response = Requests::get($url, $headers, $options);
-				if ($response) {
-					$torrentList = json_decode($response->body, true);
-					if ($GLOBALS['qBittorrentHideSeeding'] || $GLOBALS['qBittorrentHideCompleted']) {
-						$filter = array();
-						$torrents['arguments']['torrents'] = array();
-						if ($GLOBALS['qBittorrentHideSeeding']) {
-							array_push($filter, 'uploading', 'stalledUP', 'queuedUP');
-						}
-						if ($GLOBALS['qBittorrentHideCompleted']) {
-							array_push($filter, 'pausedUP');
-						}
-						foreach ($torrentList as $key => $value) {
-							if (!in_array($value['state'], $filter)) {
-								$torrents['arguments']['torrents'][] = $value;
-							}
-						}
-					} else {
-						$torrents['arguments']['torrents'] = json_decode($response->body, true);
-					}
-					$api['content']['queueItems'] = $torrents;
-					$api['content']['historyItems'] = false;
-				}
-			} else {
-				writeLog('error', 'qBittorrent Connect Function - Error: Could not get session ID', 'SYSTEM');
-			}
-		} catch (Requests_Exception $e) {
-			writeLog('error', 'qBittorrent Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
-		};
-		$api['content'] = isset($api['content']) ? $api['content'] : false;
-		return $api;
-	}
-	return false;
-}
-
-function delugeStatus($queued, $status, $state)
-{
-	if ($queued == '-1' && $state == '100' && ($status == 'Seeding' || $status == 'Queued' || $status == 'Paused')) {
-		$state = 'Seeding';
-	} elseif ($state !== '100') {
-		$state = 'Downloading';
-	} else {
-		$state = 'Finished';
-	}
-	return ($state) ? $state : $status;
-}
-
-function delugeConnect()
-{
-	if ($GLOBALS['homepageDelugeEnabled'] && !empty($GLOBALS['delugeURL']) && !empty($GLOBALS['delugePassword']) && qualifyRequest($GLOBALS['homepageDelugeAuth'])) {
-		try {
-			$deluge = new deluge($GLOBALS['delugeURL'], decrypt($GLOBALS['delugePassword']));
-			$torrents = $deluge->getTorrents(null, 'comment, download_payload_rate, eta, hash, is_finished, is_seed, message, name, paused, progress, queue, state, total_size, upload_payload_rate');
-			foreach ($torrents as $key => $value) {
-				$tempStatus = delugeStatus($value->queue, $value->state, $value->progress);
-				if ($tempStatus == 'Seeding' && $GLOBALS['delugeHideSeeding']) {
-					//do nothing
-				} elseif ($tempStatus == 'Finished' && $GLOBALS['delugeHideCompleted']) {
-					//do nothing
-				} else {
-					$api['content']['queueItems'][] = $value;
-				}
-			}
-			$api['content']['queueItems'] = (empty($api['content']['queueItems'])) ? [] : $api['content']['queueItems'];
-			$api['content']['historyItems'] = false;
-		} catch (Excecption $e) {
-			writeLog('error', 'Deluge Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
-		}
-	}
-	$api['content'] = isset($api['content']) ? $api['content'] : false;
-	return $api;
-}
-
-function sonarrConnectQueue()
-{
-	$sonarrQueueItems = array();
-	if ($GLOBALS['homepageSonarrEnabled'] && qualifyRequest($GLOBALS['homepageSonarrAuth']) && !empty($GLOBALS['sonarrURL']) && !empty($GLOBALS['sonarrToken'])) {
-		$sonarrs = array();
-		$sonarrURLList = explode(',', $GLOBALS['sonarrURL']);
-		$sonarrTokenList = explode(',', $GLOBALS['sonarrToken']);
-		if (count($sonarrURLList) == count($sonarrTokenList)) {
-			foreach ($sonarrURLList as $key => $value) {
-				$sonarrs[$key] = array(
-					'url' => $value,
-					'token' => $sonarrTokenList[$key]
-				);
-			}
-			foreach ($sonarrs as $key => $value) {
-				try {
-					$sonarr = new Kryptonit3\Sonarr\Sonarr($value['url'], $value['token']);
-					$sonarr = $sonarr->getQueue();
-					$downloadList = json_decode($sonarr, true);
-					if (is_array($downloadList) || is_object($downloadList)) {
-						$sonarrQueue = (array_key_exists('error', $downloadList)) ? '' : $downloadList;
-					} else {
-						$sonarrQueue = '';
-					}
-					if (!empty($sonarrQueue)) {
-						$sonarrQueueItems = array_merge($sonarrQueueItems, $sonarrQueue);
-					}
-				} catch (Exception $e) {
-					writeLog('error', 'Sonarr Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
-				}
-			}
-			$api['content']['queueItems'] = $sonarrQueueItems;
-			$api['content']['historyItems'] = false;
-			$api['content'] = isset($api['content']) ? $api['content'] : false;
-			return $api;
-		}
-	}
-	return false;
-}
-
-function radarrConnectQueue()
-{
-	$radarrQueueItems = array();
-	if ($GLOBALS['homepageRadarrEnabled'] && qualifyRequest($GLOBALS['homepageRadarrAuth']) && !empty($GLOBALS['radarrURL']) && !empty($GLOBALS['radarrToken'])) {
-		$radarrs = array();
-		$radarrURLList = explode(',', $GLOBALS['radarrURL']);
-		$radarrTokenList = explode(',', $GLOBALS['radarrToken']);
-		if (count($radarrURLList) == count($radarrTokenList)) {
-			foreach ($radarrURLList as $key => $value) {
-				$radarrs[$key] = array(
-					'url' => $value,
-					'token' => $radarrTokenList[$key]
-				);
-			}
-			foreach ($radarrs as $key => $value) {
-				try {
-					$radarr = new Kryptonit3\Sonarr\Sonarr($value['url'], $value['token']);
-					$radarr = $radarr->getQueue();
-					$downloadList = json_decode($radarr, true);
-					if (is_array($downloadList) || is_object($downloadList)) {
-						$radarrQueue = (array_key_exists('error', $downloadList)) ? '' : $downloadList;
-					} else {
-						$radarrQueue = '';
-					}
-					if (!empty($radarrQueue)) {
-						$radarrQueueItems = array_merge($radarrQueueItems, $radarrQueue);
-					}
-				} catch (Exception $e) {
-					writeLog('error', 'Radarr Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
-				}
-			}
-			$api['content']['queueItems'] = $radarrQueueItems;
-			$api['content']['historyItems'] = false;
-			$api['content'] = isset($api['content']) ? $api['content'] : false;
-			return $api;
-		}
-	}
-	return false;
-}
-
-function getCalendar()
-{
-	$startDate = date('Y-m-d', strtotime("-" . $GLOBALS['calendarStart'] . " days"));
-	$endDate = date('Y-m-d', strtotime("+" . $GLOBALS['calendarEnd'] . " days"));
-	$icalCalendarSources = array();
-	$calendarItems = array();
-	// SONARR CONNECT
-	if ($GLOBALS['homepageSonarrEnabled'] && qualifyRequest($GLOBALS['homepageSonarrAuth']) && !empty($GLOBALS['sonarrURL']) && !empty($GLOBALS['sonarrToken'])) {
-		$sonarrs = array();
-		$sonarrURLList = explode(',', $GLOBALS['sonarrURL']);
-		$sonarrTokenList = explode(',', $GLOBALS['sonarrToken']);
-		if (count($sonarrURLList) == count($sonarrTokenList)) {
-			foreach ($sonarrURLList as $key => $value) {
-				$sonarrs[$key] = array(
-					'url' => $value,
-					'token' => $sonarrTokenList[$key]
-				);
-			}
-			foreach ($sonarrs as $key => $value) {
-				try {
-					$sonarr = new Kryptonit3\Sonarr\Sonarr($value['url'], $value['token']);
-					$sonarr = $sonarr->getCalendar($startDate, $endDate, $GLOBALS['sonarrUnmonitored']);
-					$result = json_decode($sonarr, true);
-					if (is_array($result) || is_object($result)) {
-						$sonarrCalendar = (array_key_exists('error', $result)) ? '' : getSonarrCalendar($sonarr, $key);
-					} else {
-						$sonarrCalendar = '';
-					}
-				} catch (Exception $e) {
-					writeLog('error', 'Sonarr Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
-				}
-				if (!empty($sonarrCalendar)) {
-					$calendarItems = array_merge($calendarItems, $sonarrCalendar);
-				}
-			}
-		}
-	}
-	// LIDARR CONNECT
-	if ($GLOBALS['homepageLidarrEnabled'] && qualifyRequest($GLOBALS['homepageLidarrAuth']) && !empty($GLOBALS['lidarrURL']) && !empty($GLOBALS['lidarrToken'])) {
-		$lidarrs = array();
-		$lidarrURLList = explode(',', $GLOBALS['lidarrURL']);
-		$lidarrTokenList = explode(',', $GLOBALS['lidarrToken']);
-		if (count($lidarrURLList) == count($lidarrTokenList)) {
-			foreach ($lidarrURLList as $key => $value) {
-				$lidarrs[$key] = array(
-					'url' => $value,
-					'token' => $lidarrTokenList[$key]
-				);
-			}
-			foreach ($lidarrs as $key => $value) {
-				try {
-					$lidarr = new Kryptonit3\Sonarr\Sonarr($value['url'], $value['token'], true);
-					$lidarr = $lidarr->getCalendar($startDate, $endDate);
-					$result = json_decode($lidarr, true);
-					if (is_array($result) || is_object($result)) {
-						$lidarrCalendar = (array_key_exists('error', $result)) ? '' : getLidarrCalendar($lidarr, $key);
-					} else {
-						$lidarrCalendar = '';
-					}
-				} catch (Exception $e) {
-					writeLog('error', 'Lidarr Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
-				}
-				if (!empty($lidarrCalendar)) {
-					$calendarItems = array_merge($calendarItems, $lidarrCalendar);
-				}
-			}
-		}
-	}
-	// RADARR CONNECT
-	if ($GLOBALS['homepageRadarrEnabled'] && qualifyRequest($GLOBALS['homepageRadarrAuth']) && !empty($GLOBALS['radarrURL']) && !empty($GLOBALS['radarrToken'])) {
-		$radarrs = array();
-		$radarrURLList = explode(',', $GLOBALS['radarrURL']);
-		$radarrTokenList = explode(',', $GLOBALS['radarrToken']);
-		if (count($radarrURLList) == count($radarrTokenList)) {
-			foreach ($radarrURLList as $key => $value) {
-				$radarrs[$key] = array(
-					'url' => $value,
-					'token' => $radarrTokenList[$key]
-				);
-			}
-			foreach ($radarrs as $key => $value) {
-				try {
-					$radarr = new Kryptonit3\Sonarr\Sonarr($value['url'], $value['token']);
-					$radarr = $radarr->getCalendar($startDate, $endDate);
-					$result = json_decode($radarr, true);
-					if (is_array($result) || is_object($result)) {
-						$radarrCalendar = (array_key_exists('error', $result)) ? '' : getRadarrCalendar($radarr, $key, $value['url']);
-					} else {
-						$radarrCalendar = '';
-					}
-				} catch (Exception $e) {
-					writeLog('error', 'Radarr Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
-				}
-				if (!empty($radarrCalendar)) {
-					$calendarItems = array_merge($calendarItems, $radarrCalendar);
-				}
-			}
-		}
-	}
-	// SICKRAGE/BEARD/MEDUSA CONNECT
-	if ($GLOBALS['homepageSickrageEnabled'] && qualifyRequest($GLOBALS['homepageSickrageAuth']) && !empty($GLOBALS['sickrageURL']) && !empty($GLOBALS['sickrageToken'])) {
-		$sicks = array();
-		$sickURLList = explode(',', $GLOBALS['sickrageURL']);
-		$sickTokenList = explode(',', $GLOBALS['sickrageToken']);
-		if (count($sickURLList) == count($sickTokenList)) {
-			foreach ($sickURLList as $key => $value) {
-				$sicks[$key] = array(
-					'url' => $value,
-					'token' => $sickTokenList[$key]
-				);
-			}
-			foreach ($sicks as $key => $value) {
-				try {
-					$sickrage = new Kryptonit3\SickRage\SickRage($value['url'], $value['token']);
-					$sickrageFuture = getSickrageCalendarWanted($sickrage->future(), $key);
-					$sickrageHistory = getSickrageCalendarHistory($sickrage->history("100", "downloaded"), $key);
-					if (!empty($sickrageFuture)) {
-						$calendarItems = array_merge($calendarItems, $sickrageFuture);
-					}
-					if (!empty($sickrageHistory)) {
-						$calendarItems = array_merge($calendarItems, $sickrageHistory);
-					}
-				} catch (Exception $e) {
-					writeLog('error', 'Sickrage Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
-				}
-			}
-		}
-	}
-	// COUCHPOTATO CONNECT
-	if ($GLOBALS['homepageCouchpotatoEnabled'] && qualifyRequest($GLOBALS['homepageCouchpotatoAuth']) && !empty($GLOBALS['couchpotatoURL']) && !empty($GLOBALS['couchpotatoToken'])) {
-		$couchs = array();
-		$couchpotatoURLList = explode(',', $GLOBALS['couchpotatoURL']);
-		$couchpotatoTokenList = explode(',', $GLOBALS['couchpotatoToken']);
-		if (count($couchpotatoURLList) == count($couchpotatoTokenList)) {
-			foreach ($couchpotatoURLList as $key => $value) {
-				$couchs[$key] = array(
-					'url' => $value,
-					'token' => $couchpotatoTokenList[$key]
-				);
-			}
-			foreach ($couchs as $key => $value) {
-				try {
-					$couchpotato = new Kryptonit3\CouchPotato\CouchPotato($value['url'], $value['token']);
-					$couchCalendar = getCouchCalendar($couchpotato->getMediaList(), $key);
-					if (!empty($couchCalendar)) {
-						$calendarItems = array_merge($calendarItems, $couchCalendar);
-					}
-				} catch (Exception $e) {
-					writeLog('error', 'Sickrage Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
-				}
-			}
-		}
-	}
-	// iCal URL
-	if ($GLOBALS['homepageCalendarEnabled'] && qualifyRequest($GLOBALS['homepageCalendarAuth']) && !empty($GLOBALS['calendariCal'])) {
-		$calendars = array();
-		$calendarURLList = explode(',', $GLOBALS['calendariCal']);
-		$icalEvents = array();
-		foreach ($calendarURLList as $key => $value) {
-			$icsEvents = getIcsEventsAsArray($value);
-			if (isset($icsEvents) && !empty($icsEvents)) {
-				$timeZone = isset($icsEvents [1] ['X-WR-TIMEZONE']) ? trim($icsEvents[1]['X-WR-TIMEZONE']) : date_default_timezone_get();
-				$originalTimeZone = isset($icsEvents [1] ['X-WR-TIMEZONE']) ? str_replace('"', '', trim($icsEvents[1]['X-WR-TIMEZONE'])) : false;
-				unset($icsEvents [1]);
-				foreach ($icsEvents as $icsEvent) {
-					$startKeys = array_filter_key($icsEvent, function ($key) {
-						return strpos($key, 'DTSTART') === 0;
-					});
-					$endKeys = array_filter_key($icsEvent, function ($key) {
-						return strpos($key, 'DTEND') === 0;
-					});
-					if (!empty($startKeys) && !empty($endKeys) && isset($icsEvent['SUMMARY'])) {
-						/* Getting start date and time */
-						$repeat = isset($icsEvent ['RRULE']) ? $icsEvent ['RRULE'] : false;
-						if (!$originalTimeZone) {
-							$tzKey = array_keys($startKeys);
-							if (strpos($tzKey[0], 'TZID=') !== false) {
-								$originalTimeZone = explode('TZID=', (string)$tzKey[0]);
-								$originalTimeZone = (count($originalTimeZone) >= 2) ? str_replace('"', '', $originalTimeZone[1]) : false;
-							}
-						}
-						$start = reset($startKeys);
-						$end = reset($endKeys);
-						$totalDays = $GLOBALS['calendarStart'] + $GLOBALS['calendarEnd'];
-						if ($repeat) {
-							$repeatOverride = getCalenderRepeatCount(trim($icsEvent["RRULE"]));
-							switch (trim(strtolower(getCalenderRepeat($repeat)))) {
-								case 'daily':
-									$repeat = ($repeatOverride) ? $repeatOverride : $totalDays;
-									$term = 'days';
-									break;
-								case 'weekly':
-									$repeat = ($repeatOverride) ? $repeatOverride : round($totalDays / 7);
-									$term = 'weeks';
-									break;
-								case 'monthly':
-									$repeat = ($repeatOverride) ? $repeatOverride : round($totalDays / 30);
-									$term = 'months';
-									break;
-								case 'yearly':
-									$repeat = ($repeatOverride) ? $repeatOverride : round($totalDays / 365);
-									$term = 'years';
-									break;
-								default:
-									$repeat = ($repeatOverride) ? $repeatOverride : $totalDays;
-									$term = 'days';
-									break;
-							}
-						} else {
-							$repeat = 1;
-							$term = 'day';
-						}
-						$calendarTimes = 0;
-						while ($calendarTimes < $repeat) {
-							$currentDate = new DateTime ($GLOBALS['currentTime']);
-							$oldestDay = new DateTime ($GLOBALS['currentTime']);
-							$oldestDay->modify('-' . $GLOBALS['calendarStart'] . ' days');
-							$newestDay = new DateTime ($GLOBALS['currentTime']);
-							$newestDay->modify('+' . $GLOBALS['calendarEnd'] . ' days');
-							/* Converting to datetime and apply the timezone to get proper date time */
-							$startDt = new DateTime ($start);
-							/* Getting end date with time */
-							$endDt = new DateTime ($end);
-							if ($calendarTimes !== 0) {
-								$dateDiff = date_diff($startDt, $currentDate);
-								$startDt->modify($dateDiff->format('%R') . (round(($dateDiff->days) / 7)) . ' weeks');
-								$startDt->modify('+' . $calendarTimes . ' ' . $term);
-								$endDt->modify($dateDiff->format('%R') . (round(($dateDiff->days) / 7)) . ' weeks');
-								$endDt->modify('+' . $calendarTimes . ' ' . $term);
-							} elseif ($calendarTimes == 0 && $repeat !== 1) {
-								$dateDiff = date_diff($startDt, $currentDate);
-								$startDt->modify($dateDiff->format('%R') . (round(($dateDiff->days) / 7)) . ' weeks');
-								$endDt->modify($dateDiff->format('%R') . (round(($dateDiff->days) / 7)) . ' weeks');
-							}
-							$calendarStartDiff = date_diff($startDt, $newestDay);
-							$calendarEndDiff = date_diff($startDt, $oldestDay);
-							if ($originalTimeZone && $originalTimeZone !== 'UTC' && (strpos($start, 'Z') == false)) {
-								$originalTimeZone = calendarStandardizeTimezone($originalTimeZone);
-								$dateTimeOriginalTZ = new DateTimeZone($originalTimeZone);
-								$dateTimeOriginal = new DateTime('now', $dateTimeOriginalTZ);
-								$dateTimeUTCTZ = new DateTimeZone(date_default_timezone_get());
-								$dateTimeUTC = new DateTime('now', $dateTimeUTCTZ);
-								$dateTimeOriginalOffset = $dateTimeOriginal->getOffset() / 3600;
-								$dateTimeUTCOffset = $dateTimeUTC->getOffset() / 3600;
-								$diff = $dateTimeUTCOffset - $dateTimeOriginalOffset;
-								$startDt->modify('+ ' . $diff . ' hour');
-								$endDt->modify('+ ' . $diff . ' hour');
-							}
-							$startDt->setTimeZone(new DateTimezone ($timeZone));
-							$endDt->setTimeZone(new DateTimezone ($timeZone));
-							$startDate = $startDt->format(DateTime::ATOM);
-							$endDate = $endDt->format(DateTime::ATOM);
-							if (new DateTime() < $endDt) {
-								$extraClass = 'text-info';
-							} else {
-								$extraClass = 'text-success';
-							}
-							/* Getting the name of event */
-							$eventName = $icsEvent['SUMMARY'];
-							if (!calendarDaysCheck($calendarStartDiff->format('%R') . $calendarStartDiff->days, $calendarEndDiff->format('%R') . $calendarEndDiff->days)) {
-								break;
-							}
-							if (isset($icsEvent["RRULE"]) && getCalenderRepeatUntil(trim($icsEvent["RRULE"]))) {
-								$untilDate = new DateTime (getCalenderRepeatUntil(trim($icsEvent["RRULE"])));
-								$untilDiff = date_diff($currentDate, $untilDate);
-								if ($untilDiff->days > 0) {
-									break;
-								}
-							}
-							$icalEvents[] = array(
-								'title' => $eventName,
-								'imagetype' => 'calendar-o text-warning text-custom-calendar ' . $extraClass,
-								'imagetypeFilter' => 'ical',
-								'className' => 'bg-calendar calendar-item bg-custom-calendar',
-								'start' => $startDate,
-								'end' => $endDate,
-								'bgColor' => str_replace('text', 'bg', $extraClass),
-							);
-							$calendarTimes = $calendarTimes + 1;
-						}
-					}
-				}
-			}
-		}
-		$calendarSources['ical'] = $icalEvents;
-	}
-	$calendarSources['events'] = $calendarItems;
-	return ($calendarSources) ? $calendarSources : false;
-}
-
-function calendarDaysCheck($entryStart, $entryEnd)
-{
-	$success = false;
-	$entryStart = intval($entryStart);
-	$entryEnd = intval($entryEnd);
-	if ($entryStart >= 0 && $entryEnd <= 0) {
-		$success = true;
-	}
-	return $success;
-}
-
-function calendarStandardizeTimezone($timezone)
-{
-	switch ($timezone) {
-		case('CST'):
-		case('Central Time'):
-		case('Central Standard Time'):
-			$timezone = 'America/Chicago';
-			break;
-		case('CET'):
-		case('Central European Time'):
-			$timezone = 'Europe/Berlin';
-			break;
-		case('EST'):
-		case('Eastern Time'):
-		case('Eastern Standard Time'):
-			$timezone = 'America/New_York';
-			break;
-		case('PST'):
-		case('Pacific Time'):
-		case('Pacific Standard Time'):
-			$timezone = 'America/Los_Angeles';
-			break;
-		case('China Time'):
-		case('China Standard Time'):
-			$timezone = 'Asia/Beijing';
-			break;
-		case('IST'):
-		case('India Time'):
-		case('India Standard Time'):
-			$timezone = 'Asia/New_Delhi';
-			break;
-		case('JST');
-		case('Japan Time'):
-		case('Japan Standard Time'):
-			$timezone = 'Asia/Tokyo';
-			break;
-	}
-	return $timezone;
-}
-
-function getCalenderRepeat($value)
-{
-	//FREQ=DAILY
-	//RRULE:FREQ=WEEKLY;BYDAY=TH
-	$first = explode('=', $value);
-	if (count($first) > 1) {
-		$second = explode(';', $first[1]);
-	} else {
-		return $value;
-	}
-	if ($second) {
-		return $second[0];
-	} else {
-		return $first[1];
-	}
-}
-
-function getCalenderRepeatUntil($value)
-{
-	$first = explode('UNTIL=', $value);
-	if (count($first) > 1) {
-		if (strpos($first[1], ';') !== false) {
-			$check = explode(';', $first[1]);
-			return $check[0];
-		} else {
-			return $first[1];
-		}
-	} else {
-		return false;
-	}
-}
-
-function getCalenderRepeatCount($value)
-{
-	$first = explode('COUNT=', $value);
-	if (count($first) > 1) {
-		return $first[1];
-	} else {
-		return false;
-	}
-}
-
-function getSonarrCalendar($array, $number)
-{
-	$array = json_decode($array, true);
-	$gotCalendar = array();
-	$i = 0;
-	foreach ($array as $child) {
-		$i++;
-		$seriesName = $child['series']['title'];
-		$seriesID = $child['series']['tvdbId'];
-		$episodeID = $child['series']['tvdbId'];
-		$monitored = $child['monitored'];
-		if (!isset($episodeID)) {
-			$episodeID = "";
-		}
-		//$episodeName = htmlentities($child['title'], ENT_QUOTES);
-		$episodeAirDate = $child['airDateUtc'];
-		$episodeAirDate = strtotime($episodeAirDate);
-		$episodeAirDate = date("Y-m-d H:i:s", $episodeAirDate);
-		if (new DateTime() < new DateTime($episodeAirDate)) {
-			$unaired = true;
-		}
-		if ($child['episodeNumber'] == "1") {
-			$episodePremier = "true";
-		} else {
-			$episodePremier = "false";
-			$date = new DateTime($episodeAirDate);
-			$date->add(new DateInterval("PT1S"));
-			$date->format(DateTime::ATOM);
-			$child['airDateUtc'] = gmdate('Y-m-d\TH:i:s\Z', strtotime($date->format(DateTime::ATOM)));
-		}
-		$downloaded = $child['hasFile'];
-		if ($downloaded == "0" && isset($unaired) && $episodePremier == "true") {
-			$downloaded = "text-primary animated flash";
-		} elseif ($downloaded == "0" && isset($unaired) && $monitored == "0") {
-			$downloaded = "text-dark";
-		} elseif ($downloaded == "0" && isset($unaired)) {
-			$downloaded = "text-info";
-		} elseif ($downloaded == "1") {
-			$downloaded = "text-success";
-		} else {
-			$downloaded = "text-danger";
-		}
-		$fanart = "/plugins/images/cache/no-np.png";
-		foreach ($child['series']['images'] as $image) {
-			if ($image['coverType'] == "fanart") {
-				$fanart = $image['url'];
-			}
-		}
-		if ($fanart !== "/plugins/images/cache/no-np.png" || (strpos($fanart, '://') === false)) {
-			$cacheDirectory = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR;
-			$imageURL = $fanart;
-			$cacheFile = $cacheDirectory . $seriesID . '.jpg';
-			$fanart = 'plugins/images/cache/' . $seriesID . '.jpg';
-			if (!file_exists($cacheFile)) {
-				cacheImage($imageURL, $seriesID);
-				unset($imageURL);
-				unset($cacheFile);
-			}
-		}
-		$bottomTitle = 'S' . sprintf("%02d", $child['seasonNumber']) . 'E' . sprintf("%02d", $child['episodeNumber']) . ' - ' . $child['title'];
-		$details = array(
-			"seasonCount" => $child['series']['seasonCount'],
-			"status" => $child['series']['status'],
-			"topTitle" => $seriesName,
-			"bottomTitle" => $bottomTitle,
-			"overview" => isset($child['overview']) ? $child['overview'] : '',
-			"runtime" => $child['series']['runtime'],
-			"image" => $fanart,
-			"ratings" => $child['series']['ratings']['value'],
-			"videoQuality" => $child["hasFile"] && isset($child['episodeFile']['quality']['quality']['name']) ? $child['episodeFile']['quality']['quality']['name'] : "unknown",
-			"audioChannels" => $child["hasFile"] && isset($child['episodeFile']['mediaInfo']) ? $child['episodeFile']['mediaInfo']['audioChannels'] : "unknown",
-			"audioCodec" => $child["hasFile"] && isset($child['episodeFile']['mediaInfo']) ? $child['episodeFile']['mediaInfo']['audioCodec'] : "unknown",
-			"videoCodec" => $child["hasFile"] && isset($child['episodeFile']['mediaInfo']) ? $child['episodeFile']['mediaInfo']['videoCodec'] : "unknown",
-			"size" => $child["hasFile"] && isset($child['episodeFile']['size']) ? $child['episodeFile']['size'] : "unknown",
-			"genres" => $child['series']['genres'],
-		);
-		array_push($gotCalendar, array(
-			"id" => "Sonarr-" . $number . "-" . $i,
-			"title" => $seriesName,
-			"start" => $child['airDateUtc'],
-			"className" => "inline-popups bg-calendar calendar-item tvID--" . $episodeID,
-			"imagetype" => "tv " . $downloaded,
-			"imagetypeFilter" => "tv",
-			"downloadFilter" => $downloaded,
-			"bgColor" => str_replace('text', 'bg', $downloaded),
-			"details" => $details
-		));
-	}
-	if ($i != 0) {
-		return $gotCalendar;
-	}
-	return false;
-}
-
-function getLidarrCalendar($array, $number)
-{
-	$array = json_decode($array, true);
-	$gotCalendar = array();
-	$i = 0;
-	foreach ($array as $child) {
-		$i++;
-		$albumName = $child['title'];
-		$artistName = $child['artist']['artistName'];
-		$albumID = '';
-		$releaseDate = $child['releaseDate'];
-		$releaseDate = strtotime($releaseDate);
-		$releaseDate = date("Y-m-d H:i:s", $releaseDate);
-		if (new DateTime() < new DateTime($releaseDate)) {
-			$unaired = true;
-		}
-		if (isset($child['statistics']['percentOfTracks'])) {
-			if ($child['statistics']['percentOfTracks'] == '100.0') {
-				$downloaded = '1';
-			} else {
-				$downloaded = '0';
-			}
-		} else {
-			$downloaded = '0';
-		}
-		if ($downloaded == "0" && isset($unaired)) {
-			$downloaded = "text-info";
-		} elseif ($downloaded == "1") {
-			$downloaded = "text-success";
-		} else {
-			$downloaded = "text-danger";
-		}
-		$fanart = "/plugins/images/cache/no-np.png";
-		foreach ($child['artist']['images'] as $image) {
-			if ($image['coverType'] == "fanart") {
-				$fanart = str_replace('http://', 'https://', $image['url']);
-			}
-		}
-		$details = array(
-			"seasonCount" => '',
-			"status" => '',
-			"topTitle" => $albumName,
-			"bottomTitle" => $artistName,
-			"overview" => isset($child['artist']['overview']) ? $child['artist']['overview'] : '',
-			"runtime" => '',
-			"image" => $fanart,
-			"ratings" => $child['artist']['ratings']['value'],
-			"videoQuality" => "unknown",
-			"audioChannels" => "unknown",
-			"audioCodec" => "unknown",
-			"videoCodec" => "unknown",
-			"size" => "unknown",
-			"genres" => $child['genres'],
-		);
-		array_push($gotCalendar, array(
-			"id" => "Lidarr-" . $number . "-" . $i,
-			"title" => $artistName,
-			"start" => $child['releaseDate'],
-			"className" => "inline-popups bg-calendar calendar-item musicID--",
-			"imagetype" => "music " . $downloaded,
-			"imagetypeFilter" => "music",
-			"downloadFilter" => $downloaded,
-			"bgColor" => str_replace('text', 'bg', $downloaded),
-			"details" => $details,
-			"data" => $child
-		));
-	}
-	if ($i != 0) {
-		return $gotCalendar;
-	}
-	return false;
-}
-
-function getRadarrCalendar($array, $number, $url)
-{
-	$url = rtrim($url, '/'); //remove trailing slash
-	$url = $url . '/api';
-	$array = json_decode($array, true);
-	$gotCalendar = array();
-	$i = 0;
-	foreach ($array as $child) {
-		if (isset($child['physicalRelease'])) {
-			$i++;
-			$movieName = $child['title'];
-			$movieID = $child['tmdbId'];
-			if (!isset($movieID)) {
-				$movieID = "";
-			}
-			$physicalRelease = $child['physicalRelease'];
-			$physicalRelease = strtotime($physicalRelease);
-			$physicalRelease = date("Y-m-d", $physicalRelease);
-			if (new DateTime() < new DateTime($physicalRelease)) {
-				$notReleased = "true";
-			} else {
-				$notReleased = "false";
-			}
-			$downloaded = $child['hasFile'];
-			if ($downloaded == "0" && $notReleased == "true") {
-				$downloaded = "text-info";
-			} elseif ($downloaded == "1") {
-				$downloaded = "text-success";
-			} else {
-				$downloaded = "text-danger";
-			}
-			$banner = "/plugins/images/cache/no-np.png";
-			foreach ($child['images'] as $image) {
-				if ($image['coverType'] == "banner" || $image['coverType'] == "fanart") {
-					if (strpos($image['url'], '://') === false) {
-						$imageUrl = $image['url'];
-						$urlParts = explode("/", $url);
-						$imageParts = explode("/", $image['url']);
-						if ($imageParts[1] == end($urlParts)) {
-							unset($imageParts[1]);
-							$imageUrl = implode("/", $imageParts);
-						}
-						$banner = $url . $imageUrl . '?apikey=' . $GLOBALS['radarrToken'];
-					} else {
-						$banner = $image['url'];
-					}
-					
-				}
-			}
-			if ($banner !== "/plugins/images/cache/no-np.png" || (strpos($banner, 'apikey') !== false)) {
-				$cacheDirectory = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR;
-				$imageURL = $banner;
-				$cacheFile = $cacheDirectory . $movieID . '.jpg';
-				$banner = 'plugins/images/cache/' . $movieID . '.jpg';
-				if (!file_exists($cacheFile)) {
-					cacheImage($imageURL, $movieID);
-					unset($imageURL);
-					unset($cacheFile);
-				}
-			}
-			$alternativeTitles = "";
-			foreach ($child['alternativeTitles'] as $alternative) {
-				$alternativeTitles .= $alternative['title'] . ', ';
-			}
-			$alternativeTitles = empty($child['alternativeTitles']) ? "" : substr($alternativeTitles, 0, -2);
-			$details = array(
-				"topTitle" => $movieName,
-				"bottomTitle" => $alternativeTitles,
-				"status" => $child['status'],
-				"overview" => $child['overview'],
-				"runtime" => $child['runtime'],
-				"image" => $banner,
-				"ratings" => $child['ratings']['value'],
-				"videoQuality" => $child["hasFile"] ? @$child['movieFile']['quality']['quality']['name'] : "unknown",
-				"audioChannels" => $child["hasFile"] ? @$child['movieFile']['mediaInfo']['audioChannels'] : "unknown",
-				"audioCodec" => $child["hasFile"] ? @$child['movieFile']['mediaInfo']['audioFormat'] : "unknown",
-				"videoCodec" => $child["hasFile"] ? @$child['movieFile']['mediaInfo']['videoCodec'] : "unknown",
-				"size" => $child["hasFile"] ? @$child['movieFile']['size'] : "unknown",
-				"genres" => $child['genres'],
-				"year" => isset($child['year']) ? $child['year'] : '',
-				"studio" => isset($child['studio']) ? $child['studio'] : '',
-			);
-			array_push($gotCalendar, array(
-				"id" => "Radarr-" . $number . "-" . $i,
-				"title" => $movieName,
-				"start" => $physicalRelease,
-				"className" => "inline-popups bg-calendar movieID--" . $movieID,
-				"imagetype" => "film " . $downloaded,
-				"imagetypeFilter" => "film",
-				"downloadFilter" => $downloaded,
-				"bgColor" => str_replace('text', 'bg', $downloaded),
-				"details" => $details
-			));
-		}
-	}
-	if ($i != 0) {
-		return $gotCalendar;
-	}
-	return false;
-}
 
-function getCouchCalendar($array, $number)
-{
-	$api = json_decode($array, true);
-	$gotCalendar = array();
-	$i = 0;
-	foreach ($api['movies'] as $child) {
-		if ($child['status'] == "active" || $child['status'] == "done") {
-			$i++;
-			$movieName = $child['info']['original_title'];
-			$movieID = $child['info']['tmdb_id'];
-			if (!isset($movieID)) {
-				$movieID = "";
-			}
-			$physicalRelease = (isset($child['info']['released']) ? $child['info']['released'] : null);
-			$backupRelease = (isset($child['info']['release_date']['theater']) ? $child['info']['release_date']['theater'] : null);
-			$physicalRelease = (isset($physicalRelease) ? $physicalRelease : $backupRelease);
-			$physicalRelease = strtotime($physicalRelease);
-			$physicalRelease = date("Y-m-d", $physicalRelease);
-			if (new DateTime() < new DateTime($physicalRelease)) {
-				$notReleased = "true";
-			} else {
-				$notReleased = "false";
-			}
-			$downloaded = ($child['status'] == "active") ? "0" : "1";
-			if ($downloaded == "0" && $notReleased == "true") {
-				$downloaded = "text-info";
-			} elseif ($downloaded == "1") {
-				$downloaded = "text-success";
-			} else {
-				$downloaded = "text-danger";
-			}
-			if (!empty($child['info']['images']['backdrop_original'])) {
-				$banner = $child['info']['images']['backdrop_original'][0];
-			} elseif (!empty($child['info']['images']['backdrop'])) {
-				$banner = $child['info']['images']['backdrop_original'][0];
-			} else {
-				$banner = "/plugins/images/cache/no-np.png";
-			}
-			if ($banner !== "/plugins/images/cache/no-np.png") {
-				$cacheDirectory = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR;
-				$imageURL = $banner;
-				$cacheFile = $cacheDirectory . $movieID . '.jpg';
-				$banner = 'plugins/images/cache/' . $movieID . '.jpg';
-				if (!file_exists($cacheFile)) {
-					cacheImage($imageURL, $movieID);
-					unset($imageURL);
-					unset($cacheFile);
-				}
+trait HomepageConnectFunctions
+{
+	public function csvHomepageUrlToken($url, $token)
+	{
+		$list = array();
+		$urlList = explode(',', $url);
+		$tokenList = explode(',', $token);
+		if (count($urlList) == count($tokenList)) {
+			foreach ($urlList as $key => $value) {
+				$list[$key] = array(
+					'url' => $this->qualifyURL($value),
+					'token' => $tokenList[$key]
+				);
 			}
-			$hasFile = (!empty($child['releases']) && !empty($child['releases'][0]['files']['movie']));
-			$details = array(
-				"topTitle" => $movieName,
-				"bottomTitle" => $child['info']['tagline'],
-				"status" => $child['status'],
-				"overview" => $child['info']['plot'],
-				"runtime" => $child['info']['runtime'],
-				"image" => $banner,
-				"ratings" => isset($child['info']['rating']['imdb'][0]) ? $child['info']['rating']['imdb'][0] : '',
-				"videoQuality" => $hasFile ? $child['releases'][0]['quality'] : "unknown",
-				"audioChannels" => "",
-				"audioCodec" => "",
-				"videoCodec" => "",
-				"genres" => $child['info']['genres'],
-				"year" => isset($child['info']['year']) ? $child['info']['year'] : '',
-				"studio" => isset($child['info']['year']) ? $child['info']['year'] : '',
-			);
-			array_push($gotCalendar, array(
-				"id" => "CouchPotato-" . $number . "-" . $i,
-				"title" => $movieName,
-				"start" => $physicalRelease,
-				"className" => "inline-popups bg-calendar calendar-item movieID--" . $movieID,
-				"imagetype" => "film " . $downloaded,
-				"imagetypeFilter" => "film",
-				"downloadFilter" => $downloaded,
-				"bgColor" => str_replace('text', 'bg', $downloaded),
-				"details" => $details
-			));
-		}
-	}
-	if ($i != 0) {
-		return $gotCalendar;
-	}
-	return false;
-}
-
-function getSickrageCalendarWanted($array, $number)
-{
-	$array = json_decode($array, true);
-	$gotCalendar = array();
-	$i = 0;
-	foreach ($array['data']['missed'] as $child) {
-		$i++;
-		$seriesName = $child['show_name'];
-		$seriesID = $child['tvdbid'];
-		$episodeID = $child['tvdbid'];
-		$episodeAirDate = $child['airdate'];
-		$episodeAirDateTime = explode(" ", $child['airs']);
-		$episodeAirDateTime = date("H:i:s", strtotime($episodeAirDateTime[1] . $episodeAirDateTime[2]));
-		$episodeAirDate = strtotime($episodeAirDate . $episodeAirDateTime);
-		$episodeAirDate = date("Y-m-d H:i:s", $episodeAirDate);
-		if (new DateTime() < new DateTime($episodeAirDate)) {
-			$unaired = true;
-		}
-		$downloaded = "0";
-		if ($downloaded == "0" && isset($unaired)) {
-			$downloaded = "text-info";
-		} elseif ($downloaded == "1") {
-			$downloaded = "text-success";
-		} else {
-			$downloaded = "text-danger";
-		}
-		$bottomTitle = 'S' . sprintf("%02d", $child['season']) . 'E' . sprintf("%02d", $child['episode']) . ' - ' . $child['ep_name'];
-		$cacheDirectory = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR;
-		$cacheFile = $cacheDirectory . $seriesID . '.jpg';
-		$fanart = "/plugins/images/cache/no-np.png";
-		if (file_exists($cacheFile)) {
-			$fanart = 'plugins/images/cache/' . $seriesID . '.jpg';
-			unset($cacheFile);
-		}
-		$details = array(
-			"seasonCount" => "",
-			"status" => $child['show_status'],
-			"topTitle" => $seriesName,
-			"bottomTitle" => $bottomTitle,
-			"overview" => isset($child['ep_plot']) ? $child['ep_plot'] : '',
-			"runtime" => "",
-			"image" => $fanart,
-			"ratings" => "",
-			"videoQuality" => isset($child["quality"]) ? $child["quality"] : "",
-			"audioChannels" => "",
-			"audioCodec" => "",
-			"videoCodec" => "",
-			"size" => "",
-			"genres" => "",
-		);
-		array_push($gotCalendar, array(
-			"id" => "Sick-" . $number . "-Miss-" . $i,
-			"title" => $seriesName,
-			"start" => $episodeAirDate,
-			"className" => "inline-popups bg-calendar calendar-item tvID--" . $episodeID,
-			"imagetype" => "tv " . $downloaded,
-			"imagetypeFilter" => "tv",
-			"downloadFilter" => $downloaded,
-			"bgColor" => str_replace('text', 'bg', $downloaded),
-			"details" => $details,
-		));
-	}
-	foreach ($array['data']['today'] as $child) {
-		$i++;
-		$seriesName = $child['show_name'];
-		$seriesID = $child['tvdbid'];
-		$episodeID = $child['tvdbid'];
-		$episodeAirDate = $child['airdate'];
-		$episodeAirDateTime = explode(" ", $child['airs']);
-		$episodeAirDateTime = date("H:i:s", strtotime($episodeAirDateTime[1] . $episodeAirDateTime[2]));
-		$episodeAirDate = strtotime($episodeAirDate . $episodeAirDateTime);
-		$episodeAirDate = date("Y-m-d H:i:s", $episodeAirDate);
-		if (new DateTime() < new DateTime($episodeAirDate)) {
-			$unaired = true;
-		}
-		$downloaded = "0";
-		if ($downloaded == "0" && isset($unaired)) {
-			$downloaded = "text-info";
-		} elseif ($downloaded == "1") {
-			$downloaded = "text-success";
-		} else {
-			$downloaded = "text-danger";
-		}
-		$bottomTitle = 'S' . sprintf("%02d", $child['season']) . 'E' . sprintf("%02d", $child['episode']) . ' - ' . $child['ep_name'];
-		$cacheDirectory = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR;
-		$cacheFile = $cacheDirectory . $seriesID . '.jpg';
-		$fanart = "/plugins/images/cache/no-np.png";
-		if (file_exists($cacheFile)) {
-			$fanart = 'plugins/images/cache/' . $seriesID . '.jpg';
-			unset($cacheFile);
-		}
-		$details = array(
-			"seasonCount" => "",
-			"status" => $child['show_status'],
-			"topTitle" => $seriesName,
-			"bottomTitle" => $bottomTitle,
-			"overview" => isset($child['ep_plot']) ? $child['ep_plot'] : '',
-			"runtime" => "",
-			"image" => $fanart,
-			"ratings" => "",
-			"videoQuality" => isset($child["quality"]) ? $child["quality"] : "",
-			"audioChannels" => "",
-			"audioCodec" => "",
-			"videoCodec" => "",
-			"size" => "",
-			"genres" => "",
-		);
-		array_push($gotCalendar, array(
-			"id" => "Sick-" . $number . "-Today-" . $i,
-			"title" => $seriesName,
-			"start" => $episodeAirDate,
-			"className" => "inline-popups bg-calendar calendar-item tvID--" . $episodeID,
-			"imagetype" => "tv " . $downloaded,
-			"imagetypeFilter" => "tv",
-			"downloadFilter" => $downloaded,
-			"bgColor" => str_replace('text', 'bg', $downloaded),
-			"details" => $details,
-		));
-	}
-	foreach ($array['data']['soon'] as $child) {
-		$i++;
-		$seriesName = $child['show_name'];
-		$seriesID = $child['tvdbid'];
-		$episodeID = $child['tvdbid'];
-		$episodeAirDate = $child['airdate'];
-		$episodeAirDateTime = explode(" ", $child['airs']);
-		$episodeAirDateTime = date("H:i:s", strtotime($episodeAirDateTime[1] . $episodeAirDateTime[2]));
-		$episodeAirDate = strtotime($episodeAirDate . $episodeAirDateTime);
-		$episodeAirDate = date("Y-m-d H:i:s", $episodeAirDate);
-		if (new DateTime() < new DateTime($episodeAirDate)) {
-			$unaired = true;
 		}
-		$downloaded = "0";
-		if ($downloaded == "0" && isset($unaired)) {
-			$downloaded = "text-info";
-		} elseif ($downloaded == "1") {
-			$downloaded = "text-success";
-		} else {
-			$downloaded = "text-danger";
-		}
-		$bottomTitle = 'S' . sprintf("%02d", $child['season']) . 'E' . sprintf("%02d", $child['episode']) . ' - ' . $child['ep_name'];
-		$cacheDirectory = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR;
-		$cacheFile = $cacheDirectory . $seriesID . '.jpg';
-		$fanart = "/plugins/images/cache/no-np.png";
-		if (file_exists($cacheFile)) {
-			$fanart = 'plugins/images/cache/' . $seriesID . '.jpg';
-			unset($cacheFile);
-		}
-		$details = array(
-			"seasonCount" => "",
-			"status" => $child['show_status'],
-			"topTitle" => $seriesName,
-			"bottomTitle" => $bottomTitle,
-			"overview" => isset($child['ep_plot']) ? $child['ep_plot'] : '',
-			"runtime" => "",
-			"image" => $fanart,
-			"ratings" => "",
-			"videoQuality" => isset($child["quality"]) ? $child["quality"] : "",
-			"audioChannels" => "",
-			"audioCodec" => "",
-			"videoCodec" => "",
-			"size" => "",
-			"genres" => "",
-		);
-		array_push($gotCalendar, array(
-			"id" => "Sick-" . $number . "-Soon-" . $i,
-			"title" => $seriesName,
-			"start" => $episodeAirDate,
-			"className" => "inline-popups bg-calendar calendar-item tvID--" . $episodeID,
-			"imagetype" => "tv " . $downloaded,
-			"imagetypeFilter" => "tv",
-			"downloadFilter" => $downloaded,
-			"bgColor" => str_replace('text', 'bg', $downloaded),
-			"details" => $details,
-		));
+		return $list;
 	}
-	foreach ($array['data']['later'] as $child) {
-		$i++;
-		$seriesName = $child['show_name'];
-		$seriesID = $child['tvdbid'];
-		$episodeID = $child['tvdbid'];
-		$episodeAirDate = $child['airdate'];
-		$episodeAirDateTime = explode(" ", $child['airs']);
-		$episodeAirDateTime = date("H:i:s", strtotime($episodeAirDateTime[1] . $episodeAirDateTime[2]));
-		$episodeAirDate = strtotime($episodeAirDate . $episodeAirDateTime);
-		$episodeAirDate = date("Y-m-d H:i:s", $episodeAirDate);
-		if (new DateTime() < new DateTime($episodeAirDate)) {
-			$unaired = true;
-		}
-		$downloaded = "0";
-		if ($downloaded == "0" && isset($unaired)) {
-			$downloaded = "text-info";
-		} elseif ($downloaded == "1") {
-			$downloaded = "text-success";
+	
+	public function streamType($value)
+	{
+		if ($value == "transcode" || $value == "Transcode") {
+			return "Transcode";
+		} elseif ($value == "copy" || $value == "DirectStream") {
+			return "Direct Stream";
+		} elseif ($value == "directplay" || $value == "DirectPlay") {
+			return "Direct Play";
 		} else {
-			$downloaded = "text-danger";
-		}
-		$bottomTitle = 'S' . sprintf("%02d", $child['season']) . 'E' . sprintf("%02d", $child['episode']) . ' - ' . $child['ep_name'];
-		$cacheDirectory = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR;
-		$cacheFile = $cacheDirectory . $seriesID . '.jpg';
-		$fanart = "/plugins/images/cache/no-np.png";
-		if (file_exists($cacheFile)) {
-			$fanart = 'plugins/images/cache/' . $seriesID . '.jpg';
-			unset($cacheFile);
-		}
-		$details = array(
-			"seasonCount" => "",
-			"status" => $child['show_status'],
-			"topTitle" => $seriesName,
-			"bottomTitle" => $bottomTitle,
-			"overview" => isset($child['ep_plot']) ? $child['ep_plot'] : '',
-			"runtime" => "",
-			"image" => $fanart,
-			"ratings" => "",
-			"videoQuality" => isset($child["quality"]) ? $child["quality"] : "",
-			"audioChannels" => "",
-			"audioCodec" => "",
-			"videoCodec" => "",
-			"size" => "",
-			"genres" => "",
-		);
-		array_push($gotCalendar, array(
-			"id" => "Sick-" . $number . "-Later-" . $i,
-			"title" => $seriesName,
-			"start" => $episodeAirDate,
-			"className" => "inline-popups bg-calendar calendar-item tvID--" . $episodeID,
-			"imagetype" => "tv " . $downloaded,
-			"imagetypeFilter" => "tv",
-			"downloadFilter" => $downloaded,
-			"bgColor" => str_replace('text', 'bg', $downloaded),
-			"details" => $details,
-		));
-	}
-	if ($i != 0) {
-		return $gotCalendar;
-	}
-	return false;
-}
-
-function getSickrageCalendarHistory($array, $number)
-{
-	$array = json_decode($array, true);
-	$gotCalendar = array();
-	$i = 0;
-	foreach ($array['data'] as $child) {
-		$i++;
-		$seriesName = $child['show_name'];
-		$seriesID = $child['tvdbid'];
-		$episodeID = $child['tvdbid'];
-		$episodeAirDate = $child['date'];
-		$downloaded = "text-success";
-		$bottomTitle = 'S' . sprintf("%02d", $child['season']) . 'E' . sprintf("%02d", $child['episode']);
-		$cacheDirectory = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR;
-		$cacheFile = $cacheDirectory . $seriesID . '.jpg';
-		$fanart = "/plugins/images/cache/no-np.png";
-		if (file_exists($cacheFile)) {
-			$fanart = 'plugins/images/cache/' . $seriesID . '.jpg';
-			unset($cacheFile);
+			return "Direct Play";
 		}
-		$details = array(
-			"seasonCount" => "",
-			"status" => $child['status'],
-			"topTitle" => $seriesName,
-			"bottomTitle" => $bottomTitle,
-			"overview" => '',
-			"runtime" => isset($child['series']['runtime']) ? $child['series']['runtime'] : 30,
-			"image" => $fanart,
-			"ratings" => isset($child['series']['ratings']['value']) ? $child['series']['ratings']['value'] : "unknown",
-			"videoQuality" => isset($child["quality"]) ? $child['quality'] : "unknown",
-			"audioChannels" => "",
-			"audioCodec" => "",
-			"videoCodec" => "",
-			"size" => "",
-			"genres" => "",
-		);
-		array_push($gotCalendar, array(
-			"id" => "Sick-" . $number . "-History-" . $i,
-			"title" => $seriesName,
-			"start" => $episodeAirDate,
-			"className" => "inline-popups bg-calendar calendar-item tvID--" . $episodeID,
-			"imagetype" => "tv " . $downloaded,
-			"imagetypeFilter" => "tv",
-			"downloadFilter" => $downloaded,
-			"bgColor" => str_replace('text', 'bg', $downloaded),
-			"details" => $details,
-		));
 	}
-	if ($i != 0) {
-		return $gotCalendar;
-	}
-	return false;
-}
-
-function ombiAPI($array)
-{
-	return ombiAction($array['data']['id'], $array['data']['action'], $array['data']['type'], $array['data']);
-}
-
-function ombiImport($type = null)
-{
-	if (!empty($GLOBALS['ombiURL']) && !empty($GLOBALS['ombiToken']) && !empty($type)) {
-		try {
-			$url = qualifyURL($GLOBALS['ombiURL']);
-			$headers = array(
-				"Accept" => "application/json",
-				"Content-Type" => "application/json",
-				"Apikey" => $GLOBALS['ombiToken']
-			);
-			$options = (localURL($url)) ? array('verify' => false) : array();
-			switch ($type) {
-				case 'emby':
-				case 'emby_local':
-				case 'emby_connect':
-				case 'emby_all':
-					$response = Requests::post($url . "/api/v1/Job/embyuserimporter", $headers, $options);
-					break;
-				case 'plex':
-					$response = Requests::post($url . "/api/v1/Job/plexuserimporter", $headers, $options);
-					break;
-				default:
-					return false;
-					break;
-			}
-			if ($response->success) {
-				writeLog('success', 'OMBI Connect Function - Ran User Import', 'SYSTEM');
-				return true;
-			} else {
-				writeLog('error', 'OMBI Connect Function - Error: Connection Unsuccessful', 'SYSTEM');
-				return false;
-			}
-		} catch (Requests_Exception $e) {
-			writeLog('error', 'OMBI Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
-			return false;
-		};
-	}
-	return false;
-}
-
-function ombiAction($id, $action, $type, $fullArray = null)
-{
-	if ($GLOBALS['homepageOmbiEnabled'] && !empty($GLOBALS['ombiURL']) && !empty($GLOBALS['ombiToken']) && qualifyRequest($GLOBALS['homepageOmbiAuth'])) {
-		$url = qualifyURL($GLOBALS['ombiURL']);
-		$headers = array(
-			"Accept" => "application/json",
-			"Content-Type" => "application/json",
-			"Apikey" => $GLOBALS['ombiToken']
-		);
-		$data = array(
-			'id' => $id,
-		);
+	
+	public function getCacheImageSize($type)
+	{
 		switch ($type) {
-			case 'season':
-			case 'tv':
-				$type = 'tv';
-				$add = array(
-					'tvDbId' => $id,
-					'requestAll' => ombiTVDefault('all'),
-					'latestSeason' => ombiTVDefault('last'),
-					'firstSeason' => ombiTVDefault('first')
+			case 'height':
+			case 'h':
+				return 300 * $this->config['cacheImageSize'];
+			case 'width':
+			case 'w':
+				return 200 * $this->config['cacheImageSize'];
+			case 'nowPlayingHeight':
+			case 'nph':
+				return 675 * $this->config['cacheImageSize'];
+			case 'nowPlayingWidth':
+			case 'npw':
+				return 1200 * $this->config['cacheImageSize'];
+			
+		}
+	}
+	
+	public function ombiImport($type = null)
+	{
+		if (!empty($this->config['ombiURL']) && !empty($this->config['ombiToken']) && !empty($type)) {
+			try {
+				$url = $this->qualifyURL($this->config['ombiURL']);
+				$headers = array(
+					"Accept" => "application/json",
+					"Content-Type" => "application/json",
+					"Apikey" => $GLOBALS['ombiToken']
 				);
-				break;
-			default:
-				$type = 'movie';
-				$add = array("theMovieDbId" => (int)$id);
-				break;
-		}
-		$success['act'] = $action;
-		$success['data'] = $data;
-		$success['add'] = $add;
-		$success['type'] = $type;
-		try {
-			$options = (localURL($url)) ? array('verify' => false) : array();
-			switch ($action) {
-				case 'add':
-					if (isset($_COOKIE['Auth'])) {
-						$headers = array(
-							"Accept" => "application/json",
-							"Content-Type" => "application/json",
-							"Authorization" => "Bearer " . $_COOKIE['Auth']
-						);
-						$success['head'] = $headers;
-					} else {
-						return false;
-					}
-					//https://api.themoviedb.org/3/movie/157336?api_key=83cf4ee97bb728eeaf9d4a54e64356a1
-					// Lets check if it exists inside Ombi first... but since I can't search with ID - i have to query title from id
-					$tmdbResponse = Requests::get('https://api.themoviedb.org/3/' . $type . '/' . $id . '?api_key=83cf4ee97bb728eeaf9d4a54e64356a1', [], $options);
-					if ($tmdbResponse->success) {
-						$details = json_decode($tmdbResponse->body, true);
-						if (count($details) > 0) {
-							switch ($type) {
-								case 'tv':
-									$title = $details['name'];
-									$idType = 'theTvDbId';
-									$tmdbResponseID = Requests::get('https://api.themoviedb.org/3/tv/' . $id . '/external_ids?api_key=83cf4ee97bb728eeaf9d4a54e64356a1', [], $options);
-									if ($tmdbResponseID->success) {
-										$detailsID = json_decode($tmdbResponseID->body, true);
-										if (count($detailsID) > 0) {
-											if (isset($detailsID['tvdb_id'])) {
-												$id = $detailsID['tvdb_id'];
-											} else {
-												return false;
-											}
-										}
-									}
-									break;
-								case 'movie':
-									$title = $details['title'];
-									$idType = 'theMovieDbId';
-									break;
-								default:
-									return false;
-							}
-						} else {
-							return false;
-						}
-					}
-					$searchResponse = Requests::get($url . '/api/v1/Search/' . $type . '/' . urlencode($title), $headers, $options);
-					if ($searchResponse->success) {
-						$details = json_decode($searchResponse->body, true);
-						if (count($details) > 0) {
-							foreach ($details as $k => $v) {
-								if ($v[$idType] == $id) {
-									if ($v['available']) {
-										$success['api'] = $searchResponse;
-										$success['bd']['isError'] = true;
-										$success['bd']['errorMessage'] = 'Request is already available';
-										$success['bd'] = json_encode($success['bd']);
-										$success['ok'] = true;
-									} elseif ($v['requested']) {
-										$success['api'] = $searchResponse;
-										$success['bd']['isError'] = true;
-										$success['bd']['errorMessage'] = 'Request is already requested';
-										$success['bd'] = json_encode($success['bd']);
-										$success['ok'] = true;
-									} else {
-										$response = Requests::post($url . "/api/v1/Request/" . $type, $headers, json_encode($add), $options);
-									}
-								}
-							}
-						}
-					}
-					break;
-				default:
-					if (qualifyRequest(1)) {
-						switch ($action) {
-							case 'approve':
-								$response = Requests::post($url . "/api/v1/Request/" . $type . "/approve", $headers, json_encode($data), $options);
-								break;
-							case 'available':
-								$response = Requests::post($url . "/api/v1/Request/" . $type . "/available", $headers, json_encode($data), $options);
-								break;
-							case 'unavailable':
-								$response = Requests::post($url . "/api/v1/Request/" . $type . "/unavailable", $headers, json_encode($data), $options);
-								break;
-							case 'deny':
-								$response = Requests::put($url . "/api/v1/Request/" . $type . "/deny", $headers, json_encode($data), $options);
-								break;
-							case 'delete':
-								$response = Requests::delete($url . "/api/v1/Request/" . $type . "/" . $id, $headers, $options);
-								break;
-							default:
-								return false;
-						}
-					}
-					break;
-			}
-			if (!$success['ok']) {
-				$success['api'] = $response;
-				$success['bd'] = $response->body;
-				if ($response->success) {
-					$success['ok'] = true;
-				}
-			}
-		} catch (Requests_Exception $e) {
-			writeLog('error', 'OMBI Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
-		};
-	}
-	return isset($success['ok']) ? $success : false;
-}
-
-function getOmbiRequests($type = "both", $limit = 50)
-{
-	$api['count'] = array(
-		'movie' => 0,
-		'tv' => 0,
-		'limit' => (integer)$limit
-	);
-	if ($GLOBALS['homepageOmbiEnabled'] && !empty($GLOBALS['ombiURL']) && !empty($GLOBALS['ombiToken']) && qualifyRequest($GLOBALS['homepageOmbiAuth'])) {
-		$url = qualifyURL($GLOBALS['ombiURL']);
-		$headers = array(
-			"Accept" => "application/json",
-			"Apikey" => $GLOBALS['ombiToken'],
-		);
-		$requests = array();
-		try {
-			$options = (localURL($url)) ? array('verify' => false) : array();
-			switch ($type) {
-				case 'movie':
-					$movie = Requests::get($url . "/api/v1/Request/movie", $headers, $options);
-					break;
-				case 'tv':
-					$tv = Requests::get($url . "/api/v1/Request/tv", $headers, $options);
-					break;
-				default:
-					$movie = Requests::get($url . "/api/v1/Request/movie", $headers, $options);
-					$tv = Requests::get($url . "/api/v1/Request/tv", $headers, $options);
-					break;
-			}
-			if ($movie->success || $tv->success) {
-				if (isset($movie)) {
-					$movie = json_decode($movie->body, true);
-					//$movie = array_reverse($movie);
-					foreach ($movie as $key => $value) {
-						$proceed = (($GLOBALS['ombiLimitUser']) && strtolower($GLOBALS['organizrUser']['username']) == strtolower($value['requestedUser']['userName'])) || (!$GLOBALS['ombiLimitUser']) || qualifyRequest(1) ? true : false;
-						if ($proceed) {
-							$api['count']['movie']++;
-							$requests[] = array(
-								'id' => $value['theMovieDbId'],
-								'title' => $value['title'],
-								'overview' => $value['overview'],
-								'poster' => (isset($value['posterPath']) && $value['posterPath'] !== '') ? 'https://image.tmdb.org/t/p/w300/' . $value['posterPath'] : 'plugins/images/cache/no-list.png',
-								'background' => (isset($value['background']) && $value['background'] !== '') ? 'https://image.tmdb.org/t/p/w1280/' . $value['background'] : '',
-								'approved' => $value['approved'],
-								'available' => $value['available'],
-								'denied' => $value['denied'],
-								'deniedReason' => $value['deniedReason'],
-								'user' => $value['requestedUser']['userName'],
-								'userAlias' => $value['requestedUser']['userAlias'],
-								'request_id' => $value['id'],
-								'request_date' => $value['requestedDate'],
-								'release_date' => $value['releaseDate'],
-								'type' => 'movie',
-								'icon' => 'mdi mdi-filmstrip',
-								'color' => 'palette-Deep-Purple-900 bg white',
-							);
-						}
-					}
-				}
-				if (isset($tv) && (is_array($tv) || is_object($tv))) {
-					$tv = json_decode($tv->body, true);
-					foreach ($tv as $key => $value) {
-						if (count($value['childRequests']) > 0) {
-							$proceed = (($GLOBALS['ombiLimitUser']) && strtolower($GLOBALS['organizrUser']['username']) == strtolower($value['childRequests'][0]['requestedUser']['userName'])) || (!$GLOBALS['ombiLimitUser']) || qualifyRequest(1) ? true : false;
-							if ($proceed) {
-								$api['count']['tv']++;
-								$requests[] = array(
-									'id' => $value['tvDbId'],
-									'title' => $value['title'],
-									'overview' => $value['overview'],
-									'poster' => (isset($value['posterPath']) && $value['posterPath'] !== '') ? $value['posterPath'] : 'plugins/images/cache/no-list.png',
-									'background' => (isset($value['background']) && $value['background'] !== '') ? 'https://image.tmdb.org/t/p/w1280/' . $value['background'] : '',
-									'approved' => $value['childRequests'][0]['approved'],
-									'available' => $value['childRequests'][0]['available'],
-									'denied' => $value['childRequests'][0]['denied'],
-									'deniedReason' => $value['childRequests'][0]['deniedReason'],
-									'user' => $value['childRequests'][0]['requestedUser']['userName'],
-									'userAlias' => $value['childRequests'][0]['requestedUser']['userAlias'],
-									'request_id' => $value['id'],
-									'request_date' => $value['childRequests'][0]['requestedDate'],
-									'release_date' => $value['releaseDate'],
-									'type' => 'tv',
-									'icon' => 'mdi mdi-television',
-									'color' => 'grayish-blue-bg',
-								);
-							}
-						}
-					}
-				}
-				//sort here
-				usort($requests, function ($item1, $item2) {
-					if ($item1['request_date'] == $item2['request_date']) {
-						return 0;
-					}
-					return $item1['request_date'] > $item2['request_date'] ? -1 : 1;
-				});
-			}
-		} catch (Requests_Exception $e) {
-			writeLog('error', 'OMBI Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
-		};
-	}
-	$api['content'] = isset($requests) ? array_slice($requests, 0, $limit) : false;
-	return $api;
-}
-
-function unifiConnect()
-{
-	if ($GLOBALS['homepageUnifiEnabled'] && !empty($GLOBALS['unifiURL']) && !empty($GLOBALS['unifiSiteName']) && !empty($GLOBALS['unifiUsername']) && !empty($GLOBALS['unifiPassword']) && qualifyRequest($GLOBALS['homepageUnifiAuth'])) {
-		$api['content']['unifi'] = array();
-		$url = qualifyURL($GLOBALS['unifiURL']);
-		$urlStat = $url . '/api/s/' . $GLOBALS['unifiSiteName'] . '/stat/health';
-		try {
-			$options = array('verify' => false, 'verifyname' => false, 'follow_redirects' => false);
-			$data = array(
-				'username' => $GLOBALS['unifiUsername'],
-				'password' => decrypt($GLOBALS['unifiPassword']),
-				'remember' => true,
-				'strict' => true
-			);
-			$response = Requests::post($url . '/api/login', array(), json_encode($data), $options);
-			if ($response->success) {
-				$cookie['unifises'] = ($response->cookies['unifises']->value) ?? false;
-				$cookie['csrf_token'] = ($response->cookies['csrf_token']->value) ?? false;
-			} else {
-				return false;
-			}
-			$headers = array(
-				'cookie' => 'unifises=' . $cookie['unifises'] . ';' . 'csrf_token=' . $cookie['csrf_token'] . ';'
-			);
-			$response = Requests::get($urlStat, $headers, $options);
-			if ($response->success) {
-				$api['content']['unifi'] = json_decode($response->body, true);
-			}
-		} catch (Requests_Exception $e) {
-			writeLog('error', 'Unifi Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
-		};
-		$api['content']['unifi'] = isset($api['content']['unifi']) ? $api['content']['unifi'] : false;
-		return $api;
-	}
-	return false;
-}
-
-function getTautulli()
-{
-	if ($GLOBALS['homepageTautulliEnabled'] && !empty($GLOBALS['tautulliURL']) && !empty($GLOBALS['tautulliApikey']) && qualifyRequest($GLOBALS['homepageTautulliAuth'])) {
-		$api = [];
-		$url = qualifyURL($GLOBALS['tautulliURL']);
-		$apiURL = $url . '/api/v2?apikey=' . $GLOBALS['tautulliApikey'];
-		$height = getCacheImageSize('h');
-		$width = getCacheImageSize('w');
-		$nowPlayingHeight = getCacheImageSize('nph');
-		$nowPlayingWidth = getCacheImageSize('npw');
-		try {
-			$homestatsUrl = $apiURL . '&cmd=get_home_stats&grouping=1';
-			$homestats = Requests::get($homestatsUrl, [], []);
-			if ($homestats->success) {
-				$homestats = json_decode($homestats->body, true);
-				$api['homestats'] = $homestats['response'];
-				// Cache art & thumb for first result in each tautulli API result
-				$categories = ['top_movies', 'top_tv', 'popular_movies', 'popular_tv'];
-				foreach ($categories as $cat) {
-					$key = array_search($cat, array_column($api['homestats']['data'], 'stat_id'));
-					$img = $api['homestats']['data'][$key]['rows'][0];
-					cacheImage($url . '/pms_image_proxy?img=' . $img['art'] . '&rating_key=' . $img['rating_key'] . '&width=' . $nowPlayingWidth . '&height=' . $nowPlayingHeight, $img['rating_key'] . '-np');
-					cacheImage($url . '/pms_image_proxy?img=' . $img['thumb'] . '&rating_key=' . $img['rating_key'] . '&width=' . $width . '&height=' . $height, $img['rating_key'] . '-list');
-					$img['art'] = 'plugins/images/cache/' . $img['rating_key'] . '-np.jpg';
-					$img['thumb'] = 'plugins/images/cache/' . $img['rating_key'] . '-list.jpg';
-					$api['homestats']['data'][$key]['rows'][0] = $img;
-				}
-				// Cache the platform icon
-				$key = array_search('top_platforms', array_column($api['homestats']['data'], 'stat_id'));
-				$platform = $api['homestats']['data'][$key]['rows'][0]['platform_name'];
-				cacheImage($url . '/images/platforms/' . $platform . '.svg', 'tautulli-' . $platform, 'svg');
-			}
-			$libstatsUrl = $apiURL . '&cmd=get_libraries';
-			$libstats = Requests::get($libstatsUrl, [], []);
-			if ($libstats->success) {
-				$libstats = json_decode($libstats->body, true);
-				$api['libstats'] = $libstats['response'];
-				$categories = ['movie.svg', 'show.svg', 'artist.svg'];
-				foreach ($categories as $cat) {
-					$parts = explode('.', $cat);
-					cacheImage($url . '/images/libraries/' . $cat, 'tautulli-' . $parts[0], $parts[1]);
-				}
-			}
-			$api['options'] = [
-				'url' => $url,
-				'libraries' => $GLOBALS['tautulliLibraries'],
-				'topMovies' => $GLOBALS['tautulliTopMovies'],
-				'topTV' => $GLOBALS['tautulliTopTV'],
-				'topUsers' => $GLOBALS['tautulliTopUsers'],
-				'topPlatforms' => $GLOBALS['tautulliTopPlatforms'],
-				'popularMovies' => $GLOBALS['tautulliPopularMovies'],
-				'popularTV' => $GLOBALS['tautulliPopularTV'],
-				'title' => $GLOBALS['tautulliHeaderToggle'],
-			];
-			$ids = []; // Array of stat_ids to remove from the returned array
-			if (!qualifyRequest($GLOBALS['homepageTautulliLibraryAuth'])) {
-				$api['options']['libraries'] = false;
-				unset($api['libstats']);
-			}
-			if (!qualifyRequest($GLOBALS['homepageTautulliViewsAuth'])) {
-				$api['options']['topMovies'] = false;
-				$api['options']['topTV'] = false;
-				$api['options']['popularMovies'] = false;
-				$api['options']['popularTV'] = false;
-				$ids = array_merge(['top_movies', 'popular_movies', 'popular_tv', 'top_tv'], $ids);
-				$api['homestats']['data'] = array_values($api['homestats']['data']);
-			}
-			if (!qualifyRequest($GLOBALS['homepageTautulliMiscAuth'])) {
-				$api['options']['topUsers'] = false;
-				$api['options']['topPlatforms'] = false;
-				$ids = array_merge(['top_platforms', 'top_users'], $ids);
-				$api['homestats']['data'] = array_values($api['homestats']['data']);
-			}
-			$ids = array_merge(['top_music', 'popular_music', 'last_watched', 'most_concurrent'], $ids);
-			foreach ($ids as $id) {
-				if ($key = array_search($id, array_column($api['homestats']['data'], 'stat_id'))) {
-					unset($api['homestats']['data'][$key]);
-					$api['homestats']['data'] = array_values($api['homestats']['data']);
-				}
-			}
-		} catch (Requests_Exception $e) {
-			writeLog('error', 'Tautulli Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
-		};
-		$api = isset($api) ? $api : false;
-		return $api;
-	}
-	return false;
-}
-
-function getMonitorr()
-{
-	if ($GLOBALS['homepageMonitorrEnabled'] && !empty($GLOBALS['monitorrURL']) && qualifyRequest($GLOBALS['homepageMonitorrAuth'])) {
-		$api = [];
-		$url = qualifyURL($GLOBALS['monitorrURL']);
-		$dataUrl = $url . '/assets/php/loop.php';
-		try {
-			$response = Requests::get($dataUrl, ['Token' => $GLOBALS['organizrAPI']], []);
-			if ($response->success) {
-				$html = html_entity_decode($response->body);
-				// This section grabs the names of all services by regex
-				$services = [];
-				$servicesMatch = [];
-				$servicePattern = '/<div id="servicetitle"><div>(.*)<\/div><\/div><div class="btnonline">Online<\/div><\/a><\/div><\/div>|<div id="servicetitleoffline".*><div>(.*)<\/div><\/div><div class="btnoffline".*>Offline<\/div><\/div><\/div>|<div id="servicetitlenolink".*><div>(.*)<\/div><\/div><div class="btnonline".*>Online<\/div><\/div><\/div>|<div id="servicetitle"><div>(.*)<\/div><\/div><div class="btnunknown">/';
-				preg_match_all($servicePattern, $html, $servicesMatch);
-				$services = array_filter($servicesMatch[1]) + array_filter($servicesMatch[2]) + array_filter($servicesMatch[3]) + array_filter($servicesMatch[4]);
-				$statuses = [];
-				foreach ($services as $key => $service) {
-					$statusPattern = '/' . $service . '<\/div><\/div><div class="btnonline">(Online)<\/div>|' . $service . '<\/div><\/div><div class="btnoffline".*>(Offline)<\/div><\/div><\/div>|' . $service . '<\/div><\/div><div class="btnunknown">(.*)<\/div><\/a>/';
-					$status = [];
-					preg_match($statusPattern, $html, $status);
-					$statuses[$service] = $status;
-					foreach ($status as $match) {
-						if ($match == 'Online') {
-							$statuses[$service] = [
-								'status' => true
-							];
-						} else if ($match == 'Offline') {
-							$statuses[$service] = [
-								'status' => false
-							];
-						} else if ($match == 'Unresponsive') {
-							$statuses[$service] = [
-								'status' => 'unresponsive'
-							];
-						}
-					}
-					$statuses[$service]['sort'] = $key;
-					$imageMatch = [];
-					$imgPattern = '/assets\/img\/\.\.(.*)" class="serviceimg" alt=.*><\/div><\/div><div id="servicetitle"><div>' . $service . '|assets\/img\/\.\.(.*)" class="serviceimg imgoffline" alt=.*><\/div><\/div><div id="servicetitleoffline".*><div>' . $service . '|assets\/img\/\.\.(.*)" class="serviceimg" alt=.*><\/div><\/div><div id="servicetitlenolink".*><div>' . $service . '/';
-					preg_match($imgPattern, $html, $imageMatch);
-					unset($imageMatch[0]);
-					$imageMatch = array_values($imageMatch);
-					// array_push($api['imagematches'][$service], $imageMatch);
-					foreach ($imageMatch as $match) {
-						if ($match !== '') {
-							$image = $match;
-						}
-					}
-					$ext = explode('.', $image);
-					$ext = $ext[key(array_slice($ext, -1, 1, true))];
-					$imageUrl = $url . '/assets' . $image;
-					$cacheDirectory = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR;
-					$img = Requests::get($imageUrl, ['Token' => $GLOBALS['organizrAPI']], []);
-					if ($img->success) {
-						$base64 = 'data:image/' . $ext . ';base64,' . base64_encode($img->body);
-						$statuses[$service]['image'] = $base64;
-					} else {
-						$statuses[$service]['image'] = $cacheDirectory . 'no-list.png';
-					}
-					$linkMatch = [];
-					$linkPattern = '/<a class="servicetile" href="(.*)" target="_blank" style="display: block"><div id="serviceimg"><div><img id="' . strtolower($service) . '-service-img/';
-					preg_match($linkPattern, $html, $linkMatch);
-					$linkMatch = array_values($linkMatch);
-					unset($linkMatch[0]);
-					foreach ($linkMatch as $link) {
-						if ($link !== '') {
-							$statuses[$service]['link'] = $link;
-						}
-					}
-				}
-				foreach ($statuses as $status) {
-					foreach ($status as $key => $value) {
-						if (!isset($sortArray[$key])) {
-							$sortArray[$key] = array();
-						}
-						$sortArray[$key][] = $value;
-					}
-				}
-				array_multisort($sortArray['status'], SORT_ASC, $sortArray['sort'], SORT_ASC, $statuses);
-				$api['services'] = $statuses;
-				$api['options'] = [
-					'title' => $GLOBALS['monitorrHeader'],
-					'titleToggle' => $GLOBALS['monitorrHeaderToggle'],
-					'compact' => $GLOBALS['monitorrCompact'],
-				];
-			}
-		} catch (Requests_Exception $e) {
-			writeLog('error', 'Monitorr Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
-			$api['error'] = $e->getMessage();
-		};
-		$api = isset($api) ? $api : false;
-		return $api;
-	}
-}
-
-function getSpeedtest()
-{
-	if ($GLOBALS['homepageSpeedtestEnabled'] && !empty($GLOBALS['speedtestURL']) && qualifyRequest($GLOBALS['homepageSpeedtestAuth'])) {
-		$api = [];
-		$url = qualifyURL($GLOBALS['speedtestURL']);
-		$dataUrl = $url . '/api/speedtest/latest';
-		try {
-			$response = Requests::get($dataUrl);
-			if ($response->success) {
-				$json = json_decode($response->body, true);
-				$api['data'] = [
-					'current' => $json['data'],
-					'average' => $json['average'],
-					'max' => $json['max'],
-				];
-				$api['options'] = [
-					'title' => $GLOBALS['speedtestHeader'],
-					'titleToggle' => $GLOBALS['speedtestHeaderToggle'],
-				];
-			}
-		} catch (Requests_Exception $e) {
-			writeLog('error', 'Speedtest Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
-		};
-		$api = isset($api) ? $api : false;
-		return $api;
-	}
-}
-
-function getNetdata()
-{
-	if ($GLOBALS['homepageNetdataEnabled'] && !empty($GLOBALS['netdataURL']) && qualifyRequest($GLOBALS['homepageNetdataAuth'])) {
-		require_once('netdata-functions.php');
-		$api = [];
-		$api['data'] = [];
-		$api['url'] = $GLOBALS['netdataURL'];
-		$url = qualifyURL($GLOBALS['netdataURL']);
-		for ($i = 1; $i < 8; $i++) {
-			if ($GLOBALS['netdata' . ($i) . 'Enabled']) {
-				switch ($GLOBALS['netdata' . $i . 'Data']) {
-					case 'disk-read':
-						$data = disk('in', $url);
-						break;
-					case 'disk-write':
-						$data = disk('out', $url);
-						$data['value'] = abs($data['value']);
-						$data['percent'] = abs($data['percent']);
-						break;
-					case 'cpu':
-						$data = cpu($url);
-						break;
-					case 'net-in':
-						$data = net('received', $url);
-						break;
-					case 'net-out':
-						$data = net('sent', $url);
-						$data['value'] = abs($data['value']);
-						$data['percent'] = abs($data['percent']);
+				$options = ($this->localURL($url)) ? array('verify' => false) : array();
+				switch ($type) {
+					case 'emby':
+					case 'emby_local':
+					case 'emby_connect':
+					case 'emby_all':
+						$response = Requests::post($url . "/api/v1/Job/embyuserimporter", $headers, $options);
 						break;
-					case 'ram-used':
-						$data = ram($url);
-						break;
-					case 'swap-used':
-						$data = swap($url);
-						break;
-					case 'disk-avail':
-						$data = diskSpace('avail', $url);
-						break;
-					case 'disk-used':
-						$data = diskSpace('used', $url);
-						break;
-					case 'ipmi-temp-c':
-						$data = ipmiTemp($url, 'c');
-						break;
-					case 'ipmi-temp-f':
-						$data = ipmiTemp($url, 'f');
-						break;
-					case 'cpu-temp-c':
-						$data = cpuTemp($url, 'c');
-						break;
-					case 'cpu-temp-f':
-						$data = cpuTemp($url, 'f');
-						break;
-					case 'custom':
-						$data = customNetdata($url, $i);
+					case 'plex':
+						$response = Requests::post($url . "/api/v1/Job/plexuserimporter", $headers, $options);
 						break;
 					default:
-						$data = [
-							'title' => 'DNC',
-							'value' => 0,
-							'units' => 'N/A',
-							'max' => 100,
-						];
+						return false;
 						break;
 				}
-				$data['title'] = $GLOBALS['netdata' . $i . 'Title'];
-				$data['colour'] = $GLOBALS['netdata' . $i . 'Colour'];
-				$data['chart'] = $GLOBALS['netdata' . $i . 'Chart'];
-				$data['size'] = $GLOBALS['netdata' . $i . 'Size'];
-				$data['lg'] = $GLOBALS['netdata' . ($i) . 'lg'];
-				$data['md'] = $GLOBALS['netdata' . ($i) . 'md'];
-				$data['sm'] = $GLOBALS['netdata' . ($i) . 'sm'];
-				array_push($api['data'], $data);
-			}
-		}
-		$api = isset($api) ? $api : false;
-		return $api;
-	}
-}
-
-function getOctoprint()
-{
-	if ($GLOBALS['homepageOctoprintEnabled'] && !empty($GLOBALS['octoprintURL']) && !empty($GLOBALS['octoprintToken']) && qualifyRequest($GLOBALS['homepageOctoprintAuth'])) {
-		$api = [];
-		$url = qualifyURL($GLOBALS['octoprintURL']);
-		$endpoints = ['job', 'settings'];
-		$api['data']['url'] = $GLOBALS['octoprintURL'];
-		foreach ($endpoints as $endpoint) {
-			$dataUrl = $url . '/api/' . $endpoint;
-			try {
-				$headers = array('X-API-KEY' => $GLOBALS['octoprintToken']);
-				$response = Requests::get($dataUrl, $headers);
 				if ($response->success) {
-					$json = json_decode($response->body, true);
-					$api['data'][$endpoint] = $json;
-					$api['options'] = [
-						'title' => $GLOBALS['octoprintHeader'],
-						'titleToggle' => $GLOBALS['octoprintHeaderToggle'],
-					];
+					$this->writeLog('success', 'OMBI Connect Function - Ran User Import', 'SYSTEM');
+					return true;
+				} else {
+					$this->writeLog('error', 'OMBI Connect Function - Error: Connection Unsuccessful', 'SYSTEM');
+					return false;
 				}
 			} catch (Requests_Exception $e) {
-				writeLog('error', 'Octoprint Function - Error: ' . $e->getMessage(), 'SYSTEM');
-			};
+				$this->writeLog('error', 'OMBI Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
+				return false;
+			}
 		}
-		$api = isset($api) ? $api : false;
-		return $api;
+		return false;
 	}
 }
 
+
 function testAPIConnection($array)
 {
 	switch ($array['data']['action']) {
-		case 'unifiSite':
-			if (!empty($GLOBALS['unifiURL']) && !empty($GLOBALS['unifiUsername']) && !empty($GLOBALS['unifiPassword'])) {
-				$url = qualifyURL($GLOBALS['unifiURL']);
-				try {
-					$options = array('verify' => false, 'verifyname' => false, 'follow_redirects' => false);
-					$data = array(
-						'username' => $GLOBALS['unifiUsername'],
-						'password' => decrypt($GLOBALS['unifiPassword']),
-						'remember' => true,
-						'strict' => true
-					);
-					$response = Requests::post($url . '/api/login', array(), json_encode($data), $options);
-					if ($response->success) {
-						$cookie['unifises'] = ($response->cookies['unifises']->value) ?? false;
-						$cookie['csrf_token'] = ($response->cookies['csrf_token']->value) ?? false;
-					} else {
-						return false;
-					}
-					$headers = array(
-						'cookie' => 'unifises=' . $cookie['unifises'] . ';' . 'csrf_token=' . $cookie['csrf_token'] . ';'
-					);
-					$response = Requests::get($url . '/api/self/sites', $headers, $options);
-					if ($response->success) {
-						$body = json_decode($response->body, true);
-						return $body;
-					} else {
-						return false;
-					}
-				} catch (Requests_Exception $e) {
-					writeLog('error', 'Unifi Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
-				};
-			}
-			break;
-		case 'unifi':
-			if (!empty($GLOBALS['unifiURL']) && !empty($GLOBALS['unifiUsername']) && !empty($GLOBALS['unifiPassword']) && !empty($GLOBALS['unifiSiteName'])) {
-				$url = qualifyURL($GLOBALS['unifiURL']);
-				try {
-					$options = array('verify' => false, 'verifyname' => false, 'follow_redirects' => false);
-					$data = array(
-						'username' => $GLOBALS['unifiUsername'],
-						'password' => decrypt($GLOBALS['unifiPassword']),
-						'remember' => true,
-						'strict' => true
-					);
-					$response = Requests::post($url . '/api/login', array(), json_encode($data), $options);
-					if ($response->success) {
-						$cookie['unifises'] = ($response->cookies['unifises']->value) ?? false;
-						$cookie['csrf_token'] = ($response->cookies['csrf_token']->value) ?? false;
-					} else {
-						return 'Failed to Login';
-					}
-					$headers = array(
-						'cookie' => 'unifises=' . $cookie['unifises'] . ';' . 'csrf_token=' . $cookie['csrf_token'] . ';'
-					);
-					$response = Requests::get($url . '/api/s/' . $GLOBALS['unifiSiteName'] . '/self', $headers, $options);
-					$body = json_decode($response->body, true);
-					return ($body['meta']['rc'] == 'ok') ? true : $body['meta']['msg'];
-				} catch (Requests_Exception $e) {
-					writeLog('error', 'Unifi Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
-				};
-			} else {
-				return 'Not all data is filled in...';
-			}
-			break;
-		case 'ombi':
-			if (!empty($GLOBALS['ombiURL']) && !empty($GLOBALS['ombiToken'])) {
-				$url = qualifyURL($GLOBALS['ombiURL']);
-				$url = $url . "/api/v1/Status/info";
-				$headers = array(
-					"Accept" => "application/json",
-					"Content-Type" => "application/json",
-					"Apikey" => $GLOBALS['ombiToken']
-				);
-				try {
-					$options = (localURL($url)) ? array('verify' => false) : array();
-					$response = Requests::get($url, $headers, $options);
-					if ($response->success) {
-						return true;
-					} else {
-						return $response->body;
-					}
-				} catch (Requests_Exception $e) {
-					return $e->getMessage();
-				};
-			} else {
-				return 'URL and/or Token not setup';
-			}
-			break;
-		case 'plex':
-			if (!empty($GLOBALS['plexURL']) && !empty($GLOBALS['plexToken'])) {
-				$url = qualifyURL($GLOBALS['plexURL']);
-				$url = $url . "/?X-Plex-Token=" . $GLOBALS['plexToken'];
-				try {
-					$options = (localURL($url)) ? array('verify' => false) : array();
-					$response = Requests::get($url, array(), $options);
-					libxml_use_internal_errors(true);
-					if ($response->success) {
-						return true;
-					}
-				} catch (Requests_Exception $e) {
-					return $e->getMessage();
-				};
-			} else {
-				return 'URL and/or Token not setup';
-			}
-			break;
-		case 'emby':
-			break;
-		case 'sonarr':
-			if (!empty($GLOBALS['sonarrURL']) && !empty($GLOBALS['sonarrToken'])) {
-				$sonarrs = array();
-				$sonarrURLList = explode(',', $GLOBALS['sonarrURL']);
-				$sonarrTokenList = explode(',', $GLOBALS['sonarrToken']);
-				if (count($sonarrURLList) == count($sonarrTokenList)) {
-					foreach ($sonarrURLList as $key => $value) {
-						$sonarrs[$key] = array(
-							'url' => $value,
-							'token' => $sonarrTokenList[$key]
-						);
-					}
-					foreach ($sonarrs as $key => $value) {
-						try {
-							$sonarr = new Kryptonit3\Sonarr\Sonarr($value['url'], $value['token']);
-							$result = json_decode($sonarr->getSystemStatus(), true);
-							return (array_key_exists('error', $result)) ? $result['error']['msg'] : true;
-						} catch (Exception $e) {
-							return strip($e->getMessage());
-						}
-					}
-				}
-			} else {
-				return 'URL/s and/or Token/s not setup';
-			}
-			break;
-		case 'lidarr':
-			if (!empty($GLOBALS['lidarrURL']) && !empty($GLOBALS['lidarrToken'])) {
-				$sonarrs = array();
-				$sonarrURLList = explode(',', $GLOBALS['lidarrURL']);
-				$sonarrTokenList = explode(',', $GLOBALS['lidarrToken']);
-				if (count($sonarrURLList) == count($sonarrTokenList)) {
-					foreach ($sonarrURLList as $key => $value) {
-						$sonarrs[$key] = array(
-							'url' => $value,
-							'token' => $sonarrTokenList[$key]
-						);
-					}
-					foreach ($sonarrs as $key => $value) {
-						try {
-							$sonarr = new Kryptonit3\Sonarr\Sonarr($value['url'], $value['token'], true);
-							$result = json_decode($sonarr->getSystemStatus(), true);
-							return (array_key_exists('error', $result)) ? $result['error']['msg'] : true;
-						} catch (Exception $e) {
-							return $e->getMessage();
-						}
-					}
-				}
-			} else {
-				return 'URL/s and/or Token/s not setup';
-			}
-			break;
-		case 'radarr':
-			if (!empty($GLOBALS['radarrURL']) && !empty($GLOBALS['radarrToken'])) {
-				$sonarrs = array();
-				$sonarrURLList = explode(',', $GLOBALS['radarrURL']);
-				$sonarrTokenList = explode(',', $GLOBALS['radarrToken']);
-				if (count($sonarrURLList) == count($sonarrTokenList)) {
-					foreach ($sonarrURLList as $key => $value) {
-						$sonarrs[$key] = array(
-							'url' => $value,
-							'token' => $sonarrTokenList[$key]
-						);
-					}
-					foreach ($sonarrs as $key => $value) {
-						try {
-							$sonarr = new Kryptonit3\Sonarr\Sonarr($value['url'], $value['token']);
-							$result = json_decode($sonarr->getSystemStatus(), true);
-							return (array_key_exists('error', $result)) ? $result['error']['msg'] : true;
-						} catch (Exception $e) {
-							return $e->getMessage();
-						}
-					}
-				}
-			} else {
-				return 'URL/s and/or Token/s not setup';
-			}
-			break;
-		case 'jdownloader':
-			if (!empty($GLOBALS['jdownloaderURL'])) {
-				$url = qualifyURL($GLOBALS['jdownloaderURL']);
-				try {
-					$options = (localURL($url)) ? array('verify' => false, 'timeout' => 30) : array('timeout' => 30);
-					$response = Requests::get($url, array(), $options);
-					if ($response->success) {
-						return true;
-					}
-				} catch (Requests_Exception $e) {
-					return $e->getMessage();
-				};
-			} else {
-				return 'URL and/or Token not setup';
-			}
-			break;
-		case 'sabnzbd':
-			if (!empty($GLOBALS['sabnzbdURL']) && !empty($GLOBALS['sabnzbdToken'])) {
-				$url = qualifyURL($GLOBALS['sabnzbdURL']);
-				$url = $url . '/api?mode=queue&output=json&apikey=' . $GLOBALS['sabnzbdToken'];
-				try {
-					$options = (localURL($url)) ? array('verify' => false) : array();
-					$response = Requests::get($url, array(), $options);
-					if ($response->success) {
-						return true;
-					}
-				} catch (Requests_Exception $e) {
-					return $e->getMessage();
-				};
-			} else {
-				return 'URL and/or Token not setup';
-			}
-			break;
-		case 'nzbget':
-			if (!empty($GLOBALS['nzbgetURL'])) {
-				$url = qualifyURL($GLOBALS['nzbgetURL']);
-				$url = $url . '/jsonrpc/listgroups';
-				try {
-					$options = (localURL($url)) ? array('verify' => false) : array();
-					if ($GLOBALS['nzbgetUsername'] !== '' && decrypt($GLOBALS['nzbgetPassword']) !== '') {
-						$credentials = array('auth' => new Requests_Auth_Basic(array($GLOBALS['nzbgetUsername'], decrypt($GLOBALS['nzbgetPassword']))));
-						$options = array_merge($options, $credentials);
-					}
-					$response = Requests::get($url, array(), $options);
-					if ($response->success) {
-						return true;
-					}
-				} catch (Requests_Exception $e) {
-					return $e->getMessage();
-				};
-			} else {
-				return 'URL and/or Username/Password not setup';
-			}
-			break;
-		case 'deluge':
-			if (!empty($GLOBALS['delugeURL']) && !empty($GLOBALS['delugePassword'])) {
-				try {
-					$deluge = new deluge($GLOBALS['delugeURL'], decrypt($GLOBALS['delugePassword']));
-					$torrents = $deluge->getTorrents(null, 'comment, download_payload_rate, eta, hash, is_finished, is_seed, message, name, paused, progress, queue, state, total_size, upload_payload_rate');
-					return true;
-				} catch (Exception $e) {
-					return $e->getMessage();
-				}
-			} else {
-				return 'URL and/or Password not setup';
-			}
-			break;
-		case 'rtorrent':
-			if (!empty($GLOBALS['rTorrentURL']) || !empty($GLOBALS['rTorrentURLOverride'])) {
-				try {
-					$digest = (empty($GLOBALS['rTorrentURLOverride'])) ? qualifyURL($GLOBALS['rTorrentURL'], true) : qualifyURL(checkOverrideURL($GLOBALS['rTorrentURL'], $GLOBALS['rTorrentURLOverride']), true);
-					$passwordInclude = ($GLOBALS['rTorrentUsername'] !== '' && $GLOBALS['rTorrentPassword'] !== '') ? $GLOBALS['rTorrentUsername'] . ':' . decrypt($GLOBALS['rTorrentPassword']) . "@" : '';
-					$extraPath = (strpos($GLOBALS['rTorrentURL'], '.php') !== false) ? '' : '/RPC2';
-					$extraPath = (empty($GLOBALS['rTorrentURLOverride'])) ? $extraPath : '';
-					$url = $digest['scheme'] . '://' . $passwordInclude . $digest['host'] . $digest['port'] . $digest['path'] . $extraPath;
-					$options = (localURL($url, $GLOBALS['rTorrentDisableCertCheck'])) ? array('verify' => false) : array();
-					if ($GLOBALS['rTorrentUsername'] !== '' && decrypt($GLOBALS['rTorrentPassword']) !== '') {
-						$credentials = array('auth' => new Requests_Auth_Digest(array($GLOBALS['rTorrentUsername'], decrypt($GLOBALS['rTorrentPassword']))));
-						$options = array_merge($options, $credentials);
-					}
-					$data = xmlrpc_encode_request("system.listMethods", null);
-					$response = Requests::post($url, array(), $data, $options);
-					if ($response->success) {
-						$methods = xmlrpc_decode(str_replace('i8>', 'i4>', $response->body));
-						if (count($methods) !== 0) {
-							return true;
-						}
-					}
-					return false;
-				} catch
-				(Requests_Exception $e) {
-					writeLog('error', 'rTorrent Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
-					return $e->getMessage();
-				};
-			}
-			break;
 		case 'ldap_login':
 			$username = $array['data']['data']['username'];
 			$password = $array['data']['data']['password'];

+ 1 - 3364
api/functions/homepage-functions.php

@@ -1,3369 +1,6 @@
 <?php
-//homepage order
-function homepageOrder()
-{
-	$hpOrderSearch = 'homepageOrder';
-	$homepageItems = array_filter($GLOBALS, function ($k) use ($hpOrderSearch) {
-		return strpos($k, $hpOrderSearch) !== false;
-	}, ARRAY_FILTER_USE_KEY);
-	asort($homepageItems);
-	return $homepageItems;
-}
-
-function buildHomepage()
-{
-	$homepageOrder = homepageOrder();
-	$homepageBuilt = '';
-	foreach ($homepageOrder as $key => $value) {
-		$homepageBuilt .= buildHomepageItem($key);
-	}
-	return $homepageBuilt;
-}
-
-function buildHomepageItem($homepageItem)
-{
-	$item = '<div id="' . $homepageItem . '">';
-	switch ($homepageItem) {
-		case 'homepageOrdercustomhtml':
-			if ($GLOBALS['homepagCustomHTMLoneEnabled'] && qualifyRequest($GLOBALS['homepagCustomHTMLoneAuth'])) {
-				$item .= ($GLOBALS['customHTMLone'] !== '') ? $GLOBALS['customHTMLone'] : '';
-			}
-			break;
-		case 'homepageOrdercustomhtmlTwo':
-			if ($GLOBALS['homepagCustomHTMLtwoEnabled'] && qualifyRequest($GLOBALS['homepagCustomHTMLtwoAuth'])) {
-				$item .= ($GLOBALS['customHTMLtwo'] !== '') ? $GLOBALS['customHTMLtwo'] : '';
-			}
-			break;
-		case 'homepageOrdernotice':
-			break;
-		case 'homepageOrdernoticeguest':
-			break;
-		case 'homepageOrderqBittorrent':
-			if ($GLOBALS['homepageqBittorrentEnabled'] && qualifyRequest($GLOBALS['homepageqBittorrentAuth'])) {
-				if ($GLOBALS['qBittorrentCombine']) {
-					$item .= '
-	                <script>
-	                // homepageOrderqBittorrent
-	                buildDownloaderCombined(\'qBittorrent\');
-	                homepageDownloader("qBittorrent", "' . $GLOBALS['homepageDownloadRefresh'] . '");
-	                // End homepageOrderqBittorrent
-	                </script>
-	                ';
-				} else {
-					$item .= '<div class="white-box"><h2 class="text-center" lang="en">Loading Download Queue...</h2></div>';
-					$item .= '
-	                <script>
-	                // homepageOrderqBittorrent
-	                $("#' . $homepageItem . '").html(buildDownloader("qBittorrent"));
-	                homepageDownloader("qBittorrent", "' . $GLOBALS['homepageDownloadRefresh'] . '");
-	                // End homepageOrderqBittorrent
-	                </script>
-	                ';
-				}
-			}
-			break;
-		case 'homepageOrderrTorrent':
-			if ($GLOBALS['homepagerTorrentEnabled'] && qualifyRequest($GLOBALS['homepagerTorrentAuth'])) {
-				if ($GLOBALS['rTorrentCombine']) {
-					$item .= '
-	                <script>
-	                // homepageOrderrTorrent
-	                buildDownloaderCombined(\'rTorrent\');
-	                homepageDownloader("rTorrent", "' . $GLOBALS['homepageDownloadRefresh'] . '");
-	                // End homepageOrderrTorrent
-	                </script>
-	                ';
-				} else {
-					$item .= '<div class="white-box"><h2 class="text-center" lang="en">Loading Download Queue...</h2></div>';
-					$item .= '
-	                <script>
-	                // homepageOrderrTorrent
-	                $("#' . $homepageItem . '").html(buildDownloader("rTorrent"));
-	                homepageDownloader("rTorrent", "' . $GLOBALS['homepageDownloadRefresh'] . '");
-	                // End homepageOrderrTorrent
-	                </script>
-	                ';
-				}
-			}
-			break;
-		case 'homepageOrderdeluge':
-			if ($GLOBALS['homepageDelugeEnabled'] && qualifyRequest($GLOBALS['homepageDelugeAuth'])) {
-				if ($GLOBALS['delugeCombine']) {
-					$item .= '
-					<script>
-					// Deluge
-					buildDownloaderCombined(\'deluge\');
-					homepageDownloader("deluge", "' . $GLOBALS['homepageDownloadRefresh'] . '");
-					// End Deluge
-					</script>
-					';
-				} else {
-					$item .= '<div class="white-box"><h2 class="text-center" lang="en">Loading Download Queue...</h2></div>';
-					$item .= '
-					<script>
-					// Deluge
-					$("#' . $homepageItem . '").html(buildDownloader("deluge"));
-					homepageDownloader("deluge", "' . $GLOBALS['homepageDownloadRefresh'] . '");
-					// End Deluge
-					</script>
-					';
-				}
-			}
-			break;
-		case 'homepageOrdertransmission':
-			if ($GLOBALS['homepageTransmissionEnabled'] && qualifyRequest($GLOBALS['homepageTransmissionAuth'])) {
-				if ($GLOBALS['transmissionCombine']) {
-					$item .= '
-					<script>
-					// Transmission
-					buildDownloaderCombined(\'transmission\');
-					homepageDownloader("transmission", "' . $GLOBALS['homepageDownloadRefresh'] . '");
-					// End Transmission
-					</script>
-					';
-				} else {
-					$item .= '<div class="white-box"><h2 class="text-center" lang="en">Loading Download Queue...</h2></div>';
-					$item .= '
-					<script>
-					// Transmission
-					$("#' . $homepageItem . '").html(buildDownloader("transmission"));
-					homepageDownloader("transmission", "' . $GLOBALS['homepageDownloadRefresh'] . '");
-					// End Transmission
-					</script>
-					';
-				}
-			}
-			break;
-		case 'homepageOrdernzbget':
-			if ($GLOBALS['homepageNzbgetEnabled'] && qualifyRequest($GLOBALS['homepageNzbgetAuth'])) {
-				if ($GLOBALS['nzbgetCombine']) {
-					$item .= '
-					<script>
-					// NZBGet
-					buildDownloaderCombined(\'nzbget\');
-					homepageDownloader("nzbget", "' . $GLOBALS['homepageDownloadRefresh'] . '");
-					// End NZBGet
-					</script>
-					';
-				} else {
-					$item .= '<div class="white-box"><h2 class="text-center" lang="en">Loading Download Queue...</h2></div>';
-					$item .= '
-					<script>
-					// NZBGet
-					$("#' . $homepageItem . '").html(buildDownloader("nzbget"));
-					homepageDownloader("nzbget", "' . $GLOBALS['homepageDownloadRefresh'] . '");
-					// End NZBGet
-					</script>
-					';
-				}
-			}
-			break;
-		case 'homepageOrderjdownloader':
-			if ($GLOBALS['homepageJdownloaderEnabled'] && qualifyRequest($GLOBALS['homepageJdownloaderAuth'])) {
-				if ($GLOBALS['jdownloaderCombine']) {
-					$item .= '
-					<script>
-					// JDownloader
-					buildDownloaderCombined(\'jdownloader\');
-					homepageDownloader("jdownloader", "' . $GLOBALS['homepageDownloadRefresh'] . '");
-					// End JDownloader
-					</script>
-					';
-				} else {
-					$item .= '<div class="white-box"><h2 class="text-center" lang="en">Loading Download Queue...</h2></div>';
-					$item .= '
-					<script>
-					// JDownloader
-					$("#' . $homepageItem . '").html(buildDownloader("jdownloader"));
-					homepageDownloader("jdownloader", "' . $GLOBALS['homepageDownloadRefresh'] . '");
-					// End JDownloader
-					</script>
-					';
-				}
-			}
-			break;
-		case 'homepageOrdersabnzbd':
-			if ($GLOBALS['homepageSabnzbdEnabled'] && qualifyRequest($GLOBALS['homepageSabnzbdAuth'])) {
-				if ($GLOBALS['sabnzbdCombine']) {
-					$item .= '
-					<script>
-					// SabNZBd
-					buildDownloaderCombined(\'sabnzbd\');
-					homepageDownloader("sabnzbd", "' . $GLOBALS['homepageDownloadRefresh'] . '");
-					// End SabNZBd
-					</script>
-					';
-				} else {
-					$item .= '<div class="white-box"><h2 class="text-center" lang="en">Loading Download Queue...</h2></div>';
-					$item .= '
-					<script>
-					// SabNZBd
-					$("#' . $homepageItem . '").html(buildDownloader("sabnzbd"));
-					homepageDownloader("sabnzbd", "' . $GLOBALS['homepageDownloadRefresh'] . '");
-					// End SabNZBd
-					</script>
-					';
-				}
-			}
-			break;
-		case 'homepageOrderplexnowplaying':
-			if ($GLOBALS['homepagePlexStreams']) {
-				$item .= '<div class="white-box"><h2 class="text-center" lang="en">Loading Now Playing...</h2></div>';
-				$item .= '
-				<script>
-				// Plex Stream
-				homepageStream("plex", "' . $GLOBALS['homepageStreamRefresh'] . '");
-				// End Plex Stream
-				</script>
-				';
-			}
-			break;
-		case 'homepageOrderplexrecent':
-			if ($GLOBALS['homepagePlexRecent']) {
-				$item .= '<div class="white-box"><h2 class="text-center" lang="en">Loading Recent...</h2></div>';
-				$item .= '
-				<script>
-				// Plex Recent
-				homepageRecent("plex", "' . $GLOBALS['homepageRecentRefresh'] . '");
-				// End Plex Recent
-				</script>
-				';
-			}
-			break;
-		case 'homepageOrderplexplaylist':
-			if ($GLOBALS['homepagePlexPlaylist']) {
-				$item .= '<div class="white-box"><h2 class="text-center" lang="en">Loading Playlists...</h2></div>';
-				$item .= '
-				<script>
-				// Plex Playlist
-				homepagePlaylist("plex");
-				// End Plex Playlist
-				</script>
-				';
-			}
-			break;
-		case 'homepageOrderembynowplaying':
-			if ($GLOBALS['homepageEmbyStreams'] && $GLOBALS['homepageEmbyEnabled']) {
-				$item .= '<div class="white-box"><h2 class="text-center" lang="en">Loading Now Playing...</h2></div>';
-				$item .= '
-				<script>
-				// Emby Stream
-				homepageStream("emby", "' . $GLOBALS['homepageStreamRefresh'] . '");
-				// End Emby Stream
-				</script>
-				';
-			}
-			break;
-		case 'homepageOrderembyrecent':
-			if ($GLOBALS['homepageEmbyRecent'] && $GLOBALS['homepageEmbyEnabled']) {
-				$item .= '<div class="white-box"><h2 class="text-center" lang="en">Loading Recent...</h2></div>';
-				$item .= '
-				<script>
-				// Emby Recent
-				homepageRecent("emby", "' . $GLOBALS['homepageRecentRefresh'] . '");
-				// End Emby Recent
-				</script>
-				';
-			}
-			break;
-		case 'homepageOrderombi':
-			if ($GLOBALS['homepageOmbiEnabled']) {
-				$item .= '<div class="white-box"><h2 class="text-center" lang="en">Loading Requests...</h2></div>';
-				$item .= '
-				<script>
-				// Ombi Requests
-				homepageRequests("' . $GLOBALS['ombiRefresh'] . '");
-				// End Ombi Requests
-				</script>
-				';
-			}
-			break;
-		case 'homepageOrdercalendar':
-			if ($GLOBALS['homepageLidarrEnabled'] && qualifyRequest($GLOBALS['homepageLidarrAuth']) || $GLOBALS['homepageSonarrEnabled'] && qualifyRequest($GLOBALS['homepageSonarrAuth']) || ($GLOBALS['homepageRadarrEnabled'] && qualifyRequest($GLOBALS['homepageRadarrAuth'])) || ($GLOBALS['homepageSickrageEnabled'] && qualifyRequest($GLOBALS['homepageSickrageAuth'])) || ($GLOBALS['homepageCouchpotatoEnabled'] && qualifyRequest($GLOBALS['homepageCouchpotatoAuth'])) || ($GLOBALS['homepageCalendarEnabled'] && qualifyRequest($GLOBALS['homepageCalendarAuth']) && $GLOBALS['calendariCal'] !== '')) {
-				$item .= '
-				<div id="calendar" class="fc fc-ltr m-b-30"></div>
-				<script>
-				// Calendar
-				homepageCalendar("' . $GLOBALS['calendarRefresh'] . '");
-				// End Calendar
-				</script>
-				';
-			}
-			break;
-		case 'homepageOrderhealthchecks':
-			if ($GLOBALS['homepageHealthChecksEnabled'] && qualifyRequest($GLOBALS['homepageHealthChecksAuth'])) {
-				$item .= '<div class="white-box"><h2 class="text-center" lang="en">Loading Health Checks...</h2></div>';
-				$item .= '
-				<script>
-				// Health Checks
-				homepageHealthChecks("' . $GLOBALS['healthChecksTags'] . '","' . $GLOBALS['homepageHealthChecksRefresh'] . '");
-				// End Health Checks
-				</script>
-				';
-			}
-			break;
-		case 'homepageOrderunifi':
-			if ($GLOBALS['homepageUnifiEnabled'] && qualifyRequest($GLOBALS['homepageUnifiAuth'])) {
-				$item .= '<div class="white-box"><h2 class="text-center" lang="en">Loading Unifi...</h2></div>';
-				$item .= '
-				<script>
-				// Unifi
-				homepageUnifi("' . $GLOBALS['homepageHealthChecksRefresh'] . '");
-				// End Unifi
-				</script>
-				';
-			}
-			break;
-		case 'homepageOrdertautulli':
-			if ($GLOBALS['homepageTautulliEnabled'] && qualifyRequest($GLOBALS['homepageTautulliAuth'])) {
-				$item .= '<div class="white-box"><h2 class="text-center" lang="en">Loading Tautulli...</h2></div>';
-				$item .= '
-				<script>
-				// Tautulli
-				homepageTautulli("' . $GLOBALS['homepageTautulliRefresh'] . '");
-				// End Tautulli
-				</script>
-				';
-			}
-			break;
-		case 'homepageOrderPihole':
-			if ($GLOBALS['homepagePiholeEnabled']) {
-				$item .= '<div class="white-box"><h2 class="text-center" lang="en">Loading Pi-hole Stats...</h2></div>';
-				$item .= '
-				<script>
-				// Pi-hole Stats
-				homepagePihole("' . $GLOBALS['homepagePiholeRefresh'] . '");
-				// End Pi-hole Stats
-				</script>
-				';
-			}
-			break;
-		case 'homepageOrderMonitorr':
-			if ($GLOBALS['homepageMonitorrEnabled']) {
-				$item .= '<div class="white-box"><h2 class="text-center" lang="en">Loading Monitorr...</h2></div>';
-				$item .= '
-				<script>
-				// Monitorr
-				homepageMonitorr("' . $GLOBALS['homepageMonitorrRefresh'] . '");
-				// End Monitorr
-				</script>
-				';
-			}
-			break;
-		case 'homepageOrderWeatherAndAir':
-			if ($GLOBALS['homepageWeatherAndAirEnabled']) {
-				$item .= '<div class="white-box"><h2 class="text-center" lang="en">Loading Weather And Air...</h2></div>';
-				$item .= '
-				<script>
-				// Weather And Air
-				homepageWeatherAndAir("' . $GLOBALS['homepageWeatherAndAirRefresh'] . '");
-				// End Weather And Air
-				</script>
-				';
-			}
-			break;
-		case 'homepageOrderSpeedtest':
-			if ($GLOBALS['homepageSpeedtestEnabled']) {
-				$item .= '<div class="white-box"><h2 class="text-center" lang="en">Loading Speedtest...</h2></div>';
-				$item .= '
-				<script>
-				// Speedtest
-				homepageSpeedtest("' . $GLOBALS['homepageSpeedtestRefresh'] . '");
-				// End Speedtest
-				</script>
-				';
-			}
-			break;
-		case 'homepageOrderNetdata':
-			if ($GLOBALS['homepageNetdataEnabled']) {
-				$item .= '<div class="white-box"><h2 class="text-center" lang="en">Loading Netdata...</h2></div>';
-				$item .= '
-				<script>
-				// Netdata
-				homepageNetdata("' . $GLOBALS['homepageNetdataRefresh'] . '");
-				// End Netdata
-				</script>
-				';
-			}
-			break;
-		case 'homepageOrderOctoprint':
-			if ($GLOBALS['homepageOctoprintEnabled']) {
-				$item .= '<div class="white-box"><h2 class="text-center" lang="en">Loading Octoprint...</h2></div>';
-				$item .= '
-				<script>
-				// Octoprint
-				homepageOctoprint("' . $GLOBALS['homepageOctoprintRefresh'] . '");
-				// End Octoprint
-				</script>
-				';
-			}
-			break;
-		case 'homepageOrderSonarrQueue':
-			if ($GLOBALS['homepageSonarrQueueEnabled'] && qualifyRequest($GLOBALS['homepageSonarrQueueAuth'])) {
-				if ($GLOBALS['homepageSonarrQueueCombine']) {
-					$item .= '
-					<script>
-					// Sonarr Queue
-					buildDownloaderCombined(\'sonarr\');
-					homepageDownloader("sonarr", "' . $GLOBALS['homepageSonarrQueueRefresh'] . '");
-					// End Sonarr Queue
-					</script>
-					';
-				} else {
-					$item .= '<div class="white-box"><h2 class="text-center" lang="en">Loading Download Queue...</h2></div>';
-					$item .= '
-					<script>
-					// Sonarr Queue
-					$("#' . $homepageItem . '").html(buildDownloader("sonarr"));
-					homepageDownloader("sonarr", "' . $GLOBALS['homepageSonarrQueueRefresh'] . '");
-					// End Sonarr Queue
-					</script>
-					';
-				}
-			}
-			break;
-		case 'homepageOrderRadarrQueue':
-			if ($GLOBALS['homepageRadarrQueueEnabled'] && qualifyRequest($GLOBALS['homepageRadarrQueueAuth'])) {
-				if ($GLOBALS['homepageRadarrQueueCombine']) {
-					$item .= '
-					<script>
-					// Radarr Queue
-					buildDownloaderCombined(\'radarr\');
-					homepageDownloader("radarr", "' . $GLOBALS['homepageRadarrQueueRefresh'] . '");
-					// End Radarr Queue
-					</script>
-					';
-				} else {
-					$item .= '<div class="white-box"><h2 class="text-center" lang="en">Loading Download Queue...</h2></div>';
-					$item .= '
-					<script>
-					// Radarr Queue
-					$("#' . $homepageItem . '").html(buildDownloader("radarr"));
-					homepageDownloader("radarr", "' . $GLOBALS['homepageRadarrQueueRefresh'] . '");
-					// End Radarr Queue
-					</script>
-					';
-				}
-			}
-			break;
-		default:
-			# code...
-			break;
-	}
-	return $item . '</div>';
-}
-
-function getHomepageList()
-{
-	$groups = groupSelect();
-	$ombiTvOptions = array(
-		array(
-			'name' => 'All Seasons',
-			'value' => 'all'
-		),
-		array(
-			'name' => 'First Season Only',
-			'value' => 'first'
-		),
-		array(
-			'name' => 'Last Season Only',
-			'value' => 'last'
-		),
-	);
-	$mediaServers = array(
-		array(
-			'name' => 'N/A',
-			'value' => ''
-		),
-		array(
-			'name' => 'Plex',
-			'value' => 'plex'
-		),
-		array(
-			'name' => 'Emby [Not Available]',
-			'value' => 'emby'
-		)
-	);
-	$limit = array(
-		array(
-			'name' => '1 Item',
-			'value' => '1'
-		),
-		array(
-			'name' => '2 Items',
-			'value' => '2'
-		),
-		array(
-			'name' => '3 Items',
-			'value' => '3'
-		),
-		array(
-			'name' => '4 Items',
-			'value' => '4'
-		),
-		array(
-			'name' => '5 Items',
-			'value' => '5'
-		),
-		array(
-			'name' => '6 Items',
-			'value' => '6'
-		),
-		array(
-			'name' => '7 Items',
-			'value' => '7'
-		),
-		array(
-			'name' => '8 Items',
-			'value' => '8'
-		),
-		array(
-			'name' => 'Unlimited',
-			'value' => '1000'
-		),
-	);
-	$day = array(
-		array(
-			'name' => 'Sunday',
-			'value' => '0'
-		),
-		array(
-			'name' => 'Monday',
-			'value' => '1'
-		),
-		array(
-			'name' => 'Tueday',
-			'value' => '2'
-		),
-		array(
-			'name' => 'Wednesday',
-			'value' => '3'
-		),
-		array(
-			'name' => 'Thursday',
-			'value' => '4'
-		),
-		array(
-			'name' => 'Friday',
-			'value' => '5'
-		),
-		array(
-			'name' => 'Saturday',
-			'value' => '6'
-		)
-	);
-	$calendarDefault = array(
-		array(
-			'name' => 'Month',
-			'value' => 'month'
-		),
-		array(
-			'name' => 'Day',
-			'value' => 'basicDay'
-		),
-		array(
-			'name' => 'Week',
-			'value' => 'basicWeek'
-		),
-		array(
-			'name' => 'List',
-			'value' => 'list'
-		)
-	);
-	$timeFormat = array(
-		array(
-			'name' => '6p',
-			'value' => 'h(:mm)t'
-		),
-		array(
-			'name' => '6:00p',
-			'value' => 'h:mmt'
-		),
-		array(
-			'name' => '6:00',
-			'value' => 'h:mm'
-		),
-		array(
-			'name' => '18',
-			'value' => 'H(:mm)'
-		),
-		array(
-			'name' => '18:00',
-			'value' => 'H:mm'
-		)
-	);
-	$rTorrentSortOptions = array(
-		array(
-			'name' => 'Date Desc',
-			'value' => 'dated'
-		),
-		array(
-			'name' => 'Date Asc',
-			'value' => 'datea'
-		),
-		array(
-			'name' => 'Hash Desc',
-			'value' => 'hashd'
-		),
-		array(
-			'name' => 'Hash Asc',
-			'value' => 'hasha'
-		),
-		array(
-			'name' => 'Name Desc',
-			'value' => 'named'
-		),
-		array(
-			'name' => 'Name Asc',
-			'value' => 'namea'
-		),
-		array(
-			'name' => 'Size Desc',
-			'value' => 'sized'
-		),
-		array(
-			'name' => 'Size Asc',
-			'value' => 'sizea'
-		),
-		array(
-			'name' => 'Label Desc',
-			'value' => 'labeld'
-		),
-		array(
-			'name' => 'Label Asc',
-			'value' => 'labela'
-		),
-		array(
-			'name' => 'Status Desc',
-			'value' => 'statusd'
-		),
-		array(
-			'name' => 'Status Asc',
-			'value' => 'statusa'
-		),
-	);
-	$qBittorrentApiOptions = array(
-		array(
-			'name' => 'V1',
-			'value' => '1'
-		),
-		array(
-			'name' => 'V2',
-			'value' => '2'
-		),
-	);
-	$qBittorrentSortOptions = array(
-		array(
-			'name' => 'Hash',
-			'value' => 'hash'
-		),
-		array(
-			'name' => 'Name',
-			'value' => 'name'
-		),
-		array(
-			'name' => 'Size',
-			'value' => 'size'
-		),
-		array(
-			'name' => 'Progress',
-			'value' => 'progress'
-		),
-		array(
-			'name' => 'Download Speed',
-			'value' => 'dlspeed'
-		),
-		array(
-			'name' => 'Upload Speed',
-			'value' => 'upspeed'
-		),
-		array(
-			'name' => 'Priority',
-			'value' => 'priority'
-		),
-		array(
-			'name' => 'Number of Seeds',
-			'value' => 'num_seeds'
-		),
-		array(
-			'name' => 'Number of Seeds in Swarm',
-			'value' => 'num_complete'
-		),
-		array(
-			'name' => 'Number of Leechers',
-			'value' => 'num_leechs'
-		),
-		array(
-			'name' => 'Number of Leechers in Swarm',
-			'value' => 'num_incomplete'
-		),
-		array(
-			'name' => 'Ratio',
-			'value' => 'ratio'
-		),
-		array(
-			'name' => 'ETA',
-			'value' => 'eta'
-		),
-		array(
-			'name' => 'State',
-			'value' => 'state'
-		),
-		array(
-			'name' => 'Category',
-			'value' => 'category'
-		)
-	);
-	$xmlStatus = (extension_loaded('xmlrpc')) ? 'Installed' : 'Not Installed';
-	return array(
-		array(
-			'name' => 'Calendar',
-			'enabled' => (strpos('personal', $GLOBALS['license']) !== false) ? true : false,
-			'image' => 'plugins/images/tabs/calendar.png',
-			'category' => 'HOMEPAGE',
-			'settings' => array(
-				'Enable' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'homepageCalendarEnabled',
-						'label' => 'Enable iCal',
-						'value' => $GLOBALS['homepageCalendarEnabled']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageCalendarAuth',
-						'label' => 'Minimum Authentication',
-						'value' => $GLOBALS['homepageCalendarAuth'],
-						'options' => $groups
-					),
-					array(
-						'type' => 'input',
-						'name' => 'calendariCal',
-						'label' => 'iCal URL\'s',
-						'value' => $GLOBALS['calendariCal'],
-						'placeholder' => 'separate by comma\'s'
-					),
-				),
-				'Misc Options' => array(
-					array(
-						'type' => 'number',
-						'name' => 'calendarStart',
-						'label' => '# of Days Before',
-						'value' => $GLOBALS['calendarStart'],
-						'placeholder' => ''
-					),
-					array(
-						'type' => 'number',
-						'name' => 'calendarEnd',
-						'label' => '# of Days After',
-						'value' => $GLOBALS['calendarEnd'],
-						'placeholder' => ''
-					),
-					array(
-						'type' => 'select',
-						'name' => 'calendarFirstDay',
-						'label' => 'Start Day',
-						'value' => $GLOBALS['calendarFirstDay'],
-						'options' => $day
-					),
-					array(
-						'type' => 'select',
-						'name' => 'calendarDefault',
-						'label' => 'Default View',
-						'value' => $GLOBALS['calendarDefault'],
-						'options' => $calendarDefault
-					),
-					array(
-						'type' => 'select',
-						'name' => 'calendarTimeFormat',
-						'label' => 'Time Format',
-						'value' => $GLOBALS['calendarTimeFormat'],
-						'options' => $timeFormat
-					),
-					array(
-						'type' => 'select',
-						'name' => 'calendarLimit',
-						'label' => 'Items Per Day',
-						'value' => $GLOBALS['calendarLimit'],
-						'options' => $limit
-					),
-					array(
-						'type' => 'select',
-						'name' => 'calendarRefresh',
-						'label' => 'Refresh Seconds',
-						'value' => $GLOBALS['calendarRefresh'],
-						'options' => optionTime()
-					)
-				),
-			)
-		),
-		array(
-			'name' => 'Plex',
-			'enabled' => (strpos('personal', $GLOBALS['license']) !== false) ? true : false,
-			'image' => 'plugins/images/tabs/plex.png',
-			'category' => 'Media Server',
-			//'license' => $GLOBALS['license'],
-			'settings' => array(
-				'Enable' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'homepagePlexEnabled',
-						'label' => 'Enable',
-						'value' => $GLOBALS['homepagePlexEnabled']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepagePlexAuth',
-						'label' => 'Minimum Authentication',
-						'value' => $GLOBALS['homepagePlexAuth'],
-						'options' => $groups
-					)
-				),
-				'Connection' => array(
-					array(
-						'type' => 'input',
-						'name' => 'plexURL',
-						'label' => 'URL',
-						'value' => $GLOBALS['plexURL'],
-						'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
-						'placeholder' => 'http(s)://hostname:port'
-					),
-					array(
-						'type' => 'password-alt',
-						'name' => 'plexToken',
-						'label' => 'Token',
-						'value' => $GLOBALS['plexToken']
-					),
-					array(
-						'type' => 'password-alt',
-						'name' => 'plexID',
-						'label' => 'Plex Machine',
-						'value' => $GLOBALS['plexID']
-					)
-				),
-				'Active Streams' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'homepagePlexStreams',
-						'label' => 'Enable',
-						'value' => $GLOBALS['homepagePlexStreams']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepagePlexStreamsAuth',
-						'label' => 'Minimum Authorization',
-						'value' => $GLOBALS['homepagePlexStreamsAuth'],
-						'options' => $groups
-					),
-					array(
-						'type' => 'switch',
-						'name' => 'homepageShowStreamNames',
-						'label' => 'User Information',
-						'value' => $GLOBALS['homepageShowStreamNames']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageShowStreamNamesAuth',
-						'label' => 'Minimum Authorization',
-						'value' => $GLOBALS['homepageShowStreamNamesAuth'],
-						'options' => $groups
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageStreamRefresh',
-						'label' => 'Refresh Seconds',
-						'value' => $GLOBALS['homepageStreamRefresh'],
-						'options' => optionTime()
-					),
-				),
-				'Recent Items' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'homepagePlexRecent',
-						'label' => 'Enable',
-						'value' => $GLOBALS['homepagePlexRecent']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepagePlexRecentAuth',
-						'label' => 'Minimum Authorization',
-						'value' => $GLOBALS['homepagePlexRecentAuth'],
-						'options' => $groups
-					),
-					array(
-						'type' => 'number',
-						'name' => 'homepageRecentLimit',
-						'label' => 'Item Limit',
-						'value' => $GLOBALS['homepageRecentLimit'],
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageRecentRefresh',
-						'label' => 'Refresh Seconds',
-						'value' => $GLOBALS['homepageRecentRefresh'],
-						'options' => optionTime()
-					),
-				),
-				'Media Search' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'mediaSearch',
-						'label' => 'Enable',
-						'value' => $GLOBALS['mediaSearch']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'mediaSearchAuth',
-						'label' => 'Minimum Authorization',
-						'value' => $GLOBALS['mediaSearchAuth'],
-						'options' => $groups
-					),
-					array(
-						'type' => 'select',
-						'name' => 'mediaSearchType',
-						'label' => 'Media Server',
-						'value' => $GLOBALS['mediaSearchType'],
-						'options' => $mediaServers
-					),
-				),
-				'Playlists' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'homepagePlexPlaylist',
-						'label' => 'Enable',
-						'value' => $GLOBALS['homepagePlexPlaylist']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepagePlexPlaylistAuth',
-						'label' => 'Minimum Authorization',
-						'value' => $GLOBALS['homepagePlexPlaylistAuth'],
-						'options' => $groups
-					),
-				),
-				'Misc Options' => array(
-					array(
-						'type' => 'input',
-						'name' => 'plexTabName',
-						'label' => 'Plex Tab Name',
-						'value' => $GLOBALS['plexTabName'],
-						'placeholder' => 'Only use if you have Plex in a reverse proxy'
-					),
-					array(
-						'type' => 'input',
-						'name' => 'plexTabURL',
-						'label' => 'Plex Tab WAN URL',
-						'value' => $GLOBALS['plexTabURL'],
-						'placeholder' => 'http(s)://hostname:port'
-					),
-					array(
-						'type' => 'select',
-						'name' => 'cacheImageSize',
-						'label' => 'Image Cache Size',
-						'value' => $GLOBALS['cacheImageSize'],
-						'options' => array(
-							array(
-								'name' => 'Low',
-								'value' => '.5'
-							),
-							array(
-								'name' => '1x',
-								'value' => '1'
-							),
-							array(
-								'name' => '2x',
-								'value' => '2'
-							),
-							array(
-								'name' => '3x',
-								'value' => '3'
-							)
-						)
-					)
-				),
-				'Test Connection' => array(
-					array(
-						'type' => 'blank',
-						'label' => 'Please Save before Testing'
-					),
-					array(
-						'type' => 'button',
-						'label' => '',
-						'icon' => 'fa fa-flask',
-						'class' => 'pull-right',
-						'text' => 'Test Connection',
-						'attr' => 'onclick="testAPIConnection(\'plex\')"'
-					),
-				)
-			)
-		),
-		array(
-			'name' => 'Emby-Jellyfin',
-			'enabled' => (strpos('personal', $GLOBALS['license']) !== false) ? true : false,
-			'image' => 'plugins/images/tabs/emby.png',
-			'category' => 'Media Server',
-			'settings' => array(
-				'Enable' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'homepageEmbyEnabled',
-						'label' => 'Enable',
-						'value' => $GLOBALS['homepageEmbyEnabled']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageEmbyAuth',
-						'label' => 'Minimum Authentication',
-						'value' => $GLOBALS['homepageEmbyAuth'],
-						'options' => $groups
-					)
-				),
-				'Connection' => array(
-					array(
-						'type' => 'input',
-						'name' => 'embyURL',
-						'label' => 'URL',
-						'value' => $GLOBALS['embyURL'],
-						'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
-						'placeholder' => 'http(s)://hostname:port'
-					),
-					array(
-						'type' => 'password-alt',
-						'name' => 'embyToken',
-						'label' => 'Token',
-						'value' => $GLOBALS['embyToken']
-					),
-					array(
-						'type' => 'switch',
-						'name' => 'homepageJellyfinInstead',
-						'label' => 'Jellyfin',
-						'value' => $GLOBALS['homepageJellyfinInstead']
-					)
-				),
-				'Active Streams' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'homepageEmbyStreams',
-						'label' => 'Enable',
-						'value' => $GLOBALS['homepageEmbyStreams']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageEmbyStreamsAuth',
-						'label' => 'Minimum Authorization',
-						'value' => $GLOBALS['homepageEmbyStreamsAuth'],
-						'options' => $groups
-					),
-					array(
-						'type' => 'switch',
-						'name' => 'homepageShowStreamNames',
-						'label' => 'User Information',
-						'value' => $GLOBALS['homepageShowStreamNames']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageShowStreamNamesAuth',
-						'label' => 'Minimum Authorization',
-						'value' => $GLOBALS['homepageShowStreamNamesAuth'],
-						'options' => $groups
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageStreamRefresh',
-						'label' => 'Refresh Seconds',
-						'value' => $GLOBALS['homepageStreamRefresh'],
-						'options' => optionTime()
-					),
-				),
-				'Recent Items' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'homepageEmbyRecent',
-						'label' => 'Enable',
-						'value' => $GLOBALS['homepageEmbyRecent']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageEmbyRecentAuth',
-						'label' => 'Minimum Authorization',
-						'value' => $GLOBALS['homepageEmbyRecentAuth'],
-						'options' => $groups
-					),
-					array(
-						'type' => 'number',
-						'name' => 'homepageRecentLimit',
-						'label' => 'Item Limit',
-						'value' => $GLOBALS['homepageRecentLimit'],
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageRecentRefresh',
-						'label' => 'Refresh Seconds',
-						'value' => $GLOBALS['homepageRecentRefresh'],
-						'options' => optionTime()
-					),
-				),
-				'Misc Options' => array(
-					array(
-						'type' => 'input',
-						'name' => 'embyTabName',
-						'label' => 'Emby Tab Name',
-						'value' => $GLOBALS['embyTabName'],
-						'placeholder' => 'Only use if you have Emby in a reverse proxy'
-					),
-					array(
-						'type' => 'input',
-						'name' => 'embyTabURL',
-						'label' => 'Emby Tab WAN URL',
-						'value' => $GLOBALS['embyTabURL'],
-						'placeholder' => 'http(s)://hostname:port'
-					),
-					array(
-						'type' => 'select',
-						'name' => 'cacheImageSize',
-						'label' => 'Image Cache Size',
-						'value' => $GLOBALS['cacheImageSize'],
-						'options' => array(
-							array(
-								'name' => 'Low',
-								'value' => '.5'
-							),
-							array(
-								'name' => '1x',
-								'value' => '1'
-							),
-							array(
-								'name' => '2x',
-								'value' => '2'
-							),
-							array(
-								'name' => '3x',
-								'value' => '3'
-							)
-						)
-					)
-				)
-			)
-		),
-		array(
-			'name' => 'JDownloader',
-			'enabled' => (strpos('personal', $GLOBALS['license']) !== false) ? true : false,
-			'image' => 'plugins/images/tabs/jdownloader.png',
-			'category' => 'Downloader',
-			'settings' => array(
-				'custom' => '
-				<div class="row">
-                    <div class="col-lg-12">
-                        <div class="panel panel-info">
-                            <div class="panel-heading">
-								<span lang="en">Notice</span>
-                            </div>
-                            <div class="panel-wrapper collapse in" aria-expanded="true">
-                                <div class="panel-body">
-									<ul class="list-icons">
-                                        <li><i class="fa fa-chevron-right text-danger"></i> <a href="https://pypi.org/project/myjd-api/" target="_blank">Download [myjd-api] Module</a></li>
-                                        <li><i class="fa fa-chevron-right text-danger"></i> Add <b>/api/myjd</b> to the URL if you are using <a href="https://pypi.org/project/RSScrawler/" target="_blank">RSScrawler</a></li>
-                                    </ul>
-                                </div>
-                            </div>
-                        </div>
-                    </div>
-				</div>
-				',
-				'Enable' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'homepageJdownloaderEnabled',
-						'label' => 'Enable',
-						'value' => $GLOBALS['homepageJdownloaderEnabled']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageJdownloaderAuth',
-						'label' => 'Minimum Authentication',
-						'value' => $GLOBALS['homepageJdownloaderAuth'],
-						'options' => $groups
-					)
-				),
-				'Connection' => array(
-					array(
-						'type' => 'input',
-						'name' => 'jdownloaderURL',
-						'label' => 'URL',
-						'value' => $GLOBALS['jdownloaderURL'],
-						'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
-						'placeholder' => 'http(s)://hostname:port'
-					)
-				),
-				'Misc Options' => array(
-					array(
-						'type' => 'select',
-						'name' => 'homepageDownloadRefresh',
-						'label' => 'Refresh Seconds',
-						'value' => $GLOBALS['homepageDownloadRefresh'],
-						'options' => optionTime()
-					),
-					array(
-						'type' => 'switch',
-						'name' => 'jdownloaderCombine',
-						'label' => 'Add to Combined Downloader',
-						'value' => $GLOBALS['jdownloaderCombine']
-					),
-				),
-				'Test Connection' => array(
-					array(
-						'type' => 'blank',
-						'label' => 'Please Save before Testing'
-					),
-					array(
-						'type' => 'button',
-						'label' => '',
-						'icon' => 'fa fa-flask',
-						'class' => 'pull-right',
-						'text' => 'Test Connection',
-						'attr' => 'onclick="testAPIConnection(\'jdownloader\')"'
-					),
-				)
-			)
-		),
-		array(
-			'name' => 'SabNZBD',
-			'enabled' => (strpos('personal', $GLOBALS['license']) !== false) ? true : false,
-			'image' => 'plugins/images/tabs/sabnzbd.png',
-			'category' => 'Downloader',
-			'settings' => array(
-				'Enable' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'homepageSabnzbdEnabled',
-						'label' => 'Enable',
-						'value' => $GLOBALS['homepageSabnzbdEnabled']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageSabnzbdAuth',
-						'label' => 'Minimum Authentication',
-						'value' => $GLOBALS['homepageSabnzbdAuth'],
-						'options' => $groups
-					)
-				),
-				'Connection' => array(
-					array(
-						'type' => 'input',
-						'name' => 'sabnzbdURL',
-						'label' => 'URL',
-						'value' => $GLOBALS['sabnzbdURL'],
-						'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
-						'placeholder' => 'http(s)://hostname:port'
-					),
-					array(
-						'type' => 'password-alt',
-						'name' => 'sabnzbdToken',
-						'label' => 'Token',
-						'value' => $GLOBALS['sabnzbdToken']
-					)
-				),
-				'Misc Options' => array(
-					array(
-						'type' => 'select',
-						'name' => 'homepageDownloadRefresh',
-						'label' => 'Refresh Seconds',
-						'value' => $GLOBALS['homepageDownloadRefresh'],
-						'options' => optionTime()
-					),
-					array(
-						'type' => 'switch',
-						'name' => 'sabnzbdCombine',
-						'label' => 'Add to Combined Downloader',
-						'value' => $GLOBALS['sabnzbdCombine']
-					),
-				),
-				'Test Connection' => array(
-					array(
-						'type' => 'blank',
-						'label' => 'Please Save before Testing'
-					),
-					array(
-						'type' => 'button',
-						'label' => '',
-						'icon' => 'fa fa-flask',
-						'class' => 'pull-right',
-						'text' => 'Test Connection',
-						'attr' => 'onclick="testAPIConnection(\'sabnzbd\')"'
-					),
-				)
-			)
-		),
-		array(
-			'name' => 'NZBGet',
-			'enabled' => (strpos('personal', $GLOBALS['license']) !== false) ? true : false,
-			'image' => 'plugins/images/tabs/nzbget.png',
-			'category' => 'Downloader',
-			'settings' => array(
-				'Enable' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'homepageNzbgetEnabled',
-						'label' => 'Enable',
-						'value' => $GLOBALS['homepageNzbgetEnabled']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageNzbgetAuth',
-						'label' => 'Minimum Authentication',
-						'value' => $GLOBALS['homepageNzbgetAuth'],
-						'options' => $groups
-					)
-				),
-				'Connection' => array(
-					array(
-						'type' => 'input',
-						'name' => 'nzbgetURL',
-						'label' => 'URL',
-						'value' => $GLOBALS['nzbgetURL'],
-						'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
-						'placeholder' => 'http(s)://hostname:port'
-					),
-					array(
-						'type' => 'input',
-						'name' => 'nzbgetUsername',
-						'label' => 'Username',
-						'value' => $GLOBALS['nzbgetUsername']
-					),
-					array(
-						'type' => 'password',
-						'name' => 'nzbgetPassword',
-						'label' => 'Password',
-						'value' => $GLOBALS['nzbgetPassword']
-					)
-				),
-				'Misc Options' => array(
-					array(
-						'type' => 'select',
-						'name' => 'homepageDownloadRefresh',
-						'label' => 'Refresh Seconds',
-						'value' => $GLOBALS['homepageDownloadRefresh'],
-						'options' => optionTime()
-					),
-					array(
-						'type' => 'switch',
-						'name' => 'nzbgetCombine',
-						'label' => 'Add to Combined Downloader',
-						'value' => $GLOBALS['nzbgetCombine']
-					),
-				),
-				'Test Connection' => array(
-					array(
-						'type' => 'blank',
-						'label' => 'Please Save before Testing'
-					),
-					array(
-						'type' => 'button',
-						'label' => '',
-						'icon' => 'fa fa-flask',
-						'class' => 'pull-right',
-						'text' => 'Test Connection',
-						'attr' => 'onclick="testAPIConnection(\'nzbget\')"'
-					),
-				)
-			)
-		),
-		array(
-			'name' => 'Transmission',
-			'enabled' => (strpos('personal', $GLOBALS['license']) !== false) ? true : false,
-			'image' => 'plugins/images/tabs/transmission.png',
-			'category' => 'Downloader',
-			'settings' => array(
-				'Enable' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'homepageTransmissionEnabled',
-						'label' => 'Enable',
-						'value' => $GLOBALS['homepageTransmissionEnabled']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageTransmissionAuth',
-						'label' => 'Minimum Authentication',
-						'value' => $GLOBALS['homepageTransmissionAuth'],
-						'options' => $groups
-					)
-				),
-				'Connection' => array(
-					array(
-						'type' => 'input',
-						'name' => 'transmissionURL',
-						'label' => 'URL',
-						'value' => $GLOBALS['transmissionURL'],
-						'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
-						'placeholder' => 'http(s)://hostname:port'
-					),
-					array(
-						'type' => 'input',
-						'name' => 'transmissionUsername',
-						'label' => 'Username',
-						'value' => $GLOBALS['transmissionUsername']
-					),
-					array(
-						'type' => 'password',
-						'name' => 'transmissionPassword',
-						'label' => 'Password',
-						'value' => $GLOBALS['transmissionPassword']
-					)
-				),
-				'Misc Options' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'transmissionHideSeeding',
-						'label' => 'Hide Seeding',
-						'value' => $GLOBALS['transmissionHideSeeding']
-					), array(
-						'type' => 'switch',
-						'name' => 'transmissionHideCompleted',
-						'label' => 'Hide Completed',
-						'value' => $GLOBALS['transmissionHideCompleted']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageDownloadRefresh',
-						'label' => 'Refresh Seconds',
-						'value' => $GLOBALS['homepageDownloadRefresh'],
-						'options' => optionTime()
-					),
-					array(
-						'type' => 'switch',
-						'name' => 'transmissionCombine',
-						'label' => 'Add to Combined Downloader',
-						'value' => $GLOBALS['transmissionCombine']
-					),
-				)
-			)
-		),
-		array(
-			'name' => 'qBittorrent',
-			'enabled' => (strpos('personal', $GLOBALS['license']) !== false) ? true : false,
-			'image' => 'plugins/images/tabs/qBittorrent.png',
-			'category' => 'Downloader',
-			'settings' => array(
-				'Enable' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'homepageqBittorrentEnabled',
-						'label' => 'Enable',
-						'value' => $GLOBALS['homepageqBittorrentEnabled']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageqBittorrentAuth',
-						'label' => 'Minimum Authentication',
-						'value' => $GLOBALS['homepageqBittorrentAuth'],
-						'options' => $groups
-					)
-				),
-				'Connection' => array(
-					array(
-						'type' => 'input',
-						'name' => 'qBittorrentURL',
-						'label' => 'URL',
-						'value' => $GLOBALS['qBittorrentURL'],
-						'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
-						'placeholder' => 'http(s)://hostname:port'
-					),
-					array(
-						'type' => 'select',
-						'name' => 'qBittorrentApiVersion',
-						'label' => 'API Version',
-						'value' => $GLOBALS['qBittorrentApiVersion'],
-						'options' => $qBittorrentApiOptions
-					),
-					array(
-						'type' => 'input',
-						'name' => 'qBittorrentUsername',
-						'label' => 'Username',
-						'value' => $GLOBALS['qBittorrentUsername']
-					),
-					array(
-						'type' => 'password',
-						'name' => 'qBittorrentPassword',
-						'label' => 'Password',
-						'value' => $GLOBALS['qBittorrentPassword']
-					)
-				),
-				'Misc Options' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'qBittorrentHideSeeding',
-						'label' => 'Hide Seeding',
-						'value' => $GLOBALS['qBittorrentHideSeeding']
-					),
-					array(
-						'type' => 'switch',
-						'name' => 'qBittorrentHideCompleted',
-						'label' => 'Hide Completed',
-						'value' => $GLOBALS['qBittorrentHideCompleted']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'qBittorrentSortOrder',
-						'label' => 'Order',
-						'value' => $GLOBALS['qBittorrentSortOrder'],
-						'options' => $qBittorrentSortOptions
-					), array(
-						'type' => 'switch',
-						'name' => 'qBittorrentReverseSorting',
-						'label' => 'Reverse Sorting',
-						'value' => $GLOBALS['qBittorrentReverseSorting']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageDownloadRefresh',
-						'label' => 'Refresh Seconds',
-						'value' => $GLOBALS['homepageDownloadRefresh'],
-						'options' => optionTime()
-					),
-					array(
-						'type' => 'switch',
-						'name' => 'qBittorrentCombine',
-						'label' => 'Add to Combined Downloader',
-						'value' => $GLOBALS['qBittorrentCombine']
-					),
-				)
-			)
-		),
-		array(
-			'name' => 'rTorrent',
-			'enabled' => (strpos('personal', $GLOBALS['license']) !== false) ? true : false,
-			'image' => 'plugins/images/tabs/rTorrent.png',
-			'category' => 'Downloader',
-			'settings' => array(
-				'FYI' => array(
-					array(
-						'type' => 'html',
-						'label' => '',
-						'override' => 12,
-						'html' => '
-						<div class="row">
-						    <div class="col-lg-12">
-						        <div class="panel panel-info">
-						            <div class="panel-heading">
-						                <span lang="en">ATTENTION</span>
-						            </div>
-						            <div class="panel-wrapper collapse in" aria-expanded="true">
-						                <div class="panel-body">
-						                	<h4 lang="en">This module requires XMLRPC</h4>
-						                    <span lang="en">Status: [ <b>' . $xmlStatus . '</b> ]</span>
-						                    <br/></br>
-						                    <span lang="en">
-						                    	<h4><b>Note about API URL</b></h4>
-						                    	Organizr appends the url with <code>/RPC2</code> unless the URL ends in <code>.php</code><br/>
-						                    	<h5>Possible URLs:</h5>
-						                    	<li>http://localhost:8080</li>
-						                    	<li>https://domain.site/xmlrpc.php</li>
-						                    	<li>https://seedbox.site/rutorrent/plugins/httprpc/action.php</li>
-						                    </span>
-						                </div>
-						            </div>
-						        </div>
-						    </div>
-						</div>
-						'
-					)
-				),
-				'Enable' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'homepagerTorrentEnabled',
-						'label' => 'Enable',
-						'value' => $GLOBALS['homepagerTorrentEnabled']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepagerTorrentAuth',
-						'label' => 'Minimum Authentication',
-						'value' => $GLOBALS['homepagerTorrentAuth'],
-						'options' => $groups
-					)
-				),
-				'Connection' => array(
-					array(
-						'type' => 'input',
-						'name' => 'rTorrentURL',
-						'label' => 'URL',
-						'value' => $GLOBALS['rTorrentURL'],
-						'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
-						'placeholder' => 'http(s)://hostname:port'
-					),
-					array(
-						'type' => 'input',
-						'name' => 'rTorrentURLOverride',
-						'label' => 'rTorrent API URL Override',
-						'value' => $GLOBALS['rTorrentURLOverride'],
-						'help' => 'Only use if you cannot connect.  Please make sure to use local IP address and port - You also may use local dns name too.',
-						'placeholder' => 'http(s)://hostname:port/xmlrpc'
-					),
-					array(
-						'type' => 'input',
-						'name' => 'rTorrentUsername',
-						'label' => 'Username',
-						'value' => $GLOBALS['rTorrentUsername']
-					),
-					array(
-						'type' => 'password',
-						'name' => 'rTorrentPassword',
-						'label' => 'Password',
-						'value' => $GLOBALS['rTorrentPassword']
-					),
-					array(
-						'type' => 'switch',
-						'name' => 'rTorrentDisableCertCheck',
-						'label' => 'Disable Certificate Check',
-						'value' => $GLOBALS['rTorrentDisableCertCheck']
-					),
-				),
-				'Misc Options' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'rTorrentHideSeeding',
-						'label' => 'Hide Seeding',
-						'value' => $GLOBALS['rTorrentHideSeeding']
-					), array(
-						'type' => 'switch',
-						'name' => 'rTorrentHideCompleted',
-						'label' => 'Hide Completed',
-						'value' => $GLOBALS['rTorrentHideCompleted']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'rTorrentSortOrder',
-						'label' => 'Order',
-						'value' => $GLOBALS['rTorrentSortOrder'],
-						'options' => $rTorrentSortOptions
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageDownloadRefresh',
-						'label' => 'Refresh Seconds',
-						'value' => $GLOBALS['homepageDownloadRefresh'],
-						'options' => optionTime()
-					),
-					array(
-						'type' => 'number',
-						'name' => 'rTorrentLimit',
-						'label' => 'Item Limit',
-						'value' => $GLOBALS['rTorrentLimit'],
-					),
-					array(
-						'type' => 'switch',
-						'name' => 'rTorrentCombine',
-						'label' => 'Add to Combined Downloader',
-						'value' => $GLOBALS['rTorrentCombine']
-					),
-				),
-				'Test Connection' => array(
-					array(
-						'type' => 'blank',
-						'label' => 'Please Save before Testing'
-					),
-					array(
-						'type' => 'button',
-						'label' => '',
-						'icon' => 'fa fa-flask',
-						'class' => 'pull-right',
-						'text' => 'Test Connection',
-						'attr' => 'onclick="testAPIConnection(\'rtorrent\')"'
-					),
-				)
-			)
-		),
-		array(
-			'name' => 'Deluge',
-			'enabled' => (strpos('personal', $GLOBALS['license']) !== false) ? true : false,
-			'image' => 'plugins/images/tabs/deluge.png',
-			'category' => 'Downloader',
-			'settings' => array(
-				'custom' => '
-				<div class="row">
-                    <div class="col-lg-12">
-                        <div class="panel panel-info">
-                            <div class="panel-heading">
-								<span lang="en">Notice</span>
-                            </div>
-                            <div class="panel-wrapper collapse in" aria-expanded="true">
-                                <div class="panel-body">
-									<ul class="list-icons">
-                                        <li><i class="fa fa-chevron-right text-danger"></i> <a href="https://github.com/idlesign/deluge-webapi/tree/master/dist" target="_blank">Download Plugin</a></li>
-                                        <li><i class="fa fa-chevron-right text-danger"></i> Open Deluge Web UI, go to "Preferences -> Plugins -> Install plugin" and choose egg file.</li>
-                                        <li><i class="fa fa-chevron-right text-danger"></i> Activate WebAPI plugin </li>
-                                    </ul>
-                                </div>
-                            </div>
-                        </div>
-                    </div>
-				</div>
-				',
-				'Enable' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'homepageDelugeEnabled',
-						'label' => 'Enable',
-						'value' => $GLOBALS['homepageDelugeEnabled']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageDelugeAuth',
-						'label' => 'Minimum Authentication',
-						'value' => $GLOBALS['homepageDelugeAuth'],
-						'options' => $groups
-					)
-				),
-				'Connection' => array(
-					array(
-						'type' => 'input',
-						'name' => 'delugeURL',
-						'label' => 'URL',
-						'value' => $GLOBALS['delugeURL'],
-						'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
-						'placeholder' => 'http(s)://hostname:port'
-					),
-					array(
-						'type' => 'password',
-						'name' => 'delugePassword',
-						'label' => 'Password',
-						'value' => $GLOBALS['delugePassword']
-					)
-				),
-				'Misc Options' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'delugeHideSeeding',
-						'label' => 'Hide Seeding',
-						'value' => $GLOBALS['delugeHideSeeding']
-					), array(
-						'type' => 'switch',
-						'name' => 'delugeHideCompleted',
-						'label' => 'Hide Completed',
-						'value' => $GLOBALS['delugeHideCompleted']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageDownloadRefresh',
-						'label' => 'Refresh Seconds',
-						'value' => $GLOBALS['homepageDownloadRefresh'],
-						'options' => optionTime()
-					),
-					array(
-						'type' => 'switch',
-						'name' => 'delugeCombine',
-						'label' => 'Add to Combined Downloader',
-						'value' => $GLOBALS['delugeCombine']
-					),
-				),
-				'Test Connection' => array(
-					array(
-						'type' => 'blank',
-						'label' => 'Please Save before Testing'
-					),
-					array(
-						'type' => 'button',
-						'label' => '',
-						'icon' => 'fa fa-flask',
-						'class' => 'pull-right',
-						'text' => 'Test Connection',
-						'attr' => 'onclick="testAPIConnection(\'deluge\')"'
-					),
-				)
-			)
-		),
-		array(
-			'name' => 'Sonarr',
-			'enabled' => (strpos('personal', $GLOBALS['license']) !== false) ? true : false,
-			'image' => 'plugins/images/tabs/sonarr.png',
-			'category' => 'PVR',
-			'settings' => array(
-				'Enable' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'homepageSonarrEnabled',
-						'label' => 'Enable',
-						'value' => $GLOBALS['homepageSonarrEnabled']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageSonarrAuth',
-						'label' => 'Minimum Authentication',
-						'value' => $GLOBALS['homepageSonarrAuth'],
-						'options' => $groups
-					)
-				),
-				'Connection' => array(
-					array(
-						'type' => 'input',
-						'name' => 'sonarrURL',
-						'label' => 'URL',
-						'value' => $GLOBALS['sonarrURL'],
-						'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
-						'placeholder' => 'http(s)://hostname:port'
-					),
-					array(
-						'type' => 'password-alt',
-						'name' => 'sonarrToken',
-						'label' => 'Token',
-						'value' => $GLOBALS['sonarrToken']
-					)
-				),
-				'Queue' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'homepageSonarrQueueEnabled',
-						'label' => 'Enable',
-						'value' => $GLOBALS['homepageSonarrQueueEnabled']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageSonarrQueueAuth',
-						'label' => 'Minimum Authentication',
-						'value' => $GLOBALS['homepageSonarrQueueAuth'],
-						'options' => $groups
-					),
-					array(
-						'type' => 'switch',
-						'name' => 'homepageSonarrQueueCombine',
-						'label' => 'Add to Combined Downloader',
-						'value' => $GLOBALS['homepageSonarrQueueCombine']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageSonarrQueueRefresh',
-						'label' => 'Refresh Seconds',
-						'value' => $GLOBALS['homepageSonarrQueueRefresh'],
-						'options' => optionTime()
-					),
-				),
-				'Calendar' => array(
-					array(
-						'type' => 'number',
-						'name' => 'calendarStart',
-						'label' => '# of Days Before',
-						'value' => $GLOBALS['calendarStart'],
-						'placeholder' => ''
-					),
-					array(
-						'type' => 'number',
-						'name' => 'calendarEnd',
-						'label' => '# of Days After',
-						'value' => $GLOBALS['calendarEnd'],
-						'placeholder' => ''
-					),
-					array(
-						'type' => 'select',
-						'name' => 'calendarFirstDay',
-						'label' => 'Start Day',
-						'value' => $GLOBALS['calendarFirstDay'],
-						'options' => $day
-					),
-					array(
-						'type' => 'select',
-						'name' => 'calendarDefault',
-						'label' => 'Default View',
-						'value' => $GLOBALS['calendarDefault'],
-						'options' => $calendarDefault
-					),
-					array(
-						'type' => 'select',
-						'name' => 'calendarTimeFormat',
-						'label' => 'Time Format',
-						'value' => $GLOBALS['calendarTimeFormat'],
-						'options' => $timeFormat
-					),
-					array(
-						'type' => 'select',
-						'name' => 'calendarLimit',
-						'label' => 'Items Per Day',
-						'value' => $GLOBALS['calendarLimit'],
-						'options' => $limit
-					),
-					array(
-						'type' => 'select',
-						'name' => 'calendarRefresh',
-						'label' => 'Refresh Seconds',
-						'value' => $GLOBALS['calendarRefresh'],
-						'options' => optionTime()
-					),
-					array(
-						'type' => 'switch',
-						'name' => 'sonarrUnmonitored',
-						'label' => 'Show Unmonitored',
-						'value' => $GLOBALS['sonarrUnmonitored']
-					)
-				),
-				'Test Connection' => array(
-					array(
-						'type' => 'blank',
-						'label' => 'Please Save before Testing'
-					),
-					array(
-						'type' => 'button',
-						'label' => '',
-						'icon' => 'fa fa-flask',
-						'class' => 'pull-right',
-						'text' => 'Test Connection',
-						'attr' => 'onclick="testAPIConnection(\'sonarr\')"'
-					),
-				)
-			)
-		),
-		array(
-			'name' => 'Lidarr',
-			'enabled' => (strpos('personal', $GLOBALS['license']) !== false) ? true : false,
-			'image' => 'plugins/images/tabs/lidarr.png',
-			'category' => 'PMR',
-			'settings' => array(
-				'Enable' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'homepageLidarrEnabled',
-						'label' => 'Enable',
-						'value' => $GLOBALS['homepageLidarrEnabled']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageLidarrAuth',
-						'label' => 'Minimum Authentication',
-						'value' => $GLOBALS['homepageLidarrAuth'],
-						'options' => $groups
-					)
-				),
-				'Connection' => array(
-					array(
-						'type' => 'input',
-						'name' => 'lidarrURL',
-						'label' => 'URL',
-						'value' => $GLOBALS['lidarrURL'],
-						'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
-						'placeholder' => 'http(s)://hostname:port'
-					),
-					array(
-						'type' => 'password-alt',
-						'name' => 'lidarrToken',
-						'label' => 'Token',
-						'value' => $GLOBALS['lidarrToken']
-					)
-				),
-				'Misc Options' => array(
-					array(
-						'type' => 'number',
-						'name' => 'calendarStart',
-						'label' => '# of Days Before',
-						'value' => $GLOBALS['calendarStart'],
-						'placeholder' => ''
-					),
-					array(
-						'type' => 'number',
-						'name' => 'calendarEnd',
-						'label' => '# of Days After',
-						'value' => $GLOBALS['calendarEnd'],
-						'placeholder' => ''
-					),
-					array(
-						'type' => 'select',
-						'name' => 'calendarFirstDay',
-						'label' => 'Start Day',
-						'value' => $GLOBALS['calendarFirstDay'],
-						'options' => $day
-					),
-					array(
-						'type' => 'select',
-						'name' => 'calendarDefault',
-						'label' => 'Default View',
-						'value' => $GLOBALS['calendarDefault'],
-						'options' => $calendarDefault
-					),
-					array(
-						'type' => 'select',
-						'name' => 'calendarTimeFormat',
-						'label' => 'Time Format',
-						'value' => $GLOBALS['calendarTimeFormat'],
-						'options' => $timeFormat
-					),
-					array(
-						'type' => 'select',
-						'name' => 'calendarLimit',
-						'label' => 'Items Per Day',
-						'value' => $GLOBALS['calendarLimit'],
-						'options' => $limit
-					),
-					array(
-						'type' => 'select',
-						'name' => 'calendarRefresh',
-						'label' => 'Refresh Seconds',
-						'value' => $GLOBALS['calendarRefresh'],
-						'options' => optionTime()
-					),
-				),
-				'Test Connection' => array(
-					array(
-						'type' => 'blank',
-						'label' => 'Please Save before Testing'
-					),
-					array(
-						'type' => 'button',
-						'label' => '',
-						'icon' => 'fa fa-flask',
-						'class' => 'pull-right',
-						'text' => 'Test Connection',
-						'attr' => 'onclick="testAPIConnection(\'lidarr\')"'
-					),
-				)
-			)
-		),
-		array(
-			'name' => 'Radarr',
-			'enabled' => (strpos('personal', $GLOBALS['license']) !== false) ? true : false,
-			'image' => 'plugins/images/tabs/radarr.png',
-			'category' => 'PVR',
-			'settings' => array(
-				'Enable' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'homepageRadarrEnabled',
-						'label' => 'Enable',
-						'value' => $GLOBALS['homepageRadarrEnabled']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageRadarrAuth',
-						'label' => 'Minimum Authentication',
-						'value' => $GLOBALS['homepageRadarrAuth'],
-						'options' => $groups
-					)
-				),
-				'Connection' => array(
-					array(
-						'type' => 'input',
-						'name' => 'radarrURL',
-						'label' => 'URL',
-						'value' => $GLOBALS['radarrURL'],
-						'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
-						'placeholder' => 'http(s)://hostname:port'
-					),
-					array(
-						'type' => 'password-alt',
-						'name' => 'radarrToken',
-						'label' => 'Token',
-						'value' => $GLOBALS['radarrToken']
-					)
-				),
-				'Queue' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'homepageRadarrQueueEnabled',
-						'label' => 'Enable',
-						'value' => $GLOBALS['homepageRadarrQueueEnabled']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageRadarrQueueAuth',
-						'label' => 'Minimum Authentication',
-						'value' => $GLOBALS['homepageRadarrQueueAuth'],
-						'options' => $groups
-					),
-					array(
-						'type' => 'switch',
-						'name' => 'homepageRadarrQueueCombine',
-						'label' => 'Add to Combined Downloader',
-						'value' => $GLOBALS['homepageRadarrQueueCombine']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageRadarrQueueRefresh',
-						'label' => 'Refresh Seconds',
-						'value' => $GLOBALS['homepageRadarrQueueRefresh'],
-						'options' => optionTime()
-					),
-				),
-				'Calendar' => array(
-					array(
-						'type' => 'number',
-						'name' => 'calendarStart',
-						'label' => '# of Days Before',
-						'value' => $GLOBALS['calendarStart'],
-						'placeholder' => ''
-					),
-					array(
-						'type' => 'number',
-						'name' => 'calendarEnd',
-						'label' => '# of Days After',
-						'value' => $GLOBALS['calendarEnd'],
-						'placeholder' => ''
-					),
-					array(
-						'type' => 'select',
-						'name' => 'calendarFirstDay',
-						'label' => 'Start Day',
-						'value' => $GLOBALS['calendarFirstDay'],
-						'options' => $day
-					),
-					array(
-						'type' => 'select',
-						'name' => 'calendarDefault',
-						'label' => 'Default View',
-						'value' => $GLOBALS['calendarDefault'],
-						'options' => $calendarDefault
-					),
-					array(
-						'type' => 'select',
-						'name' => 'calendarTimeFormat',
-						'label' => 'Time Format',
-						'value' => $GLOBALS['calendarTimeFormat'],
-						'options' => $timeFormat
-					),
-					array(
-						'type' => 'select',
-						'name' => 'calendarLimit',
-						'label' => 'Items Per Day',
-						'value' => $GLOBALS['calendarLimit'],
-						'options' => $limit
-					),
-					array(
-						'type' => 'select',
-						'name' => 'calendarRefresh',
-						'label' => 'Refresh Seconds',
-						'value' => $GLOBALS['calendarRefresh'],
-						'options' => optionTime()
-					)
-				),
-				'Test Connection' => array(
-					array(
-						'type' => 'blank',
-						'label' => 'Please Save before Testing'
-					),
-					array(
-						'type' => 'button',
-						'label' => '',
-						'icon' => 'fa fa-flask',
-						'class' => 'pull-right',
-						'text' => 'Test Connection',
-						'attr' => 'onclick="testAPIConnection(\'radarr\')"'
-					),
-				)
-			)
-		),
-		array(
-			'name' => 'CouchPotato',
-			'enabled' => (strpos('personal', $GLOBALS['license']) !== false) ? true : false,
-			'image' => 'plugins/images/tabs/couchpotato.png',
-			'category' => 'PVR',
-			'settings' => array(
-				'Enable' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'homepageCouchpotatoEnabled',
-						'label' => 'Enable',
-						'value' => $GLOBALS['homepageCouchpotatoEnabled']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageCouchpotatoAuth',
-						'label' => 'Minimum Authentication',
-						'value' => $GLOBALS['homepageCouchpotatoAuth'],
-						'options' => $groups
-					)
-				),
-				'Connection' => array(
-					array(
-						'type' => 'input',
-						'name' => 'couchpotatoURL',
-						'label' => 'URL',
-						'value' => $GLOBALS['couchpotatoURL'],
-						'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
-						'placeholder' => 'http(s)://hostname:port'
-					),
-					array(
-						'type' => 'password-alt',
-						'name' => 'couchpotatoToken',
-						'label' => 'Token',
-						'value' => $GLOBALS['couchpotatoToken']
-					)
-				),
-				'Misc Options' => array(
-					array(
-						'type' => 'select',
-						'name' => 'calendarFirstDay',
-						'label' => 'Start Day',
-						'value' => $GLOBALS['calendarFirstDay'],
-						'options' => $day
-					),
-					array(
-						'type' => 'select',
-						'name' => 'calendarDefault',
-						'label' => 'Default View',
-						'value' => $GLOBALS['calendarDefault'],
-						'options' => $calendarDefault
-					),
-					array(
-						'type' => 'select',
-						'name' => 'calendarTimeFormat',
-						'label' => 'Time Format',
-						'value' => $GLOBALS['calendarTimeFormat'],
-						'options' => $timeFormat
-					),
-					array(
-						'type' => 'select',
-						'name' => 'calendarLimit',
-						'label' => 'Items Per Day',
-						'value' => $GLOBALS['calendarLimit'],
-						'options' => $limit
-					),
-					array(
-						'type' => 'select',
-						'name' => 'calendarRefresh',
-						'label' => 'Refresh Seconds',
-						'value' => $GLOBALS['calendarRefresh'],
-						'options' => optionTime()
-					)
-				)
-			)
-		),
-		array(
-			'name' => 'SickRage',
-			'enabled' => (strpos('personal', $GLOBALS['license']) !== false) ? true : false,
-			'image' => 'plugins/images/tabs/sickrage.png',
-			'category' => 'PVR',
-			'settings' => array(
-				'Enable' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'homepageSickrageEnabled',
-						'label' => 'Enable',
-						'value' => $GLOBALS['homepageSickrageEnabled']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageSickrageAuth',
-						'label' => 'Minimum Authentication',
-						'value' => $GLOBALS['homepageSickrageAuth'],
-						'options' => $groups
-					)
-				),
-				'Connection' => array(
-					array(
-						'type' => 'input',
-						'name' => 'sickrageURL',
-						'label' => 'URL',
-						'value' => $GLOBALS['sickrageURL'],
-						'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
-						'placeholder' => 'http(s)://hostname:port'
-					),
-					array(
-						'type' => 'password-alt',
-						'name' => 'sickrageToken',
-						'label' => 'Token',
-						'value' => $GLOBALS['sickrageToken']
-					)
-				),
-				'Misc Options' => array(
-					array(
-						'type' => 'select',
-						'name' => 'calendarFirstDay',
-						'label' => 'Start Day',
-						'value' => $GLOBALS['calendarFirstDay'],
-						'options' => $day
-					),
-					array(
-						'type' => 'select',
-						'name' => 'calendarDefault',
-						'label' => 'Default View',
-						'value' => $GLOBALS['calendarDefault'],
-						'options' => $calendarDefault
-					),
-					array(
-						'type' => 'select',
-						'name' => 'calendarTimeFormat',
-						'label' => 'Time Format',
-						'value' => $GLOBALS['calendarTimeFormat'],
-						'options' => $timeFormat
-					),
-					array(
-						'type' => 'select',
-						'name' => 'calendarLimit',
-						'label' => 'Items Per Day',
-						'value' => $GLOBALS['calendarLimit'],
-						'options' => $limit
-					),
-					array(
-						'type' => 'select',
-						'name' => 'calendarRefresh',
-						'label' => 'Refresh Seconds',
-						'value' => $GLOBALS['calendarRefresh'],
-						'options' => optionTime()
-					)
-				)
-			)
-		),
-		array(
-			'name' => 'Ombi',
-			'enabled' => (strpos('personal', $GLOBALS['license']) !== false) ? true : false,
-			'image' => 'plugins/images/tabs/ombi.png',
-			'category' => 'Requests',
-			'settings' => array(
-				'Enable' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'homepageOmbiEnabled',
-						'label' => 'Enable',
-						'value' => $GLOBALS['homepageOmbiEnabled']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageOmbiAuth',
-						'label' => 'Minimum Authentication',
-						'value' => $GLOBALS['homepageOmbiAuth'],
-						'options' => $groups
-					)
-				),
-				'Connection' => array(
-					array(
-						'type' => 'input',
-						'name' => 'ombiURL',
-						'label' => 'URL',
-						'value' => $GLOBALS['ombiURL'],
-						'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
-						'placeholder' => 'http(s)://hostname:port'
-					),
-					array(
-						'type' => 'password-alt',
-						'name' => 'ombiToken',
-						'label' => 'Token',
-						'value' => $GLOBALS['ombiToken']
-					)
-				),
-				'Misc Options' => array(
-					array(
-						'type' => 'select',
-						'name' => 'homepageOmbiRequestAuth',
-						'label' => 'Minimum Group to Request',
-						'value' => $GLOBALS['homepageOmbiRequestAuth'],
-						'options' => $groups
-					),
-					array(
-						'type' => 'select',
-						'name' => 'ombiTvDefault',
-						'label' => 'TV Show Default Request',
-						'value' => $GLOBALS['ombiTvDefault'],
-						'options' => $ombiTvOptions
-					),
-					array(
-						'type' => 'switch',
-						'name' => 'ombiLimitUser',
-						'label' => 'Limit to User',
-						'value' => $GLOBALS['ombiLimitUser']
-					),
-					array(
-						'type' => 'number',
-						'name' => 'ombiLimit',
-						'label' => 'Item Limit',
-						'value' => $GLOBALS['ombiLimit'],
-					),
-					array(
-						'type' => 'select',
-						'name' => 'ombiRefresh',
-						'label' => 'Refresh Seconds',
-						'value' => $GLOBALS['ombiRefresh'],
-						'options' => optionTime()
-					),
-					array(
-						'type' => 'switch',
-						'name' => 'ombiAlias',
-						'label' => 'Use Ombi Alias Names',
-						'value' => $GLOBALS['ombiAlias'],
-						'help' => 'Use Ombi Alias Names instead of Usernames - If Alias is blank, Alias will fallback to Username'
-					)
-				),
-				'Default Filter' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'ombiDefaultFilterAvailable',
-						'label' => 'Show Available',
-						'value' => $GLOBALS['ombiDefaultFilterAvailable'],
-						'help' => 'Show All Available Ombi Requests'
-					),
-					array(
-						'type' => 'switch',
-						'name' => 'ombiDefaultFilterUnavailable',
-						'label' => 'Show Unavailable',
-						'value' => $GLOBALS['ombiDefaultFilterUnavailable'],
-						'help' => 'Show All Unavailable Ombi Requests'
-					),
-					array(
-						'type' => 'switch',
-						'name' => 'ombiDefaultFilterApproved',
-						'label' => 'Show Approved',
-						'value' => $GLOBALS['ombiDefaultFilterApproved'],
-						'help' => 'Show All Approved Ombi Requests'
-					),
-					array(
-						'type' => 'switch',
-						'name' => 'ombiDefaultFilterUnapproved',
-						'label' => 'Show Unapproved',
-						'value' => $GLOBALS['ombiDefaultFilterUnapproved'],
-						'help' => 'Show All Unapproved Ombi Requests'
-					),
-					array(
-						'type' => 'switch',
-						'name' => 'ombiDefaultFilterDenied',
-						'label' => 'Show Denied',
-						'value' => $GLOBALS['ombiDefaultFilterDenied'],
-						'help' => 'Show All Denied Ombi Requests'
-					)
-				),
-				'Test Connection' => array(
-					array(
-						'type' => 'blank',
-						'label' => 'Please Save before Testing'
-					),
-					array(
-						'type' => 'button',
-						'label' => '',
-						'icon' => 'fa fa-flask',
-						'class' => 'pull-right',
-						'text' => 'Test Connection',
-						'attr' => 'onclick="testAPIConnection(\'ombi\')"'
-					),
-				)
-			)
-		),
-		array(
-			'name' => 'Unifi',
-			'enabled' => true,
-			'image' => 'plugins/images/tabs/ubnt.png',
-			'category' => 'Monitor',
-			'settings' => array(
-				'Enable' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'homepageUnifiEnabled',
-						'label' => 'Enable',
-						'value' => $GLOBALS['homepageUnifiEnabled']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageUnifiAuth',
-						'label' => 'Minimum Authentication',
-						'value' => $GLOBALS['homepageUnifiAuth'],
-						'options' => $groups
-					)
-				),
-				'Connection' => array(
-					array(
-						'type' => 'input',
-						'name' => 'unifiURL',
-						'label' => 'URL',
-						'value' => $GLOBALS['unifiURL'],
-						'help' => 'URL for Unifi',
-						'placeholder' => 'Unifi API URL'
-					),
-					array(
-						'type' => 'blank',
-						'label' => ''
-					),
-					array(
-						'type' => 'input',
-						'name' => 'unifiUsername',
-						'label' => 'Username',
-						'value' => $GLOBALS['unifiUsername']
-					),
-					array(
-						'type' => 'password',
-						'name' => 'unifiPassword',
-						'label' => 'Password',
-						'value' => $GLOBALS['unifiPassword']
-					),
-					array(
-						'type' => 'input',
-						'name' => 'unifiSiteName',
-						'label' => 'Site Name',
-						'value' => $GLOBALS['unifiSiteName'],
-						'help' => 'Site Name - not Site ID nor Site Description',
-					),
-					array(
-						'type' => 'button',
-						'label' => 'Grab Unifi Site',
-						'icon' => 'fa fa-building',
-						'text' => 'Get Unifi Site',
-						'attr' => 'onclick="getUnifiSite(\'unifiSite\')"'
-					),
-				),
-				'Misc Options' => array(
-					array(
-						'type' => 'select',
-						'name' => 'homepageUnifiRefresh',
-						'label' => 'Refresh Seconds',
-						'value' => $GLOBALS['homepageUnifiRefresh'],
-						'options' => optionTime()
-					),
-				),
-				'Test Connection' => array(
-					array(
-						'type' => 'blank',
-						'label' => 'Please Save before Testing'
-					),
-					array(
-						'type' => 'button',
-						'label' => '',
-						'icon' => 'fa fa-flask',
-						'class' => 'pull-right',
-						'text' => 'Test Connection',
-						'attr' => 'onclick="testAPIConnection(\'unifi\')"'
-					),
-				)
-			)
-		),
-		array(
-			'name' => 'HealthChecks',
-			'enabled' => true,
-			'image' => 'plugins/images/tabs/healthchecks.png',
-			'category' => 'Monitor',
-			'settings' => array(
-				'Enable' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'homepageHealthChecksEnabled',
-						'label' => 'Enable',
-						'value' => $GLOBALS['homepageHealthChecksEnabled']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageHealthChecksAuth',
-						'label' => 'Minimum Authentication',
-						'value' => $GLOBALS['homepageHealthChecksAuth'],
-						'options' => $groups
-					)
-				),
-				'Connection' => array(
-					array(
-						'type' => 'input',
-						'name' => 'healthChecksURL',
-						'label' => 'URL',
-						'value' => $GLOBALS['healthChecksURL'],
-						'help' => 'URL for HealthChecks API',
-						'placeholder' => 'HealthChecks API URL'
-					),
-					array(
-						'type' => 'password-alt',
-						'name' => 'healthChecksToken',
-						'label' => 'Token',
-						'value' => $GLOBALS['healthChecksToken']
-					)
-				),
-				'Misc Options' => array(
-					array(
-						'type' => 'input',
-						'name' => 'healthChecksTags',
-						'label' => 'Tags',
-						'value' => $GLOBALS['healthChecksTags'],
-						'help' => 'Pull only checks with this tag - Blank for all',
-						'placeholder' => 'Multiple tags using CSV - tag1,tag2'
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageHealthChecksRefresh',
-						'label' => 'Refresh Seconds',
-						'value' => $GLOBALS['homepageHealthChecksRefresh'],
-						'options' => optionTime()
-					),
-				),
-			)
-		),
-		array(
-			'name' => 'CustomHTML-1',
-			'enabled' => (strpos('personal,business', $GLOBALS['license']) !== false) ? true : false,
-			'image' => 'plugins/images/tabs/custom1.png',
-			'category' => 'Custom',
-			'settings' => array(
-				'Enable' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'homepagCustomHTMLoneEnabled',
-						'label' => 'Enable',
-						'value' => $GLOBALS['homepagCustomHTMLoneEnabled']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepagCustomHTMLoneAuth',
-						'label' => 'Minimum Authentication',
-						'value' => $GLOBALS['homepagCustomHTMLoneAuth'],
-						'options' => $groups
-					)
-				),
-				'Code' => array(
-					array(
-						'type' => 'textbox',
-						'name' => 'customHTMLone',
-						'class' => 'hidden customHTMLoneTextarea',
-						'label' => '',
-						'value' => $GLOBALS['customHTMLone'],
-					),
-					array(
-						'type' => 'html',
-						'override' => 12,
-						'label' => 'Custom HTML/JavaScript',
-						'html' => '<button type="button" class="hidden savecustomHTMLoneTextarea btn btn-info btn-circle pull-right m-r-5 m-l-10"><i class="fa fa-save"></i> </button><div id="customHTMLoneEditor" style="height:300px">' . htmlentities($GLOBALS['customHTMLone']) . '</div>'
-					),
-				)
-			)
-		),
-		array(
-			'name' => 'CustomHTML-2',
-			'enabled' => (strpos('personal,business', $GLOBALS['license']) !== false) ? true : false,
-			'image' => 'plugins/images/tabs/custom2.png',
-			'category' => 'Custom',
-			'settings' => array(
-				'Enable' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'homepagCustomHTMLtwoEnabled',
-						'label' => 'Enable',
-						'value' => $GLOBALS['homepagCustomHTMLtwoEnabled']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepagCustomHTMLtwoAuth',
-						'label' => 'Minimum Authentication',
-						'value' => $GLOBALS['homepagCustomHTMLtwoAuth'],
-						'options' => $groups
-					)
-				),
-				'Code' => array(
-					array(
-						'type' => 'textbox',
-						'name' => 'customHTMLtwo',
-						'class' => 'hidden customHTMLtwoTextarea',
-						'label' => '',
-						'value' => $GLOBALS['customHTMLtwo'],
-					),
-					array(
-						'type' => 'html',
-						'override' => 12,
-						'label' => 'Custom HTML/JavaScript',
-						'html' => '<button type="button" class="hidden savecustomHTMLtwoTextarea btn btn-info btn-circle pull-right m-r-5 m-l-10"><i class="fa fa-save"></i> </button><div id="customHTMLtwoEditor" style="height:300px">' . htmlentities($GLOBALS['customHTMLtwo']) . '</div>'
-					),
-				)
-			)
-		),
-		array(
-			'name' => 'Misc',
-			'enabled' => true,
-			'image' => 'plugins/images/organizr/logo-no-border.png',
-			'category' => 'Custom',
-			'settings' => array(
-				'YouTube' => array(
-					array(
-						'type' => 'input',
-						'name' => 'youtubeAPI',
-						'label' => 'Youtube API Key',
-						'value' => $GLOBALS['youtubeAPI'],
-						'help' => 'Please make sure to input this API key as the organizr one gets limited'
-					),
-					array(
-						'type' => 'html',
-						'override' => 6,
-						'label' => 'Instructions',
-						'html' => '<a href="https://www.slickremix.com/docs/get-api-key-for-youtube/" target="_blank">Click here for instructions</a>'
-					),
-				)
-			)
-		),
-		array(
-			'name' => 'Pi-hole',
-			'enabled' => true,
-			'image' => 'plugins/images/tabs/pihole.png',
-			'category' => 'Monitor',
-			'settings' => array(
-				'Enable' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'homepagePiholeEnabled',
-						'label' => 'Enable',
-						'value' => $GLOBALS['homepagePiholeEnabled']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepagePiholeAuth',
-						'label' => 'Minimum Authentication',
-						'value' => $GLOBALS['homepagePiholeAuth'],
-						'options' => $groups
-					)
-				),
-				'Connection' => array(
-					array(
-						'type' => 'input',
-						'name' => 'piholeURL',
-						'label' => 'URL',
-						'value' => $GLOBALS['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/'
-					),
-				),
-				'Misc' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'piholeHeaderToggle',
-						'label' => 'Toggle Title',
-						'value' => $GLOBALS['piholeHeaderToggle'],
-						'help' => 'Shows/hides the title of this homepage module'
-					),
-					array(
-						'type' => 'switch',
-						'name' => 'homepagePiholeCombine',
-						'label' => 'Combine stat cards',
-						'value' => $GLOBALS['homepagePiholeCombine'],
-						'help' => 'This controls whether to combine the stats for multiple piholes into 1 card.',
-					),
-				),
-			)
-		),
-		array(
-			'name' => 'Tautulli',
-			'enabled' => (strpos('personal', $GLOBALS['license']) !== false) ? true : false,
-			'image' => 'plugins/images/tabs/tautulli.png',
-			'category' => 'Monitor',
-			'settings' => array(
-				'Enable' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'homepageTautulliEnabled',
-						'label' => 'Enable',
-						'value' => $GLOBALS['homepageTautulliEnabled']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageTautulliAuth',
-						'label' => 'Minimum Authentication',
-						'value' => $GLOBALS['homepageTautulliAuth'],
-						'options' => $groups
-					)
-				),
-				'Options' => array(
-					array(
-						'type' => 'input',
-						'name' => 'tautulliHeader',
-						'label' => 'Title',
-						'value' => $GLOBALS['tautulliHeader'],
-						'help' => 'Sets the title of this homepage module'
-					),
-					array(
-						'type' => 'switch',
-						'name' => 'tautulliHeaderToggle',
-						'label' => 'Toggle Title',
-						'value' => $GLOBALS['tautulliHeaderToggle'],
-						'help' => 'Shows/hides the title of this homepage module'
-					)
-				),
-				'Connection' => array(
-					array(
-						'type' => 'input',
-						'name' => 'tautulliURL',
-						'label' => 'URL',
-						'value' => $GLOBALS['tautulliURL'],
-						'help' => 'URL for Tautulli API, include the IP, the port and the base URL (e.g. /tautulli/) in the URL',
-						'placeholder' => 'http://<ip>:<port>'
-					),
-					array(
-						'type' => 'password-alt',
-						'name' => 'tautulliApikey',
-						'label' => 'API Key',
-						'value' => $GLOBALS['tautulliApikey']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageTautulliRefresh',
-						'label' => 'Refresh Seconds',
-						'value' => $GLOBALS['homepageTautulliRefresh'],
-						'options' => optionTime()
-					),
-				),
-				'Library Stats' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'tautulliLibraries',
-						'label' => 'Libraries',
-						'value' => $GLOBALS['tautulliLibraries'],
-						'help' => 'Shows/hides the card with library information.',
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageTautulliLibraryAuth',
-						'label' => 'Minimum Authentication',
-						'value' => $GLOBALS['homepageTautulliLibraryAuth'],
-						'options' => $groups
-					),
-				),
-				'Viewing Stats' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'tautulliPopularMovies',
-						'label' => 'Popular Movies',
-						'value' => $GLOBALS['tautulliPopularMovies'],
-						'help' => 'Shows/hides the card with Popular Movies information.',
-					),
-					array(
-						'type' => 'switch',
-						'name' => 'tautulliPopularTV',
-						'label' => 'Popular TV',
-						'value' => $GLOBALS['tautulliPopularTV'],
-						'help' => 'Shows/hides the card with Popular TV information.',
-					),
-					array(
-						'type' => 'switch',
-						'name' => 'tautulliTopMovies',
-						'label' => 'Top Movies',
-						'value' => $GLOBALS['tautulliTopMovies'],
-						'help' => 'Shows/hides the card with Top Movies information.',
-					),
-					array(
-						'type' => 'switch',
-						'name' => 'tautulliTopTV',
-						'label' => 'Top TV',
-						'value' => $GLOBALS['tautulliTopTV'],
-						'help' => 'Shows/hides the card with Top TV information.',
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageTautulliViewsAuth',
-						'label' => 'Minimum Authentication',
-						'value' => $GLOBALS['homepageTautulliViewsAuth'],
-						'options' => $groups
-					),
-				),
-				'Misc Stats' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'tautulliTopUsers',
-						'label' => 'Top Users',
-						'value' => $GLOBALS['tautulliTopUsers'],
-						'help' => 'Shows/hides the card with Top Users information.',
-					),
-					array(
-						'type' => 'switch',
-						'name' => 'tautulliTopPlatforms',
-						'label' => 'Top Platforms',
-						'value' => $GLOBALS['tautulliTopPlatforms'],
-						'help' => 'Shows/hides the card with Top Platforms information.',
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageTautulliMiscAuth',
-						'label' => 'Minimum Authentication',
-						'value' => $GLOBALS['homepageTautulliMiscAuth'],
-						'options' => $groups
-					),
-				),
-			)
-		),
-		array(
-			'name' => 'Monitorr',
-			'enabled' => true,
-			'image' => 'plugins/images/tabs/monitorr.png',
-			'category' => 'Monitor',
-			'settings' => array(
-				'Enable' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'homepageMonitorrEnabled',
-						'label' => 'Enable',
-						'value' => $GLOBALS['homepageMonitorrEnabled']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageMonitorrAuth',
-						'label' => 'Minimum Authentication',
-						'value' => $GLOBALS['homepageMonitorrAuth'],
-						'options' => $groups
-					)
-				),
-				'Connection' => array(
-					array(
-						'type' => 'input',
-						'name' => 'monitorrURL',
-						'label' => 'URL',
-						'value' => $GLOBALS['monitorrURL'],
-						'help' => 'URL for Monitorr. Please use the revers proxy URL i.e. https://domain.com/monitorr/.',
-						'placeholder' => 'http://domain.com/monitorr/'
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageMonitorrRefresh',
-						'label' => 'Refresh Seconds',
-						'value' => $GLOBALS['homepageMonitorrRefresh'],
-						'options' => optionTime()
-					),
-				),
-				'Options' => array(
-					array(
-						'type' => 'input',
-						'name' => 'monitorrHeader',
-						'label' => 'Title',
-						'value' => $GLOBALS['monitorrHeader'],
-						'help' => 'Sets the title of this homepage module',
-					),
-					array(
-						'type' => 'switch',
-						'name' => 'monitorrHeaderToggle',
-						'label' => 'Toggle Title',
-						'value' => $GLOBALS['monitorrHeaderToggle'],
-						'help' => 'Shows/hides the title of this homepage module'
-					),
-					array(
-						'type' => 'switch',
-						'name' => 'monitorrCompact',
-						'label' => 'Compact view',
-						'value' => $GLOBALS['monitorrCompact'],
-						'help' => 'Toggles the compact view of this homepage module'
-					),
-				),
-			)
-		),
-		array(
-			'name' => 'Weather-Air',
-			'enabled' => true,
-			'image' => 'plugins/images/tabs/wind.png',
-			'category' => 'Monitor',
-			'settings' => array(
-				'Enable' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'homepageWeatherAndAirEnabled',
-						'label' => 'Enable',
-						'value' => $GLOBALS['homepageWeatherAndAirEnabled']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageWeatherAndAirAuth',
-						'label' => 'Minimum Authentication',
-						'value' => $GLOBALS['homepageWeatherAndAirAuth'],
-						'options' => $groups
-					)
-				),
-				'Connection' => array(
-					array(
-						'type' => 'input',
-						'name' => 'homepageWeatherAndAirLatitude',
-						'label' => 'Latitude',
-						'value' => $GLOBALS['homepageWeatherAndAirLatitude'],
-						'help' => 'Please enter full latitude including minus if needed'
-					),
-					array(
-						'type' => 'input',
-						'name' => 'homepageWeatherAndAirLongitude',
-						'label' => 'Longitude',
-						'value' => $GLOBALS['homepageWeatherAndAirLongitude'],
-						'help' => 'Please enter full longitude including minus if needed'
-					),
-					array(
-						'type' => 'blank',
-						'label' => ''
-					),
-					array(
-						'type' => 'button',
-						'label' => '',
-						'icon' => 'fa fa-search',
-						'class' => 'pull-right',
-						'text' => 'Need Help With Coordinates?',
-						'attr' => 'onclick="showLookupCoordinatesModal()"'
-					),
-				),
-				'Options' => array(
-					array(
-						'type' => 'input',
-						'name' => 'homepageWeatherAndAirWeatherHeader',
-						'label' => 'Title',
-						'value' => $GLOBALS['homepageWeatherAndAirWeatherHeader'],
-						'help' => 'Sets the title of this homepage module',
-					),
-					array(
-						'type' => 'switch',
-						'name' => 'homepageWeatherAndAirWeatherHeaderToggle',
-						'label' => 'Toggle Title',
-						'value' => $GLOBALS['homepageWeatherAndAirWeatherHeaderToggle'],
-						'help' => 'Shows/hides the title of this homepage module'
-					),
-					array(
-						'type' => 'switch',
-						'name' => 'homepageWeatherAndAirWeatherEnabled',
-						'label' => 'Enable Weather',
-						'value' => $GLOBALS['homepageWeatherAndAirWeatherEnabled'],
-						'help' => 'Toggles the view module for Weather'
-					),
-					array(
-						'type' => 'switch',
-						'name' => 'homepageWeatherAndAirAirQualityEnabled',
-						'label' => 'Enable Air Quality',
-						'value' => $GLOBALS['homepageWeatherAndAirAirQualityEnabled'],
-						'help' => 'Toggles the view module for Air Quality'
-					),
-					array(
-						'type' => 'switch',
-						'name' => 'homepageWeatherAndAirPollenEnabled',
-						'label' => 'Enable Pollen',
-						'value' => $GLOBALS['homepageWeatherAndAirPollenEnabled'],
-						'help' => 'Toggles the view module for Pollen'
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageWeatherAndAirUnits',
-						'label' => 'Unit of Measurement',
-						'value' => $GLOBALS['homepageWeatherAndAirUnits'],
-						'options' => array(
-							array(
-								'name' => 'Imperial',
-								'value' => 'imperial'
-							),
-							array(
-								'name' => 'Metric',
-								'value' => 'metric'
-							)
-						)
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageWeatherAndAirRefresh',
-						'label' => 'Refresh Seconds',
-						'value' => $GLOBALS['homepageWeatherAndAirRefresh'],
-						'options' => optionTime()
-					),
-				),
-			)
-		),
-		array(
-			'name' => 'Speedtest',
-			'enabled' => true,
-			'image' => 'plugins/images/tabs/speedtest-icon.png',
-			'category' => 'Monitor',
-			'settings' => array(
-				'Enable' => array(
-					array(
-						'type' => 'html',
-						'override' => 6,
-						'label' => 'Info',
-						'html' => '<p>This homepage item requires <a href="https://github.com/henrywhitaker3/Speedtest-Tracker" target="_blank" rel="noreferrer noopener">Speedtest-Tracker <i class="fa fa-external-link" aria-hidden="true"></i></a> to be running on your network.</p>'
-					),
-					array(
-						'type' => 'switch',
-						'name' => 'homepageSpeedtestEnabled',
-						'label' => 'Enable',
-						'value' => $GLOBALS['homepageSpeedtestEnabled']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageSpeedtestAuth',
-						'label' => 'Minimum Authentication',
-						'value' => $GLOBALS['homepageSpeedtestAuth'],
-						'options' => $groups
-					)
-				),
-				'Connection' => array(
-					array(
-						'type' => 'input',
-						'name' => 'speedtestURL',
-						'label' => 'URL',
-						'value' => $GLOBALS['speedtestURL'],
-						'help' => 'Enter the IP:PORT of your speedtest instance e.g. http(s)://<ip>:<port>'
-					),
-				),
-				'Options' => array(
-					array(
-						'type' => 'input',
-						'name' => 'speedtestHeader',
-						'label' => 'Title',
-						'value' => $GLOBALS['speedtestHeader'],
-						'help' => 'Sets the title of this homepage module',
-					),
-					array(
-						'type' => 'switch',
-						'name' => 'speedtestHeaderToggle',
-						'label' => 'Toggle Title',
-						'value' => $GLOBALS['speedtestHeaderToggle'],
-						'help' => 'Shows/hides the title of this homepage module'
-					),
-				),
-			)
-		),
-		netdataSettngsArray(),
-		array(
-			'name' => 'Octoprint',
-			'enabled' => true,
-			'image' => 'plugins/images/tabs/octoprint.png',
-			'category' => 'Monitor',
-			'settings' => array(
-				'Enable' => array(
-					array(
-						'type' => 'switch',
-						'name' => 'homepageOctoprintEnabled',
-						'label' => 'Enable',
-						'value' => $GLOBALS['homepageOctoprintEnabled']
-					),
-					array(
-						'type' => 'select',
-						'name' => 'homepageOctoprintAuth',
-						'label' => 'Minimum Authentication',
-						'value' => $GLOBALS['homepageOctoprintAuth'],
-						'options' => $groups
-					)
-				),
-				'Connection' => array(
-					array(
-						'type' => 'input',
-						'name' => 'octoprintURL',
-						'label' => 'URL',
-						'value' => $GLOBALS['octoprintURL'],
-						'help' => 'Enter the IP:PORT of your Octoprint instance e.g. http://octopi.local'
-					),
-					array(
-						'type' => 'input',
-						'name' => 'octoprintToken',
-						'label' => 'API Key',
-						'value' => $GLOBALS['octoprintToken'],
-						'help' => 'Enter your Octoprint API key, found in Octoprints settings page.'
-					),
-				),
-				'Options' => array(
-					array(
-						'type' => 'input',
-						'name' => 'octoprintHeader',
-						'label' => 'Title',
-						'value' => $GLOBALS['octoprintHeader'],
-						'help' => 'Sets the title of this homepage module',
-					),
-					array(
-						'type' => 'switch',
-						'name' => 'octoprintToggle',
-						'label' => 'Toggle Title',
-						'value' => $GLOBALS['octoprintHeaderToggle'],
-						'help' => 'Shows/hides the title of this homepage module'
-					),
-				),
-			)
-		),
-	);
-}
 
-function buildHomepageSettings()
+trait HomepageFunctions
 {
-	$homepageOrder = homepageOrder();
-	$homepageList = '<h4>Drag Homepage Items to Order Them</h4><div id="homepage-items-sort" class="external-events">';
-	$inputList = '<form id="homepage-values" class="row">';
-	foreach ($homepageOrder as $key => $val) {
-		switch ($key) {
-			case 'homepageOrdercustomhtml':
-				$class = 'bg-info';
-				$image = 'plugins/images/tabs/custom1.png';
-				if (!$GLOBALS['homepagCustomHTMLoneEnabled']) {
-					$class .= ' faded';
-				}
-				break;
-			case 'homepageOrdercustomhtmlTwo':
-				$class = 'bg-info';
-				$image = 'plugins/images/tabs/custom2.png';
-				if (!$GLOBALS['homepagCustomHTMLtwoEnabled']) {
-					$class .= ' faded';
-				}
-				break;
-			case 'homepageOrdertransmission':
-				$class = 'bg-transmission';
-				$image = 'plugins/images/tabs/transmission.png';
-				if (!$GLOBALS['homepageTransmissionEnabled']) {
-					$class .= ' faded';
-				}
-				break;
-			case 'homepageOrdernzbget':
-				$class = 'bg-nzbget';
-				$image = 'plugins/images/tabs/nzbget.png';
-				if (!$GLOBALS['homepageNzbgetEnabled']) {
-					$class .= ' faded';
-				}
-				break;
-			case 'homepageOrderjdownloader':
-				$class = 'bg-sab';
-				$image = 'plugins/images/tabs/jdownloader.png';
-				if (!$GLOBALS['homepageJdownloaderEnabled']) {
-					$class .= ' faded';
-				}
-				break;
-			case 'homepageOrdersabnzbd':
-				$class = 'bg-sab';
-				$image = 'plugins/images/tabs/sabnzbd.png';
-				if (!$GLOBALS['homepageSabnzbdEnabled']) {
-					$class .= ' faded';
-				}
-				break;
-			case 'homepageOrderdeluge':
-				$class = 'bg-deluge';
-				$image = 'plugins/images/tabs/deluge.png';
-				if (!$GLOBALS['homepageDelugeEnabled']) {
-					$class .= ' faded';
-				}
-				break;
-			case 'homepageOrderqBittorrent':
-				$class = 'bg-qbit';
-				$image = 'plugins/images/tabs/qBittorrent.png';
-				if (!$GLOBALS['homepageqBittorrentEnabled']) {
-					$class .= ' faded';
-				}
-				break;
-			case 'homepageOrderrTorrent':
-				$class = 'bg-qbit';
-				$image = 'plugins/images/tabs/rTorrent.png';
-				if (!$GLOBALS['homepagerTorrentEnabled']) {
-					$class .= ' faded';
-				}
-				break;
-			case 'homepageOrderplexnowplaying':
-			case 'homepageOrderplexrecent':
-			case 'homepageOrderplexplaylist':
-				$class = 'bg-plex';
-				$image = 'plugins/images/tabs/plex.png';
-				if (!$GLOBALS['homepagePlexEnabled']) {
-					$class .= ' faded';
-				}
-				break;
-			case 'homepageOrderembynowplaying':
-			case 'homepageOrderembyrecent':
-				$class = 'bg-emby';
-				$image = 'plugins/images/tabs/emby.png';
-				if (!$GLOBALS['homepageEmbyEnabled']) {
-					$class .= ' faded';
-				}
-				break;
-			case 'homepageOrderombi':
-				$class = 'bg-inverse';
-				$image = 'plugins/images/tabs/ombi.png';
-				if (!$GLOBALS['homepageOmbiEnabled']) {
-					$class .= ' faded';
-				}
-				break;
-			case 'homepageOrdercalendar':
-				$class = 'bg-primary';
-				$image = 'plugins/images/tabs/calendar.png';
-				if (!$GLOBALS['homepageSonarrEnabled'] && !$GLOBALS['homepageRadarrEnabled'] && !$GLOBALS['homepageSickrageEnabled'] && !$GLOBALS['homepageCouchpotatoEnabled']) {
-					$class .= ' faded';
-				}
-				break;
-			case 'homepageOrderdownloader':
-				$class = 'bg-inverse';
-				$image = 'plugins/images/tabs/downloader.png';
-				if (!$GLOBALS['jdownloaderCombine'] && !$GLOBALS['sabnzbdCombine'] && !$GLOBALS['nzbgetCombine'] && !$GLOBALS['rTorrentCombine'] && !$GLOBALS['delugeCombine'] && !$GLOBALS['transmissionCombine'] && !$GLOBALS['qBittorrentCombine']) {
-					$class .= ' faded';
-				}
-				break;
-			case 'homepageOrderhealthchecks':
-				$class = 'bg-healthchecks';
-				$image = 'plugins/images/tabs/healthchecks.png';
-				if (!$GLOBALS['homepageHealthChecksEnabled']) {
-					$class .= ' faded';
-				}
-				break;
-			case 'homepageOrderunifi':
-				$class = 'bg-info';
-				$image = 'plugins/images/tabs/ubnt.png';
-				if (!$GLOBALS['homepageUnifiEnabled']) {
-					$class .= ' faded';
-				}
-				break;
-			case 'homepageOrdertautulli':
-				$class = 'bg-info';
-				$image = 'plugins/images/tabs/tautulli.png';
-				if (!$GLOBALS['homepageTautulliEnabled']) {
-					$class .= ' faded';
-				}
-				break;
-			case 'homepageOrderPihole':
-				$class = 'bg-info';
-				$image = 'plugins/images/tabs/pihole.png';
-				if (!$GLOBALS['homepagePiholeEnabled']) {
-					$class .= ' faded';
-				}
-				break;
-			case 'homepageOrderMonitorr':
-				$class = 'bg-info';
-				$image = 'plugins/images/tabs/monitorr.png';
-				if (!$GLOBALS['homepageMonitorrEnabled']) {
-					$class .= ' faded';
-				}
-				break;
-			case 'homepageOrderWeatherAndAir':
-				$class = 'bg-success';
-				$image = 'plugins/images/tabs/wind.png';
-				if (!$GLOBALS['homepageWeatherAndAirEnabled']) {
-					$class .= ' faded';
-				}
-				break;
-			case 'homepageOrderSpeedtest':
-				$class = 'bg-success';
-				$image = 'plugins/images/tabs/speedtest-icon.png';
-				if (!$GLOBALS['homepageSpeedtestEnabled']) {
-					$class .= ' faded';
-				}
-				break;
-			case 'homepageOrderNetdata':
-				$class = 'bg-success';
-				$image = 'plugins/images/tabs/netdata.png';
-				if (!$GLOBALS['homepageNetdataEnabled']) {
-					$class .= ' faded';
-				}
-				break;
-			case 'homepageOrderOctoprint':
-				$class = 'bg-success';
-				$image = 'plugins/images/tabs/octoprint.png';
-				if (!$GLOBALS['homepageOctoprintEnabled']) {
-					$class .= ' faded';
-				}
-				break;
-			case 'homepageOrderSonarrQueue':
-				$class = 'bg-sonarr';
-				$image = 'plugins/images/tabs/sonarr.png';
-				if (!$GLOBALS['homepageSonarrQueueEnabled']) {
-					$class .= ' faded';
-				}
-				break;
-			case 'homepageOrderRadarrQueue':
-				$class = 'bg-radarr';
-				$image = 'plugins/images/tabs/radarr.png';
-				if (!$GLOBALS['homepageRadarrQueueEnabled']) {
-					$class .= ' faded';
-				}
-				break;
-			default:
-				$class = 'blue-bg';
-				$image = '';
-				break;
-		}
-		$homepageList .= '
-		<div class="col-md-3 col-xs-12 sort-homepage m-t-10 hvr-grow clearfix">
-			<div class="homepage-drag fc-event ' . $class . ' lazyload"  data-src="' . $image . '">
-				<span class="ordinal-position text-uppercase badge bg-org homepage-number" data-link="' . $key . '" style="float:left;width: 30px;">' . $val . '</span>
-				<span class="homepage-text">&nbsp; ' . strtoupper(substr($key, 13)) . '</span>
 
-			</div>
-		</div>
-		';
-		$inputList .= '<input type="hidden" name="' . $key . '">';
-	}
-	$homepageList .= '</div>';
-	$inputList .= '</form>';
-	return $homepageList . $inputList;
-}
-
-function ombiTVDefault($type)
-{
-	switch ($type) {
-		case 'all':
-			return ($type == $GLOBALS['ombiTvDefault']) ? true : false;
-		case 'first':
-			return ($type == $GLOBALS['ombiTvDefault']) ? true : false;
-		case 'last':
-			return ($type == $GLOBALS['ombiTvDefault']) ? true : false;
-		default:
-			return false;
-	}
-	return false;
 }

+ 4 - 69
api/functions/log-functions.php

@@ -1,75 +1,10 @@
 <?php
-function checkLog($path)
-{
-	if (file_exists($path)) {
-		if (filesize($path) > 500000) {
-			rename($path, $path . '[' . date('Y-m-d') . '].json');
-			return false;
-		}
-		return true;
-	} else {
-		return false;
-	}
-}
 
-function writeLoginLog($username, $authType)
+trait LogFunctions
 {
-	$username = htmlspecialchars($username, ENT_QUOTES);
-	if (checkLog($GLOBALS['organizrLoginLog'])) {
-		$getLog = str_replace("\r\ndate", "date", file_get_contents($GLOBALS['organizrLoginLog']));
-		$gotLog = json_decode($getLog, true);
-	}
-	$logEntryFirst = array('logType' => 'login_log', 'auth' => array(array('date' => date("Y-m-d H:i:s"), 'utc_date' => $GLOBALS['currentTime'], 'username' => $username, 'ip' => userIP(), 'auth_type' => $authType)));
-	$logEntry = array('date' => date("Y-m-d H:i:s"), 'utc_date' => $GLOBALS['currentTime'], 'username' => $username, 'ip' => userIP(), 'auth_type' => $authType);
-	if (isset($gotLog)) {
-		array_push($gotLog["auth"], $logEntry);
-		$writeFailLog = str_replace("date", "\r\ndate", json_encode($gotLog));
-	} else {
-		$writeFailLog = str_replace("date", "\r\ndate", json_encode($logEntryFirst));
-	}
-	file_put_contents($GLOBALS['organizrLoginLog'], $writeFailLog);
-}
 
-function writeLog($type = 'error', $message, $username = null)
-{
-	$GLOBALS['timeExecution'] = timeExecution($GLOBALS['timeExecution']);
-	$message = $message . ' [Execution Time: ' . formatSeconds($GLOBALS['timeExecution']) . ']';
-	$username = ($username) ? htmlspecialchars($username, ENT_QUOTES) : $GLOBALS['organizrUser']['username'];
-	if (checkLog($GLOBALS['organizrLog'])) {
-		$getLog = str_replace("\r\ndate", "date", file_get_contents($GLOBALS['organizrLog']));
-		$gotLog = json_decode($getLog, true);
-	}
-	$logEntryFirst = array('logType' => 'organizr_log', 'log_items' => array(array('date' => date("Y-m-d H:i:s"), 'utc_date' => $GLOBALS['currentTime'], 'type' => $type, 'username' => $username, 'ip' => userIP(), 'message' => $message)));
-	$logEntry = array('date' => date("Y-m-d H:i:s"), 'utc_date' => $GLOBALS['currentTime'], 'type' => $type, 'username' => $username, 'ip' => userIP(), 'message' => $message);
-	if (isset($gotLog)) {
-		array_push($gotLog["log_items"], $logEntry);
-		$writeFailLog = str_replace("date", "\r\ndate", json_encode($gotLog));
-	} else {
-		$writeFailLog = str_replace("date", "\r\ndate", json_encode($logEntryFirst));
-	}
-	file_put_contents($GLOBALS['organizrLog'], $writeFailLog);
 }
 
-function getLog($type, $reverse = true)
-{
-	switch ($type) {
-		case 'login':
-		case 'loginLog':
-			$file = $GLOBALS['organizrLoginLog'];
-			$parent = 'auth';
-			break;
-		case 'org':
-		case 'organizrLog':
-			$file = $GLOBALS['organizrLog'];
-			$parent = 'log_items';
-		// no break
-		default:
-			break;
-	}
-	if (!file_exists($file)) {
-		return false;
-	}
-	$getLog = str_replace("\r\ndate", "date", file_get_contents($file));
-	$gotLog = json_decode($getLog, true);
-	return ($reverse) ? array_reverse($gotLog[$parent]) : $gotLog[$parent];
-}
+
+
+

+ 1 - 415
api/functions/netdata-functions.php

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

文件差異過大導致無法顯示
+ 532 - 434
api/functions/normal-functions.php


+ 257 - 252
api/functions/option-functions.php

@@ -1,257 +1,262 @@
 <?php
-function optionLimit()
-{
-	return array(
-		array(
-			'name' => '1 Item',
-			'value' => '1'
-		),
-		array(
-			'name' => '2 Items',
-			'value' => '2'
-		),
-		array(
-			'name' => '3 Items',
-			'value' => '3'
-		),
-		array(
-			'name' => '4 Items',
-			'value' => '4'
-		),
-		array(
-			'name' => '5 Items',
-			'value' => '5'
-		),
-		array(
-			'name' => '6 Items',
-			'value' => '6'
-		),
-		array(
-			'name' => '7 Items',
-			'value' => '7'
-		),
-		array(
-			'name' => '8 Items',
-			'value' => '8'
-		),
-		array(
-			'name' => 'Unlimited',
-			'value' => '1000'
-		),
-	);
-}
-
-function optionNotificationTypes()
-{
-	return array(
-		array(
-			'name' => 'Toastr',
-			'value' => 'toastr'
-		),
-		array(
-			'name' => 'Izi',
-			'value' => 'izi'
-		),
-		array(
-			'name' => 'Alertify',
-			'value' => 'alertify'
-		),
-		array(
-			'name' => 'Noty',
-			'value' => 'noty'
-		),
-	);
-}
-
-function optionNotificationPositions()
-{
-	return array(
-		array(
-			'name' => 'Bottom Right',
-			'value' => 'br'
-		),
-		array(
-			'name' => 'Bottom Left',
-			'value' => 'bl'
-		),
-		array(
-			'name' => 'Bottom Center',
-			'value' => 'bc'
-		),
-		array(
-			'name' => 'Top Right',
-			'value' => 'tr'
-		),
-		array(
-			'name' => 'Top Left',
-			'value' => 'tl'
-		),
-		array(
-			'name' => 'Top Center',
-			'value' => 'tc'
-		),
-		array(
-			'name' => 'Center',
-			'value' => 'c'
-		),
-	);
-}
 
-function optionTime()
+trait OptionsFunction
 {
-	return array(
-		array(
-			'name' => '2.5',
-			'value' => '2500'
-		),
-		array(
-			'name' => '5',
-			'value' => '5000'
-		),
-		array(
-			'name' => '10',
-			'value' => '10000'
-		),
-		array(
-			'name' => '15',
-			'value' => '15000'
-		),
-		array(
-			'name' => '30',
-			'value' => '30000'
-		),
-		array(
-			'name' => '60 [1 Minute]',
-			'value' => '60000'
-		),
-		array(
-			'name' => '300 [5 Minutes]',
-			'value' => '300000'
-		),
-		array(
-			'name' => '600 [10 Minutes]',
-			'value' => '600000'
-		),
-		array(
-			'name' => '900 [15 Minutes]',
-			'value' => '900000'
-		),
-		array(
-			'name' => '1800 [30 Minutes]',
-			'value' => '1800000'
-		),
-		array(
-			'name' => '3600 [1 Hour]',
-			'value' => '3600000'
-		),
-	);
 	
-}
-
-function netdataOptions()
-{
-	return [
-		[
-			'name' => 'Disk Read',
-			'value' => 'disk-read',
-		],
-		[
-			'name' => 'Disk Write',
-			'value' => 'disk-write',
-		],
-		[
-			'name' => 'CPU',
-			'value' => 'cpu'
-		],
-		[
-			'name' => 'Network Inbound',
-			'value' => 'net-in',
-		],
-		[
-			'name' => 'Network Outbound',
-			'value' => 'net-out',
-		],
-		[
-			'name' => 'Used RAM',
-			'value' => 'ram-used',
-		],
-		[
-			'name' => 'Used Swap',
-			'value' => 'swap-used',
-		],
-		[
-			'name' => 'Disk space used',
-			'value' => 'disk-used',
-		],
-		[
-			'name' => 'Disk space available',
-			'value' => 'disk-avail',
-		],
-		[
-			'name' => 'Custom',
-			'value' => 'custom',
-		]
-	];
-}
-
-function netdataChartOptions()
-{
-	return [
-		[
-			'name' => 'Easy Pie Chart',
-			'value' => 'easypiechart',
-		],
-		[
-			'name' => 'Gauge',
-			'value' => 'gauge'
-		]
-	];
-}
-
-function netdataColourOptions()
-{
-	return [
-		[
-			'name' => 'Red',
-			'value' => 'fe3912',
-		],
-		[
-			'name' => 'Green',
-			'value' => '46e302',
-		],
-		[
-			'name' => 'Purple',
-			'value' => 'CC22AA'
-		],
-		[
-			'name' => 'Blue',
-			'value' => '5054e6',
-		],
-		[
-			'name' => 'Yellow',
-			'value' => 'dddd00',
-		],
-		[
-			'name' => 'Orange',
-			'value' => 'd66300',
-		]
-	];
-}
-
-function netdataSizeOptions()
-{
-	return [
-		[
-			'name' => 'Large',
-			'value' => 'lg',
-		],
-		[
-			'name' => 'Medium',
-			'value' => 'md',
-		],
-		[
-			'name' => 'Small',
-			'value' => 'sm'
-		]
-	];
+	public function optionLimit()
+	{
+		return array(
+			array(
+				'name' => '1 Item',
+				'value' => '1'
+			),
+			array(
+				'name' => '2 Items',
+				'value' => '2'
+			),
+			array(
+				'name' => '3 Items',
+				'value' => '3'
+			),
+			array(
+				'name' => '4 Items',
+				'value' => '4'
+			),
+			array(
+				'name' => '5 Items',
+				'value' => '5'
+			),
+			array(
+				'name' => '6 Items',
+				'value' => '6'
+			),
+			array(
+				'name' => '7 Items',
+				'value' => '7'
+			),
+			array(
+				'name' => '8 Items',
+				'value' => '8'
+			),
+			array(
+				'name' => 'Unlimited',
+				'value' => '1000'
+			),
+		);
+	}
+	
+	public function optionNotificationTypes()
+	{
+		return array(
+			array(
+				'name' => 'Toastr',
+				'value' => 'toastr'
+			),
+			array(
+				'name' => 'Izi',
+				'value' => 'izi'
+			),
+			array(
+				'name' => 'Alertify',
+				'value' => 'alertify'
+			),
+			array(
+				'name' => 'Noty',
+				'value' => 'noty'
+			),
+		);
+	}
+	
+	public function optionNotificationPositions()
+	{
+		return array(
+			array(
+				'name' => 'Bottom Right',
+				'value' => 'br'
+			),
+			array(
+				'name' => 'Bottom Left',
+				'value' => 'bl'
+			),
+			array(
+				'name' => 'Bottom Center',
+				'value' => 'bc'
+			),
+			array(
+				'name' => 'Top Right',
+				'value' => 'tr'
+			),
+			array(
+				'name' => 'Top Left',
+				'value' => 'tl'
+			),
+			array(
+				'name' => 'Top Center',
+				'value' => 'tc'
+			),
+			array(
+				'name' => 'Center',
+				'value' => 'c'
+			),
+		);
+	}
+	
+	public function optionTime()
+	{
+		return array(
+			array(
+				'name' => '2.5',
+				'value' => '2500'
+			),
+			array(
+				'name' => '5',
+				'value' => '5000'
+			),
+			array(
+				'name' => '10',
+				'value' => '10000'
+			),
+			array(
+				'name' => '15',
+				'value' => '15000'
+			),
+			array(
+				'name' => '30',
+				'value' => '30000'
+			),
+			array(
+				'name' => '60 [1 Minute]',
+				'value' => '60000'
+			),
+			array(
+				'name' => '300 [5 Minutes]',
+				'value' => '300000'
+			),
+			array(
+				'name' => '600 [10 Minutes]',
+				'value' => '600000'
+			),
+			array(
+				'name' => '900 [15 Minutes]',
+				'value' => '900000'
+			),
+			array(
+				'name' => '1800 [30 Minutes]',
+				'value' => '1800000'
+			),
+			array(
+				'name' => '3600 [1 Hour]',
+				'value' => '3600000'
+			),
+		);
+		
+	}
+	
+	public function netdataOptions()
+	{
+		return [
+			[
+				'name' => 'Disk Read',
+				'value' => 'disk-read',
+			],
+			[
+				'name' => 'Disk Write',
+				'value' => 'disk-write',
+			],
+			[
+				'name' => 'CPU',
+				'value' => 'cpu'
+			],
+			[
+				'name' => 'Network Inbound',
+				'value' => 'net-in',
+			],
+			[
+				'name' => 'Network Outbound',
+				'value' => 'net-out',
+			],
+			[
+				'name' => 'Used RAM',
+				'value' => 'ram-used',
+			],
+			[
+				'name' => 'Used Swap',
+				'value' => 'swap-used',
+			],
+			[
+				'name' => 'Disk space used',
+				'value' => 'disk-used',
+			],
+			[
+				'name' => 'Disk space available',
+				'value' => 'disk-avail',
+			],
+			[
+				'name' => 'Custom',
+				'value' => 'custom',
+			]
+		];
+	}
+	
+	public function netdataChartOptions()
+	{
+		return [
+			[
+				'name' => 'Easy Pie Chart',
+				'value' => 'easypiechart',
+			],
+			[
+				'name' => 'Gauge',
+				'value' => 'gauge'
+			]
+		];
+	}
+	
+	public function netdataColourOptions()
+	{
+		return [
+			[
+				'name' => 'Red',
+				'value' => 'fe3912',
+			],
+			[
+				'name' => 'Green',
+				'value' => '46e302',
+			],
+			[
+				'name' => 'Purple',
+				'value' => 'CC22AA'
+			],
+			[
+				'name' => 'Blue',
+				'value' => '5054e6',
+			],
+			[
+				'name' => 'Yellow',
+				'value' => 'dddd00',
+			],
+			[
+				'name' => 'Orange',
+				'value' => 'd66300',
+			]
+		];
+	}
+	
+	public function netdataSizeOptions()
+	{
+		return [
+			[
+				'name' => 'Large',
+				'value' => 'lg',
+			],
+			[
+				'name' => 'Medium',
+				'value' => 'md',
+			],
+			[
+				'name' => 'Small',
+				'value' => 'sm'
+			]
+		];
+	}
 }

文件差異過大導致無法顯示
+ 303 - 1774
api/functions/organizr-functions.php


+ 1 - 327
api/functions/ping.class.php

@@ -1,328 +1,2 @@
 <?php
-
-class Ping
-{
-	
-	private $host;
-	private $ttl;
-	private $timeout;
-	private $port = 80;
-	private $data = 'Ping';
-	private $commandOutput;
-	
-	/**
-	 * Called when the Ping object is created.
-	 *
-	 * @param string $host
-	 *   The host to be pinged.
-	 * @param int $ttl
-	 *   Time-to-live (TTL) (You may get a 'Time to live exceeded' error if this
-	 *   value is set too low. The TTL value indicates the scope or range in which
-	 *   a packet may be forwarded. By convention:
-	 *     - 0 = same host
-	 *     - 1 = same subnet
-	 *     - 32 = same site
-	 *     - 64 = same region
-	 *     - 128 = same continent
-	 *     - 255 = unrestricted
-	 * @param int $timeout
-	 *   Timeout (in seconds) used for ping and fsockopen().
-	 * @throws \Exception if the host is not set.
-	 */
-	public function __construct($host, $ttl = 255, $timeout = 10)
-	{
-		if (!isset($host)) {
-			throw new \Exception("Error: Host name not supplied.");
-		}
-		$this->host = $host;
-		$this->ttl = $ttl;
-		$this->timeout = $timeout;
-	}
-	
-	/**
-	 * Set the ttl (in hops).
-	 *
-	 * @param int $ttl
-	 *   TTL in hops.
-	 */
-	public function setTtl($ttl)
-	{
-		$this->ttl = $ttl;
-	}
-	
-	/**
-	 * Get the ttl.
-	 *
-	 * @return int
-	 *   The current ttl for Ping.
-	 */
-	public function getTtl()
-	{
-		return $this->ttl;
-	}
-	
-	/**
-	 * Set the timeout.
-	 *
-	 * @param int $timeout
-	 *   Time to wait in seconds.
-	 */
-	public function setTimeout($timeout)
-	{
-		$this->timeout = $timeout;
-	}
-	
-	/**
-	 * Get the timeout.
-	 *
-	 * @return int
-	 *   Current timeout for Ping.
-	 */
-	public function getTimeout()
-	{
-		return $this->timeout;
-	}
-	
-	/**
-	 * Set the host.
-	 *
-	 * @param string $host
-	 *   Host name or IP address.
-	 */
-	public function setHost($host)
-	{
-		$this->host = $host;
-	}
-	
-	/**
-	 * Get the host.
-	 *
-	 * @return string
-	 *   The current hostname for Ping.
-	 */
-	public function getHost()
-	{
-		return $this->host;
-	}
-	
-	/**
-	 * Set the port (only used for fsockopen method).
-	 *
-	 * Since regular pings use ICMP and don't need to worry about the concept of
-	 * 'ports', this is only used for the fsockopen method, which pings servers by
-	 * checking port 80 (by default).
-	 *
-	 * @param int $port
-	 *   Port to use for fsockopen ping (defaults to 80 if not set).
-	 */
-	public function setPort($port)
-	{
-		$this->port = $port;
-	}
-	
-	/**
-	 * Get the port (only used for fsockopen method).
-	 *
-	 * @return int
-	 *   The port used by fsockopen pings.
-	 */
-	public function getPort()
-	{
-		return $this->port;
-	}
-	
-	/**
-	 * Return the command output when method=exec.
-	 * @return string
-	 */
-	public function getCommandOutput()
-	{
-		return $this->commandOutput;
-	}
-	
-	/**
-	 * Matches an IP on command output and returns.
-	 * @return string
-	 */
-	public function getIpAddress()
-	{
-		$out = array();
-		if (preg_match('/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/', $this->commandOutput, $out)) {
-			return $out[0];
-		}
-		return null;
-	}
-	
-	/**
-	 * Ping a host.
-	 *
-	 * @param string $method
-	 *   Method to use when pinging:
-	 *     - exec (default): Pings through the system ping command. Fast and
-	 *       robust, but a security risk if you pass through user-submitted data.
-	 *     - fsockopen: Pings a server on port 80.
-	 *     - socket: Creates a RAW network socket. Only usable in some
-	 *       environments, as creating a SOCK_RAW socket requires root privileges.
-	 *
-	 * @throws InvalidArgumentException if $method is not supported.
-	 *
-	 * @return mixed
-	 *   Latency as integer, in ms, if host is reachable or FALSE if host is down.
-	 */
-	public function ping($method = 'exec')
-	{
-		$latency = false;
-		switch ($method) {
-			case 'exec':
-				$latency = $this->pingExec();
-				break;
-			case 'fsockopen':
-				$latency = $this->pingFsockopen();
-				break;
-			case 'socket':
-				$latency = $this->pingSocket();
-				break;
-			default:
-				throw new \InvalidArgumentException('Unsupported ping method.');
-		}
-		// Return the latency.
-		return $latency;
-	}
-	
-	/**
-	 * The exec method uses the possibly insecure exec() function, which passes
-	 * the input to the system. This is potentially VERY dangerous if you pass in
-	 * any user-submitted data. Be SURE you sanitize your inputs!
-	 *
-	 * @return int
-	 *   Latency, in ms.
-	 */
-	private function pingExec()
-	{
-		$latency = false;
-		$ttl = escapeshellcmd($this->ttl);
-		$timeout = escapeshellcmd($this->timeout);
-		$host = escapeshellcmd($this->host);
-		// Exec string for Windows-based systems.
-		if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
-			// -n = number of pings; -i = ttl; -w = timeout (in milliseconds).
-			$exec_string = 'ping -n 1 -i ' . $ttl . ' -w ' . ($timeout * 1000) . ' ' . $host;
-		} // Exec string for Darwin based systems (OS X).
-		else if (strtoupper(PHP_OS) === 'DARWIN') {
-			// -n = numeric output; -c = number of pings; -m = ttl; -t = timeout.
-			$exec_string = 'ping -n -c 1 -m ' . $ttl . ' -t ' . $timeout . ' ' . $host;
-		} // Exec string for other UNIX-based systems (Linux).
-		else {
-			// -n = numeric output; -c = number of pings; -t = ttl; -W = timeout
-			$exec_string = 'ping -n -c 1 -t ' . $ttl . ' -W ' . $timeout . ' ' . $host . ' 2>&1';
-		}
-		exec($exec_string, $output, $return);
-		// Strip empty lines and reorder the indexes from 0 (to make results more
-		// uniform across OS versions).
-		$this->commandOutput = implode($output, '');
-		$output = array_values(array_filter($output));
-		// If the result line in the output is not empty, parse it.
-		if (!empty($output[1])) {
-			// Search for a 'time' value in the result line.
-			$response = preg_match("/time(?:=|<)(?<time>[\.0-9]+)(?:|\s)ms/", $output[1], $matches);
-			// If there's a result and it's greater than 0, return the latency.
-			if ($response > 0 && isset($matches['time'])) {
-				$latency = round($matches['time'], 2);
-			}
-		}
-		return $latency;
-	}
-	
-	/**
-	 * The fsockopen method simply tries to reach the host on a port. This method
-	 * is often the fastest, but not necessarily the most reliable. Even if a host
-	 * doesn't respond, fsockopen may still make a connection.
-	 *
-	 * @return int
-	 *   Latency, in ms.
-	 */
-	private function pingFsockopen()
-	{
-		$start = microtime(true);
-		// fsockopen prints a bunch of errors if a host is unreachable. Hide those
-		// irrelevant errors and deal with the results instead.
-		$fp = @fsockopen($this->host, $this->port, $errno, $errstr, $this->timeout);
-		if (!$fp) {
-			$latency = false;
-		} else {
-			$latency = microtime(true) - $start;
-			$latency = round($latency * 1000, 2);
-		}
-		return $latency;
-	}
-	
-	/**
-	 * The socket method uses raw network packet data to try sending an ICMP ping
-	 * packet to a server, then measures the response time. Using this method
-	 * requires the script to be run with root privileges, though, so this method
-	 * only works reliably on Windows systems and on Linux servers where the
-	 * script is not being run as a web user.
-	 *
-	 * @return int
-	 *   Latency, in ms.
-	 */
-	private function pingSocket()
-	{
-		// Create a package.
-		$type = "\x08";
-		$code = "\x00";
-		$checksum = "\x00\x00";
-		$identifier = "\x00\x00";
-		$seq_number = "\x00\x00";
-		$package = $type . $code . $checksum . $identifier . $seq_number . $this->data;
-		// Calculate the checksum.
-		$checksum = $this->calculateChecksum($package);
-		// Finalize the package.
-		$package = $type . $code . $checksum . $identifier . $seq_number . $this->data;
-		// Create a socket, connect to server, then read socket and calculate.
-		if ($socket = socket_create(AF_INET, SOCK_RAW, 1)) {
-			socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, array(
-				'sec' => 10,
-				'usec' => 0,
-			));
-			// Prevent errors from being printed when host is unreachable.
-			@socket_connect($socket, $this->host, null);
-			$start = microtime(true);
-			// Send the package.
-			@socket_send($socket, $package, strlen($package), 0);
-			if (socket_read($socket, 255) !== false) {
-				$latency = microtime(true) - $start;
-				$latency = round($latency * 1000, 2);
-			} else {
-				$latency = false;
-			}
-		} else {
-			$latency = false;
-		}
-		// Close the socket.
-		socket_close($socket);
-		return $latency;
-	}
-	
-	/**
-	 * Calculate a checksum.
-	 *
-	 * @param string $data
-	 *   Data for which checksum will be calculated.
-	 *
-	 * @return string
-	 *   Binary string checksum of $data.
-	 */
-	private function calculateChecksum($data)
-	{
-		if (strlen($data) % 2) {
-			$data .= "\x00";
-		}
-		$bit = unpack('n*', $data);
-		$sum = array_sum($bit);
-		while ($sum >> 16) {
-			$sum = ($sum >> 16) + ($sum & 0xffff);
-		}
-		return pack('n*', ~$sum);
-	}
-}
+/* Depreciated */

+ 2 - 77
api/functions/plugin-functions.php

@@ -1,81 +1,6 @@
 <?php
-function installPlugin($plugin)
-{
-	$name = $plugin['data']['plugin']['name'];
-	$version = $plugin['data']['plugin']['version'];
-	foreach ($plugin['data']['plugin']['downloadList'] as $k => $v) {
-		$file = array(
-			'from' => $v['githubPath'],
-			'to' => str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $GLOBALS['root'] . $v['path'] . $v['fileName']),
-			'path' => str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $GLOBALS['root'] . $v['path'])
-		);
-		if (!downloadFileToPath($file['from'], $file['to'], $file['path'])) {
-			writeLog('error', 'Plugin Function -  Downloaded File Failed  for: ' . $v['githubPath'], $GLOBALS['organizrUser']['username']);
-			return false;
-		}
-	}
-	if ($GLOBALS['installedPlugins'] !== '') {
-		$installedPlugins = explode('|', $GLOBALS['installedPlugins']);
-		foreach ($installedPlugins as $k => $v) {
-			$plugins = explode(':', $v);
-			$installedPluginsList[$plugins[0]] = $plugins[1];
-		}
-		if (isset($installedPluginsList[$name])) {
-			$installedPluginsList[$name] = $version;
-			$installedPluginsNew = '';
-			foreach ($installedPluginsList as $k => $v) {
-				if ($installedPluginsNew == '') {
-					$installedPluginsNew .= $k . ':' . $v;
-				} else {
-					$installedPluginsNew .= '|' . $k . ':' . $v;
-				}
-			}
-		} else {
-			$installedPluginsNew = $GLOBALS['installedPlugins'] . '|' . $name . ':' . $version;
-		}
-	} else {
-		$installedPluginsNew = $name . ':' . $version;
-	}
-	updateConfig(array('installedPlugins' => $installedPluginsNew));
-	return 'Success!@!' . $installedPluginsNew;
-}
 
-function removePlugin($plugin)
+trait PluginFunctions
 {
-	$name = $plugin['data']['plugin']['name'];
-	$version = $plugin['data']['plugin']['version'];
-	foreach ($plugin['data']['plugin']['downloadList'] as $k => $v) {
-		$file = array(
-			'from' => $v['githubPath'],
-			'to' => str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $GLOBALS['root'] . $v['path'] . $v['fileName']),
-			'path' => str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $GLOBALS['root'] . $v['path'])
-		);
-		if (!rrmdir($file['to'])) {
-			writeLog('error', 'Plugin Function -  Remove File Failed  for: ' . $v['githubPath'], $GLOBALS['organizrUser']['username']);
-			return false;
-		}
-	}
-	if ($GLOBALS['installedPlugins'] !== '') {
-		$installedPlugins = explode('|', $GLOBALS['installedPlugins']);
-		foreach ($installedPlugins as $k => $v) {
-			$plugins = explode(':', $v);
-			$installedPluginsList[$plugins[0]] = $plugins[1];
-		}
-		if (isset($installedPluginsList[$name])) {
-			$installedPluginsNew = '';
-			foreach ($installedPluginsList as $k => $v) {
-				if ($k !== $name) {
-					if ($installedPluginsNew == '') {
-						$installedPluginsNew .= $k . ':' . $v;
-					} else {
-						$installedPluginsNew .= '|' . $k . ':' . $v;
-					}
-				}
-			}
-		}
-	} else {
-		$installedPluginsNew = '';
-	}
-	updateConfig(array('installedPlugins' => $installedPluginsNew));
-	return 'Success!@!' . $installedPluginsNew;
+
 }

+ 86 - 81
api/functions/sso-functions.php

@@ -1,92 +1,97 @@
 <?php
-function ssoCheck($username, $password, $token = null)
+
+trait SSOFunctions
 {
-	$test = '';
-	if ($GLOBALS['ssoPlex'] && $token) {
-		coookie('set', 'mpt', $token, $GLOBALS['rememberMeDays'], false);
-	}
-	if ($GLOBALS['ssoOmbi']) {
-		$ombiToken = getOmbiToken($username, $password, $token);
-		if ($ombiToken) {
-			coookie('set', 'Auth', $ombiToken, $GLOBALS['rememberMeDays'], false);
+	
+	public function ssoCheck($username, $password, $token = null)
+	{
+		if ($this->config['ssoPlex'] && $token) {
+			$this->coookie('set', 'mpt', $token, $this->config['rememberMeDays'], false);
 		}
-	}
-	if ($GLOBALS['ssoTautulli']) {
-		$tautulliToken = getTautulliToken($username, $password, $token);
-		if ($tautulliToken) {
-			foreach ($tautulliToken as $key => $value) {
-				coookie('set', 'tautulli_token_' . $value['uuid'], $value['token'], $GLOBALS['rememberMeDays'], true, $value['path']);
+		if ($this->config['ssoOmbi']) {
+			$ombiToken = $this->getOmbiToken($username, $password, $token);
+			if ($ombiToken) {
+				$this->coookie('set', 'Auth', $ombiToken, $this->config['rememberMeDays'], false);
+			}
+		}
+		if ($this->config['ssoTautulli']) {
+			$tautulliToken = $this->getTautulliToken($username, $password, $token);
+			if ($tautulliToken) {
+				foreach ($tautulliToken as $key => $value) {
+					$this->coookie('set', 'tautulli_token_' . $value['uuid'], $value['token'], $this->config['rememberMeDays'], true, $value['path']);
+				}
 			}
 		}
+		return true;
 	}
-	return true;
-}
-
-function getOmbiToken($username, $password, $oAuthToken = null)
-{
-	$token = null;
-	try {
-		$url = qualifyURL($GLOBALS['ombiURL']);
-		$headers = array(
-			"Accept" => "application/json",
-			"Content-Type" => "application/json"
-		);
-		$data = array(
-			"username" => ($oAuthToken ? "" : $username),
-			"password" => ($oAuthToken ? "" : $password),
-			"rememberMe" => "true",
-			"plexToken" => $oAuthToken
-		);
-		$endpoint = ($oAuthToken) ? '/api/v1/Token/plextoken' : '/api/v1/Token';
-		$options = (localURL($url)) ? array('verify' => false) : array();
-		$response = Requests::post($url . $endpoint, $headers, json_encode($data), $options);
-		if ($response->success) {
-			$token = json_decode($response->body, true)['access_token'];
-			writeLog('success', 'Ombi Token Function - Grabbed token.', $username);
-		} else {
-			writeLog('error', 'Ombi Token Function - Ombi did not return Token', $username);
+	
+	public function getOmbiToken($username, $password, $oAuthToken = null)
+	{
+		$token = null;
+		try {
+			$url = $this->qualifyURL($this->config['ombiURL']);
+			$headers = array(
+				"Accept" => "application/json",
+				"Content-Type" => "application/json"
+			);
+			$data = array(
+				"username" => ($oAuthToken ? "" : $username),
+				"password" => ($oAuthToken ? "" : $password),
+				"rememberMe" => "true",
+				"plexToken" => $oAuthToken
+			);
+			$endpoint = ($oAuthToken) ? '/api/v1/Token/plextoken' : '/api/v1/Token';
+			$options = ($this->localURL($url)) ? array('verify' => false) : array();
+			$response = Requests::post($url . $endpoint, $headers, json_encode($data), $options);
+			if ($response->success) {
+				$token = json_decode($response->body, true)['access_token'];
+				$this->writeLog('success', 'Ombi Token Function - Grabbed token.', $username);
+			} else {
+				$this->writeLog('error', 'Ombi Token Function - Ombi did not return Token', $username);
+			}
+		} catch (Requests_Exception $e) {
+			$this->writeLog('error', 'Ombi Token Function - Error: ' . $e->getMessage(), $username);
 		}
-	} catch (Requests_Exception $e) {
-		writeLog('error', 'Ombi Token Function - Error: ' . $e->getMessage(), $username);
-	};
-	return ($token) ? $token : false;
-}
-
-function getTautulliToken($username, $password, $plexToken = null)
-{
-	$token = null;
-	$tautulliURLList = explode(',', $GLOBALS['tautulliURL']);
-	if (count($tautulliURLList) !== 0) {
-		foreach ($tautulliURLList as $key => $value) {
-			try {
-				$url = qualifyURL($value);
-				$headers = array(
-					"Accept" => "application/json",
-					"Content-Type" => "application/x-www-form-urlencoded",
-					"User-Agent" => isset($_SERVER ['HTTP_USER_AGENT']) ? $_SERVER ['HTTP_USER_AGENT'] : null
-				);
-				$data = array(
-					"username" => ($plexToken ? "" : $username),
-					"password" => ($plexToken ? "" : $password),
-					"token" => $plexToken,
-					"remember_me" => 1,
-				);
-				$options = (localURL($url)) ? array('verify' => false) : array();
-				$response = Requests::post($url . '/auth/signin', $headers, $data, $options);
-				if ($response->success) {
-					$qualifiedURL = qualifyURL($url, true);
-					$path = ($qualifiedURL['path']) ? $qualifiedURL['path'] : '/';
-					$token[$key]['token'] = json_decode($response->body, true)['token'];
-					$token[$key]['uuid'] = json_decode($response->body, true)['uuid'];
-					$token[$key]['path'] = $path;
-					writeLog('success', 'Tautulli Token Function - Grabbed token from: ' . $url, $username);
-				} else {
-					writeLog('error', 'Tautulli Token Function - Error on URL: ' . $url, $username);
+		return ($token) ? $token : false;
+	}
+	
+	public function getTautulliToken($username, $password, $plexToken = null)
+	{
+		$token = null;
+		$tautulliURLList = explode(',', $this->config['tautulliURL']);
+		if (count($tautulliURLList) !== 0) {
+			foreach ($tautulliURLList as $key => $value) {
+				try {
+					$url = $this->qualifyURL($value);
+					$headers = array(
+						"Accept" => "application/json",
+						"Content-Type" => "application/x-www-form-urlencoded",
+						"User-Agent" => isset($_SERVER ['HTTP_USER_AGENT']) ? $_SERVER ['HTTP_USER_AGENT'] : null
+					);
+					$data = array(
+						"username" => ($plexToken ? "" : $username),
+						"password" => ($plexToken ? "" : $password),
+						"token" => $plexToken,
+						"remember_me" => 1,
+					);
+					$options = ($this->localURL($url)) ? array('verify' => false) : array();
+					$response = Requests::post($url . '/auth/signin', $headers, $data, $options);
+					if ($response->success) {
+						$qualifiedURL = $this->qualifyURL($url, true);
+						$path = ($qualifiedURL['path']) ? $qualifiedURL['path'] : '/';
+						$token[$key]['token'] = json_decode($response->body, true)['token'];
+						$token[$key]['uuid'] = json_decode($response->body, true)['uuid'];
+						$token[$key]['path'] = $path;
+						$this->writeLog('success', 'Tautulli Token Function - Grabbed token from: ' . $url, $username);
+					} else {
+						$this->writeLog('error', 'Tautulli Token Function - Error on URL: ' . $url, $username);
+					}
+				} catch (Requests_Exception $e) {
+					$this->writeLog('error', 'Tautulli Token Function - Error: [' . $url . ']' . $e->getMessage(), $username);
 				}
-			} catch (Requests_Exception $e) {
-				writeLog('error', 'Tautulli Token Function - Error: [' . $url . ']' . $e->getMessage(), $username);
-			};
+			}
 		}
+		return ($token) ? $token : false;
 	}
-	return ($token) ? $token : false;
+	
 }

+ 5 - 144
api/functions/static-globals.php

@@ -1,150 +1,11 @@
 <?php
-// ===================================
-// Organizr Version
-$GLOBALS['installedVersion'] = '2.0.740';
-// ===================================
-// Quick php Version check
-$GLOBALS['minimumPHP'] = '7.1.3';
-if (!(version_compare(PHP_VERSION, $GLOBALS['minimumPHP']) >= 0)) {
-	die('Organizr needs PHP Version: ' . $GLOBALS['minimumPHP'] . '<br/> You have PHP Version: ' . PHP_VERSION);
-}
-// Set GLOBALS from config file
-$GLOBALS['userConfigPath'] = dirname(__DIR__, 1) . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'config.php';
-$GLOBALS['defaultConfigPath'] = dirname(__DIR__, 1) . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'default.php';
-$GLOBALS['currentTime'] = gmdate("Y-m-d\TH:i:s\Z");
-$GLOBALS['docker'] = (file_exists(dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'Docker.txt'));
-$GLOBALS['dev'] = (file_exists(dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'Dev.txt'));
-$GLOBALS['demo'] = (file_exists(dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'Demo.txt'));
-if ($GLOBALS['docker'] && !$GLOBALS['dev']) {
-	$getCommit = file_get_contents(dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'Github.txt');
-	$getCommit = (empty($getCommit)) ? 'n/a' : $getCommit;
-	$GLOBALS['quickCommit'] = $getCommit;
-}
-$GLOBALS['fileHash'] = (isset($GLOBALS['quickCommit'])) ? $GLOBALS['quickCommit'] : $GLOBALS['installedVersion'];
-$GLOBALS['quickConfig'] = (file_exists($GLOBALS['userConfigPath'])) ? loadConfigOnce($GLOBALS['userConfigPath']) : null;
-$GLOBALS['organizrIndexTitle'] = (isset($GLOBALS['quickConfig']['title'])) ? $GLOBALS['quickConfig']['title'] : 'Organizr v2';
-$GLOBALS['organizrIndexDescription'] = (isset($GLOBALS['quickConfig']['description'])) ? $GLOBALS['quickConfig']['description'] : 'Organizr v2';
-// Quick function for plugins
-function pluginFiles($type)
-{
-	$files = '';
-	switch ($type) {
-		case 'js':
-			foreach (glob(dirname(__DIR__, 1) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'js' . DIRECTORY_SEPARATOR . "*.js") as $filename) {
-				$files .= '<script src="api/plugins/js/' . basename($filename) . '?v=' . $GLOBALS['fileHash'] . '" defer="true"></script>';
-			}
-			break;
-		case 'css':
-			foreach (glob(dirname(__DIR__, 1) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'css' . DIRECTORY_SEPARATOR . "*.css") as $filename) {
-				$files .= '<link href="api/plugins/css/' . basename($filename) . '?v=' . $GLOBALS['fileHash'] . '" rel="stylesheet">';
-			}
-			break;
-		default:
-			break;
-	}
-	return $files;
-}
-
-function loadConfigOnce($path = null)
-{
-	$path = ($path) ? $path : $GLOBALS['userConfigPath'];
-	if (!is_file($path)) {
-		return null;
-	} else {
-		return (array)call_user_func(function () use ($path) {
-			return include($path);
-		});
-	}
-}
-
-function formKey($script = true)
-{
-	if (isset($GLOBALS['quickConfig']['organizrHash'])) {
-		if ($GLOBALS['quickConfig']['organizrHash'] !== '') {
-			$hash = password_hash(substr($GLOBALS['quickConfig']['organizrHash'], 2, 10), PASSWORD_BCRYPT);
-			return ($script) ? '<script>local("s","formKey","' . $hash . '");</script>' : $hash;
-		}
-	}
-}
 
-function checkFormKey($formKey = '')
+trait StaticFunctions
 {
-	return password_verify(substr($GLOBALS['quickConfig']['organizrHash'], 2, 10), $formKey);
-}
 
-function favIcons()
-{
-	$favicon = '
-	<link rel="apple-touch-icon" sizes="180x180" href="plugins/images/favicon/apple-touch-icon.png">
-	<link rel="icon" type="image/png" sizes="32x32" href="plugins/images/favicon/favicon-32x32.png">
-	<link rel="icon" type="image/png" sizes="16x16" href="plugins/images/favicon/favicon-16x16.png">
-	<link rel="manifest" href="plugins/images/favicon/site.webmanifest">
-	<link rel="mask-icon" href="plugins/images/favicon/safari-pinned-tab.svg" color="#5bbad5">
-	<link rel="shortcut icon" href="plugins/images/favicon/favicon.ico">
-	<meta name="msapplication-TileColor" content="#da532c">
-	<meta name="msapplication-TileImage" content="plugins/images/favicon/mstile-144x144.png">
-	<meta name="msapplication-config" content="plugins/images/favicon/browserconfig.xml">
-	<meta name="theme-color" content="#ffffff">
-	';
-	if (isset($GLOBALS['quickConfig']['favIcon'])) {
-		if ($GLOBALS['quickConfig']['favIcon'] !== '') {
-			$favicon = $GLOBALS['quickConfig']['favIcon'];
-		}
-	}
-	return $favicon;
 }
 
-function languagePacks($encode = false)
-{
-	$files = array();
-	foreach (glob(dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'js' . DIRECTORY_SEPARATOR . 'langpack' . DIRECTORY_SEPARATOR . "*.json") as $filename) {
-		if (strpos(basename($filename), '[') !== false) {
-			$explode = explode('[', basename($filename));
-			$files[] = array(
-				'filename' => basename($filename),
-				'code' => $explode[0],
-				'language' => matchBrackets(basename($filename))
-			);
-		}
-	}
-	usort($files, function ($a, $b) {
-		return $a['language'] <=> $b['language'];
-	});
-	return ($encode) ? json_encode($files) : $files;
-}
-
-function matchBrackets($text, $brackets = 's')
-{
-	switch ($brackets) {
-		case 's':
-		case 'square':
-			$pattern = '#\[(.*?)\]#';
-			break;
-		case 'c':
-		case 'curly':
-			$pattern = '#\((.*?)\)#';
-			break;
-		default:
-			return null;
-	}
-	preg_match($pattern, $text, $match);
-	return $match[1];
-}
-
-function googleTracking()
-{
-	if (isset($GLOBALS['quickConfig']['gaTrackingID'])) {
-		if ($GLOBALS['quickConfig']['gaTrackingID'] !== '') {
-			return '
-				<script async src="https://www.googletagmanager.com/gtag/js?id=' . $GLOBALS['quickConfig']['gaTrackingID'] . '"></script>
-    			<script>
-				    window.dataLayer = window.dataLayer || [];
-				    function gtag(){dataLayer.push(arguments);}
-				    gtag("js", new Date());
-				    gtag("config","' . $GLOBALS['quickConfig']['gaTrackingID'] . '");
-    			</script>
-			';
-		}
-	}
-	return null;
-}
+// ===================================
+// Organizr Version
+$GLOBALS['installedVersion'] = '2.0.740';
+// ===================================

+ 1 - 80
api/functions/theme-functions.php

@@ -1,81 +1,2 @@
 <?php
-function installTheme($theme)
-{
-	$name = $theme['data']['theme']['name'];
-	$version = $theme['data']['theme']['version'];
-	foreach ($theme['data']['theme']['downloadList'] as $k => $v) {
-		$file = array(
-			'from' => $v['githubPath'],
-			'to' => str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $GLOBALS['root'] . $v['path'] . $v['fileName']),
-			'path' => str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $GLOBALS['root'] . $v['path'])
-		);
-		if (!downloadFileToPath($file['from'], $file['to'], $file['path'])) {
-			writeLog('error', 'Theme Function -  Downloaded File Failed  for: ' . $v['githubPath'], $GLOBALS['organizrUser']['username']);
-			return false;
-		}
-	}
-	if ($GLOBALS['installedThemes'] !== '') {
-		$installedThemes = explode('|', $GLOBALS['installedThemes']);
-		foreach ($installedThemes as $k => $v) {
-			$themes = explode(':', $v);
-			$installedThemesList[$themes[0]] = $themes[1];
-		}
-		if (isset($installedThemesList[$name])) {
-			$installedThemesList[$name] = $version;
-			$installedThemesNew = '';
-			foreach ($installedThemesList as $k => $v) {
-				if ($installedThemesNew == '') {
-					$installedThemesNew .= $k . ':' . $v;
-				} else {
-					$installedThemesNew .= '|' . $k . ':' . $v;
-				}
-			}
-		} else {
-			$installedThemesNew = $GLOBALS['installedThemes'] . '|' . $name . ':' . $version;
-		}
-	} else {
-		$installedThemesNew = $name . ':' . $version;
-	}
-	updateConfig(array('installedThemes' => $installedThemesNew));
-	return 'Success!@!' . $installedThemesNew;
-}
-
-function removeTheme($theme)
-{
-	$name = $theme['data']['theme']['name'];
-	$version = $theme['data']['theme']['version'];
-	foreach ($theme['data']['theme']['downloadList'] as $k => $v) {
-		$file = array(
-			'from' => $v['githubPath'],
-			'to' => str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $GLOBALS['root'] . $v['path'] . $v['fileName']),
-			'path' => str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $GLOBALS['root'] . $v['path'])
-		);
-		if (!rrmdir($file['to'])) {
-			writeLog('error', 'Theme Function -  Remove File Failed  for: ' . $v['githubPath'], $GLOBALS['organizrUser']['username']);
-			return false;
-		}
-	}
-	if ($GLOBALS['installedThemes'] !== '') {
-		$installedThemes = explode('|', $GLOBALS['installedThemes']);
-		foreach ($installedThemes as $k => $v) {
-			$themes = explode(':', $v);
-			$installedThemesList[$themes[0]] = $themes[1];
-		}
-		if (isset($installedThemesList[$name])) {
-			$installedThemesNew = '';
-			foreach ($installedThemesList as $k => $v) {
-				if ($k !== $name) {
-					if ($installedThemesNew == '') {
-						$installedThemesNew .= $k . ':' . $v;
-					} else {
-						$installedThemesNew .= '|' . $k . ':' . $v;
-					}
-				}
-			}
-		}
-	} else {
-		$installedThemesNew = '';
-	}
-	updateConfig(array('installedThemes' => $installedThemesNew));
-	return 'Success!@!' . $installedThemesNew;
-}
+/* Depreciated */

+ 47 - 170
api/functions/token-functions.php

@@ -1,177 +1,54 @@
 <?php
-function jwtParse($token)
-{
-	try {
-		$result = array();
-		$result['valid'] = false;
-		// Check Token with JWT
-		// Set key
-		if (!isset($GLOBALS['organizrHash'])) {
-			return null;
-		}
-		$key = $GLOBALS['organizrHash'];
-		// SHA256 Encryption
-		$signer = new Lcobucci\JWT\Signer\Hmac\Sha256();
-		$jwttoken = (new Lcobucci\JWT\Parser())->parse((string)$token); // Parses from a string
-		$jwttoken->getHeaders(); // Retrieves the token header
-		$jwttoken->getClaims(); // Retrieves the token claims
-		// Start Validation
-		if ($jwttoken->verify($signer, $key)) {
-			$data = new Lcobucci\JWT\ValidationData(); // It will use the current time to validate (iat, nbf and exp)
-			$data->setIssuer('Organizr');
-			$data->setAudience('Organizr');
-			if ($jwttoken->validate($data)) {
-				$result['valid'] = true;
-				$result['username'] = $jwttoken->getClaim('username');
-				$result['group'] = $jwttoken->getClaim('group');
-				$result['groupID'] = $jwttoken->getClaim('groupID');
-				$result['userID'] = $jwttoken->getClaim('userID');
-				$result['email'] = $jwttoken->getClaim('email');
-				$result['image'] = $jwttoken->getClaim('image');
-				$result['tokenExpire'] = $jwttoken->getClaim('exp');
-				$result['tokenDate'] = $jwttoken->getClaim('iat');
-				$result['token'] = $jwttoken->getClaim('exp');
-			}
-		}
-		if ($result['valid'] == true) {
-			return $result;
-		} else {
-			return false;
-		}
-	} catch (\RunException $e) {
-		return false;
-	} catch (\OutOfBoundsException $e) {
-		return false;
-	} catch (\RunTimeException $e) {
-		return false;
-	} catch (\InvalidArgumentException $e) {
-		return false;
-	}
-}
-
-function createToken($username, $email, $image, $group, $groupID, $key, $days = 1)
-{
-	if (!isset($GLOBALS['dbLocation']) || !isset($GLOBALS['dbName'])) {
-		return false;
-	}
-	$days = ($days > 365) ? 365 : $days;
-	//Quick get user ID
-	try {
-		$database = new Dibi\Connection([
-			'driver' => 'sqlite3',
-			'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-		]);
-		$result = $database->fetch('SELECT * FROM users WHERE username = ? COLLATE NOCASE OR email = ? COLLATE NOCASE', $username, $email);
-		// Create JWT
-		// Set key
-		// SHA256 Encryption
-		$signer = new Lcobucci\JWT\Signer\Hmac\Sha256();
-		// Start Builder
-		$jwttoken = (new Lcobucci\JWT\Builder())->setIssuer('Organizr')// Configures the issuer (iss claim)
-		->setAudience('Organizr')// Configures the audience (aud claim)
-		->setId('4f1g23a12aa', true)// Configures the id (jti claim), replicating as a header item
-		->setIssuedAt(time())// Configures the time that the token was issue (iat claim)
-		->setExpiration(time() + (86400 * $days))// Configures the expiration time of the token (exp claim)
-		->set('username', $result['username'])// Configures a new claim, called "username"
-		->set('group', $result['group'])// Configures a new claim, called "group"
-		->set('groupID', $result['group_id'])// Configures a new claim, called "groupID"
-		->set('email', $result['email'])// Configures a new claim, called "email"
-		->set('image', $result['image'])// Configures a new claim, called "image"
-		->set('userID', $result['id'])// Configures a new claim, called "image"
-		->sign($signer, $key)// creates a signature using "testing" as key
-		->getToken(); // Retrieves the generated token
-		$jwttoken->getHeaders(); // Retrieves the token headers
-		$jwttoken->getClaims(); // Retrieves the token claims
-		coookie('set', $GLOBALS['cookieName'], $jwttoken, $days);
-		// Add token to DB
-		$addToken = [
-			'token' => (string)$jwttoken,
-			'user_id' => $result['id'],
-			'created' => $GLOBALS['currentTime'],
-			'browser' => isset($_SERVER ['HTTP_USER_AGENT']) ? $_SERVER ['HTTP_USER_AGENT'] : null,
-			'ip' => userIP(),
-			'expires' => gmdate("Y-m-d\TH:i:s\Z", time() + (86400 * $days))
-		];
-		$database->query('INSERT INTO [tokens]', $addToken);
-		return $jwttoken;
-	} catch (Dibi\Exception $e) {
-		writeLog('error', 'Token Error: ' . $e, 'SYSTEM');
-		return false;
-	}
-}
 
-function validateToken($token, $global = false)
+trait TokenFunctions
 {
-	// Validate script
-	$userInfo = jwtParse($token);
-	$validated = $userInfo ? true : false;
-	if ($global == true) {
-		if ($validated == true) {
-			try {
-				$database = new Dibi\Connection([
-					'driver' => 'sqlite3',
-					'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-				]);
-				$all = $database->fetchAll('SELECT * FROM `tokens` WHERE `user_id` = ? AND `expires` > ?', $userInfo['userID'], $GLOBALS['currentTime']);
-				$tokenCheck = (searchArray($all, 'token', $token) !== false);
-				if (!$tokenCheck) {
-					// Delete cookie & reload page
-					coookie('delete', $GLOBALS['cookieName']);
-					$GLOBALS['organizrUser'] = false;
+	public function jwtParse($token)
+	{
+		try {
+			$result = array();
+			$result['valid'] = false;
+			// Check Token with JWT
+			// Set key
+			if (!isset($this->config['organizrHash'])) {
+				return null;
+			}
+			$key = $this->config['organizrHash'];
+			// SHA256 Encryption
+			$signer = new Lcobucci\JWT\Signer\Hmac\Sha256();
+			$jwttoken = (new Lcobucci\JWT\Parser())->parse((string)$token); // Parses from a string
+			$jwttoken->getHeaders(); // Retrieves the token header
+			$jwttoken->getClaims(); // Retrieves the token claims
+			// Start Validation
+			if ($jwttoken->verify($signer, $key)) {
+				$data = new Lcobucci\JWT\ValidationData(); // It will use the current time to validate (iat, nbf and exp)
+				$data->setIssuer('Organizr');
+				$data->setAudience('Organizr');
+				if ($jwttoken->validate($data)) {
+					$result['valid'] = true;
+					$result['username'] = $jwttoken->getClaim('username');
+					$result['group'] = $jwttoken->getClaim('group');
+					$result['groupID'] = $jwttoken->getClaim('groupID');
+					$result['userID'] = $jwttoken->getClaim('userID');
+					$result['email'] = $jwttoken->getClaim('email');
+					$result['image'] = $jwttoken->getClaim('image');
+					$result['tokenExpire'] = $jwttoken->getClaim('exp');
+					$result['tokenDate'] = $jwttoken->getClaim('iat');
+					$result['token'] = $jwttoken->getClaim('exp');
 				}
-				$result = $database->fetch('SELECT * FROM users WHERE id = ?', $userInfo['userID']);
-				$GLOBALS['organizrUser'] = array(
-					"token" => $token,
-					"tokenDate" => $userInfo['tokenDate'],
-					"tokenExpire" => $userInfo['tokenExpire'],
-					"username" => $result['username'],
-					"uid" => guestHash(0, 5),
-					"group" => $result['group'],
-					"groupID" => $result['group_id'],
-					"email" => $result['email'],
-					"image" => $result['image'],
-					"userID" => $result['id'],
-					"loggedin" => true,
-					"locked" => $result['locked'],
-					"tokenList" => $all,
-					"authService" => explode('::', $result['auth_service'])[0]
-				);
-			} catch (Dibi\Exception $e) {
-				$GLOBALS['organizrUser'] = false;
 			}
-		} else {
-			// Delete cookie & reload page
-			coookie('delete', $GLOBALS['cookieName']);
-			$GLOBALS['organizrUser'] = false;
+			if ($result['valid'] == true) {
+				return $result;
+			} else {
+				return false;
+			}
+		} catch (\RunException $e) {
+			return false;
+		} catch (\OutOfBoundsException $e) {
+			return false;
+		} catch (\RunTimeException $e) {
+			return false;
+		} catch (\InvalidArgumentException $e) {
+			return false;
 		}
-	} else {
-		return $userInfo;
-	}
-	return false;
-}
-
-function getOrganizrUserToken()
-{
-	if (isset($_COOKIE[$GLOBALS['cookieName']])) {
-		// Get token form cookie and validate
-		validateToken($_COOKIE[$GLOBALS['cookieName']], true);
-	} else {
-		$GLOBALS['organizrUser'] = array(
-			"token" => null,
-			"tokenDate" => null,
-			"tokenExpire" => null,
-			"username" => "Guest",
-			"uid" => guestHash(0, 5),
-			"group" => getGuest()['group'],
-			"groupID" => getGuest()['group_id'],
-			"email" => null,
-			//"groupImage"=>getGuest()['image'],
-			"image" => getGuest()['image'],
-			"userID" => null,
-			"loggedin" => false,
-			"locked" => false,
-			"tokenList" => null,
-			"authService" => null
-		);
 	}
-}
+}

+ 104 - 192
api/functions/update-functions.php

@@ -1,205 +1,117 @@
 <?php
-// Upgrade the installation
-function upgradeInstall($branch = 'v2-master', $stage)
-{
-	$notWritable = array_search(false, pathsWritable($GLOBALS['paths']));
-	if ($notWritable == false) {
-		ini_set('max_execution_time', 0);
-		set_time_limit(0);
-		$url = 'https://github.com/causefx/Organizr/archive/' . $branch . '.zip';
-		$file = "upgrade.zip";
-		$source = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'upgrade' . DIRECTORY_SEPARATOR . 'Organizr-' . str_replace('v2', '2', $branch) . DIRECTORY_SEPARATOR;
-		$cleanup = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . "upgrade" . DIRECTORY_SEPARATOR;
-		$destination = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR;
-		switch ($stage) {
-			case '1':
-				writeLog('success', 'Update Function -  Started Upgrade Process', $GLOBALS['organizrUser']['username']);
-				if (downloadFile($url, $file)) {
-					writeLog('success', 'Update Function -  Downloaded Update File for Branch: ' . $branch, $GLOBALS['organizrUser']['username']);
-					return true;
-				} else {
-					writeLog('error', 'Update Function -  Downloaded Update File Failed  for Branch: ' . $branch, $GLOBALS['organizrUser']['username']);
-					return false;
-				}
-				break;
-			case '2':
-				if (unzipFile($file)) {
-					writeLog('success', 'Update Function -  Unzipped Update File for Branch: ' . $branch, $GLOBALS['organizrUser']['username']);
-					return true;
-				} else {
-					writeLog('error', 'Update Function -  Unzip Failed for Branch: ' . $branch, $GLOBALS['organizrUser']['username']);
-					return false;
-				}
-				break;
-			case '3':
-				if (rcopy($source, $destination)) {
-					writeLog('success', 'Update Function -  Overwrited Files using Updated Files from Branch: ' . $branch, $GLOBALS['organizrUser']['username']);
-					$updateComplete = $GLOBALS['dbLocation'] . 'completed.txt';
-					if (!file_exists($updateComplete)) {
-						touch($updateComplete);
-					}
-					return true;
-				} else {
-					writeLog('error', 'Update Function -  Overwrite Failed for Branch: ' . $branch, $GLOBALS['organizrUser']['username']);
-					return false;
-				}
-				break;
-			case '4':
-				if (rrmdir($cleanup)) {
-					writeLog('success', 'Update Function -  Deleted Update Files from Branch: ' . $branch, $GLOBALS['organizrUser']['username']);
-					writeLog('success', 'Update Function -  Update Completed', $GLOBALS['organizrUser']['username']);
-					return true;
-				} else {
-					writeLog('error', 'Update Function -  Removal of Update Files Failed for Branch: ' . $branch, $GLOBALS['organizrUser']['username']);
-					return false;
-				}
-				break;
-			default:
-				return false;
-				break;
-		}
-	} else {
-		return 'permissions';
-	}
-	
-}
 
-function downloadFile($url, $path)
+trait UpdateFunctions
 {
-	ini_set('max_execution_time', 0);
-	set_time_limit(0);
-	$folderPath = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . "upgrade" . DIRECTORY_SEPARATOR;
-	if (!file_exists($folderPath)) {
-		if (@!mkdir($folderPath)) {
-			writeLog('error', 'Update Function -  Folder Creation failed', $GLOBALS['organizrUser']['username']);
+	public function dockerUpdate()
+	{
+		$dockerUpdate = null;
+		chdir('/etc/cont-init.d/');
+		if (file_exists('./30-install')) {
+			$dockerUpdate = shell_exec('./30-install');
+		} elseif (file_exists('./40-install')) {
+			$dockerUpdate = shell_exec('./40-install');
+		}
+		if ($dockerUpdate) {
+			$this->setAPIResponse('success', $dockerUpdate, 200);
+			return true;
+		} else {
+			$this->setAPIResponse('error', 'Update failed', 500);
 			return false;
 		}
 	}
-	$newfname = $folderPath . $path;
-	$context = stream_context_create(
-		array(
-			'ssl' => array(
-				'verify_peer' => true,
-				'cafile' => getCert()
-			)
-		)
-	);
-	$file = fopen($url, 'rb', false, $context);
-	if ($file) {
-		$newf = fopen($newfname, 'wb');
-		if ($newf) {
-			while (!feof($file)) {
-				fwrite($newf, fread($file, 1024 * 8), 1024 * 8);
-			}
+	
+	public function windowsUpdate()
+	{
+		$branch = ($this->config['branch'] == 'v2-master') ? '-m' : '-d';
+		ini_set('max_execution_time', 0);
+		set_time_limit(0);
+		$logFile = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'scripts' . DIRECTORY_SEPARATOR . 'log.txt';
+		$windowsScript = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'scripts' . DIRECTORY_SEPARATOR . 'windows-update.bat ' . $branch . ' > ' . $logFile . ' 2>&1';
+		$windowsUpdate = shell_exec($windowsScript);
+		if ($windowsUpdate) {
+			$this->setAPIResponse('success', $windowsUpdate, 200);
+			return true;
+		} else {
+			$this->setAPIResponse('error', 'Update Complete - check log.txt for output', 500);
+			return false;
 		}
-	} else {
-		writeLog("error", "organizr could not download $url");
-		return false;
-	}
-	if ($file) {
-		fclose($file);
-		writeLog("success", "organizr finished downloading the github zip file");
-	} else {
-		writeLog("error", "organizr could not download the github zip file");
-		return false;
-	}
-	if ($newf) {
-		fclose($newf);
-		writeLog("success", "organizr created upgrade zip file from github zip file");
-	} else {
-		writeLog("error", "organizr could not create upgrade zip file from github zip file");
-		return false;
-	}
-	return true;
-}
-
-function downloadFileToPath($from, $to, $path)
-{
-	ini_set('max_execution_time', 0);
-	set_time_limit(0);
-	if (@!mkdir($path, 0777, true)) {
-		writeLog("error", "organizr could not create upgrade folder");
 	}
-	$file = fopen($from, 'rb');
-	if ($file) {
-		$newf = fopen($to, 'wb');
-		if ($newf) {
-			while (!feof($file)) {
-				fwrite($newf, fread($file, 1024 * 8), 1024 * 8);
-			}
+	
+	public function upgradeInstall($branch = 'v2-master', $stage = '1')
+	{
+		// may kill this function in place for php script to run elsewhere
+		if ($this->docker) {
+			$this->setAPIResponse('error', 'Cannot perform update action on docker install - use script', 500);
+			return false;
 		}
-	} else {
-		writeLog("error", "organizr could not download $url");
-	}
-	if ($file) {
-		fclose($file);
-		writeLog("success", "organizr finished downloading the github zip file");
-	} else {
-		writeLog("error", "organizr could not download the github zip file");
-	}
-	if ($newf) {
-		fclose($newf);
-		writeLog("success", "organizr created upgrade zip file from github zip file");
-	} else {
-		writeLog("error", "organizr could not create upgrade zip file from github zip file");
-	}
-	return true;
-}
-
-function unzipFile($zipFile)
-{
-	ini_set('max_execution_time', 0);
-	set_time_limit(0);
-	$zip = new ZipArchive;
-	$extractPath = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . "upgrade/";
-	if ($zip->open($extractPath . $zipFile) != "true") {
-		writeLog("error", "organizr could not unzip upgrade.zip");
-	} else {
-		writeLog("success", "organizr unzipped upgrade.zip");
-	}
-	/* Extract Zip File */
-	$zip->extractTo($extractPath);
-	$zip->close();
-	return true;
-}
-
-// Function to remove folders and files
-function rrmdir($dir)
-{
-	ini_set('max_execution_time', 0);
-	set_time_limit(0);
-	if (is_dir($dir)) {
-		$files = scandir($dir);
-		foreach ($files as $file) {
-			if ($file != "." && $file != "..") {
-				rrmdir("$dir/$file");
-			}
+		if ($this->getOS() == 'win') {
+			$this->setAPIResponse('error', 'Cannot perform update action on windows install - use script', 500);
+			return false;
 		}
-		rmdir($dir);
-	} elseif (file_exists($dir)) {
-		unlink($dir);
-	}
-	return true;
-}
-
-// Function to Copy folders and files
-function rcopy($src, $dst)
-{
-	ini_set('max_execution_time', 0);
-	set_time_limit(0);
-	$src = cleanPath($src);
-	$dst = cleanPath($dst);
-	if (is_dir($src)) {
-		if (!file_exists($dst)) : mkdir($dst);
-		endif;
-		$files = scandir($src);
-		foreach ($files as $file) {
-			if ($file != "." && $file != "..") {
-				rcopy("$src/$file", "$dst/$file");
+		$notWritable = array_search(false, $this->pathsWritable($this->paths));
+		if ($notWritable == false) {
+			ini_set('max_execution_time', 0);
+			set_time_limit(0);
+			$url = 'https://github.com/causefx/Organizr/archive/' . $branch . '.zip';
+			$file = "upgrade.zip";
+			$source = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'upgrade' . DIRECTORY_SEPARATOR . 'Organizr-' . str_replace('v2', '2', $branch) . DIRECTORY_SEPARATOR;
+			$cleanup = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . "upgrade" . DIRECTORY_SEPARATOR;
+			$destination = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR;
+			switch ($stage) {
+				case '1':
+					$this->writeLog('success', 'Update Function -  Started Upgrade Process', $this->user['username']);
+					if ($this->downloadFile($url, $file)) {
+						$this->writeLog('success', 'Update Function -  Downloaded Update File for Branch: ' . $branch, $this->user['username']);
+						$this->setAPIResponse('success', 'Downloaded file successfully', 200);
+						return true;
+					} else {
+						$this->writeLog('error', 'Update Function -  Downloaded Update File Failed  for Branch: ' . $branch, $this->user['username']);
+						$this->setAPIResponse('error', 'Download failed', 500);
+						return false;
+					}
+				case '2':
+					if ($this->unzipFile($file)) {
+						$this->writeLog('success', 'Update Function -  Unzipped Update File for Branch: ' . $branch, $this->user['username']);
+						$this->setAPIResponse('success', 'Unzipped file successfully', 200);
+						return true;
+					} else {
+						$this->writeLog('error', 'Update Function -  Unzip Failed for Branch: ' . $branch, $this->user['username']);
+						$this->setAPIResponse('error', 'Unzip failed', 500);
+						return false;
+					}
+				case '3':
+					if ($this->rcopy($source, $destination)) {
+						$this->writeLog('success', 'Update Function -  Files overwritten using Updated Files from Branch: ' . $branch, $this->user['username']);
+						$updateComplete = $this->config['dbLocation'] . 'completed.txt';
+						if (!file_exists($updateComplete)) {
+							touch($updateComplete);
+						}
+						$this->setAPIResponse('success', 'Files replaced successfully', 200);
+						return true;
+					} else {
+						$this->writeLog('error', 'Update Function -  Overwrite Failed for Branch: ' . $branch, $this->user['username']);
+						$this->setAPIResponse('error', 'File replacement failed', 500);
+						return false;
+					}
+				case '4':
+					if ($this->rrmdir($cleanup)) {
+						$this->writeLog('success', 'Update Function -  Deleted Update Files from Branch: ' . $branch, $this->user['username']);
+						$this->writeLog('success', 'Update Function -  Update Completed', $this->user['username']);
+						$this->setAPIResponse('success', 'Removed update files successfully', 200);
+						return true;
+					} else {
+						$this->writeLog('error', 'Update Function -  Removal of Update Files Failed for Branch: ' . $branch, $this->user['username']);
+						$this->setAPIResponse('error', 'File removal failed', 500);
+						return false;
+					}
+				default:
+					$this->setAPIResponse('error', 'Action not setup', 500);
+					return false;
 			}
+		} else {
+			$this->setAPIResponse('error', 'File permissions not set correctly', 500);
+			return false;
 		}
-	} elseif (file_exists($src)) {
-		copy($src, $dst);
+		
 	}
-	return true;
-}
+}

+ 33 - 50
api/functions/upgrade-functions.php

@@ -1,53 +1,36 @@
 <?php
-function upgradeCheck()
+
+trait UpgradeFunctions
 {
-	if (file_exists($GLOBALS['userConfigPath'])) {
-		$tempLock = $GLOBALS['dbLocation'] . 'DBLOCK.txt';
-		$updateComplete = $GLOBALS['dbLocation'] . 'completed.txt';
-		$cleanup = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . "upgrade" . DIRECTORY_SEPARATOR;
-		if (file_exists($updateComplete)) {
-			@unlink($updateComplete);
-			@rrmdir($cleanup);
-		}
-		if (file_exists($tempLock)) {
-			die('upgrading');
-		}
-		$updateDB = false;
-		$updateSuccess = true;
-		$compare = new Composer\Semver\Comparator;
-		$oldVer = $GLOBALS['configVersion'];
-		// Upgrade check start for version below
-		$versionCheck = '2.0.0-beta-200';
-		if ($compare->lessThan($oldVer, $versionCheck)) {
-			$updateDB = true;
-			$oldVer = $versionCheck;
-		}
-		// End Upgrade check start for version above
-		// Upgrade check start for version below
-		$versionCheck = '2.0.0-beta-500';
-		if ($compare->lessThan($oldVer, $versionCheck)) {
-			$updateDB = true;
-			$oldVer = $versionCheck;
-		}
-		// End Upgrade check start for version above
-		$versionCheck = '2.0.0-beta-800';
-		if ($compare->lessThan($oldVer, $versionCheck)) {
-			$updateDB = true;
-			$oldVer = $versionCheck;
-		}
-		// End Upgrade check start for version above
-		if ($updateDB == true) {
-			//return 'Upgraded Needed - Current Version '.$oldVer.' - New Version: '.$versionCheck;
-			// Upgrade database to latest version
-			$updateSuccess = (updateDB($oldVer)) ? true : false;
-		}
-		// Update config.php version if different to the installed version
-		if ($updateSuccess && $GLOBALS['installedVersion'] !== $GLOBALS['configVersion']) {
-			updateConfig(array('apply_CONFIG_VERSION' => $GLOBALS['installedVersion']));
-		}
-		if ($updateSuccess == false) {
-			die('Database update failed - Please manually check logs and fix - Then reload this page');
-		}
-		return true;
+	public function upgradeSettingsTabURL()
+	{
+		$response = [
+			array(
+				'function' => 'query',
+				'query' => array(
+					'UPDATE tabs SET',
+					['url' => 'api/v2/page/settings'],
+					'WHERE url = ?',
+					'api/?v1/settings/page'
+				)
+			),
+		];
+		return $this->processQueries($response);
 	}
-}
+	
+	public function upgradeHomepageTabURL()
+	{
+		$response = [
+			array(
+				'function' => 'query',
+				'query' => array(
+					'UPDATE tabs SET',
+					['url' => 'api/v2/page/homepage'],
+					'WHERE url = ?',
+					'api/?v1/homepage/page'
+				)
+			),
+		];
+		return $this->processQueries($response);
+	}
+}

部分文件因文件數量過多而無法顯示