4
0
Эх сурвалжийг харах

Merge pull request #280 from causefx/cero-dev

Cero dev
causefx 9 жил өмнө
parent
commit
d7d1bd855c

+ 2 - 0
.gitignore

@@ -57,8 +57,10 @@ _config.yml
 test.php
 users.db
 config/config.php
+config/config.bak.php
 config/users/
 config/users
 config/users.db
+config/users.bak.db
 
 

+ 36 - 11
ajax.php

@@ -5,22 +5,15 @@ require_once('functions.php');
 // Upgrade environment
 upgradeCheck();
 
-// Define Version
- define('INSTALLEDVERSION', '1.31');
-
 // Lazyload settings
 $databaseConfig = configLazy('config/config.php');
 
-// Authorization
-	# Check if user is currently active and allowed to access resource
-	//require_once("user.php");
-	# ^^ I think adding this does that?
-	
 // Get Action
 if (isset($_POST['submit'])) { $action = $_POST['submit']; }
 if (isset($_POST['action'])) { $action = $_POST['action']; }
 if (isset($_GET['action'])) { $action = $_GET['action']; }
 if (isset($_GET['a'])) { $action = $_GET['a']; }
+unset($_POST['action']);
 
 // No Action
 if (!isset($action)) {
@@ -32,29 +25,44 @@ switch ($_SERVER['REQUEST_METHOD']) {
 	case 'GET':
 		switch ($action) {
 			case 'emby-image':
+				qualifyUser(EMBYHOMEAUTH, true);
 				getEmbyImage();
 				break;
 			case 'plex-image':
+				qualifyUser(PLEXHOMEAUTH, true);
 				getPlexImage();
 				break;
 			case 'emby-streams':
+				qualifyUser(EMBYHOMEAUTH, true);
 				echo getEmbyStreams(12);
 				break;
 			case 'plex-streams':
+				qualifyUser(PLEXHOMEAUTH, true);
 				echo getPlexStreams(12);
 				break;
 			case 'emby-recent':
+				qualifyUser(EMBYHOMEAUTH, true);
 				echo getEmbyRecent($_GET['type'], 12);
 				break;
 			case 'plex-recent':
+				qualifyUser(PLEXHOMEAUTH, true);
 				echo getPlexRecent($_GET['type'], 12);
 				break;
-			
+			case 'sabnzbd-update':
+				qualifyUser(NZBGETHOMEAUTH, true);
+				
+				break;
+			case 'nzbget-update':
+				qualifyUser(NZBGETHOMEAUTH, true);
+				
+				break;
 			default:
 				debug_out('Unsupported Action!',1);
 		}
 		break;
 	case 'POST':
+		// Check if the user is an admin and is allowed to commit values
+		qualifyUser('admin', true);
 		switch ($action) {
 			case 'upload-images':
 				uploadFiles('images/', array('jpg', 'png', 'svg', 'jpeg', 'bmp'));
@@ -62,19 +70,36 @@ switch ($_SERVER['REQUEST_METHOD']) {
 			case 'remove-images':
 				removeFiles('images/'.(isset($_POST['file'])?$_POST['file']:''));
 				break;
+			case 'update-config':
+				sendNotification(updateConfig($_POST));
+				break;
 			case 'editCSS':
 				write_ini_file($_POST["css-show"], "custom.css");
 				echo '<script>window.top.location = window.top.location.href.split(\'#\')[0];</script>';
 				break;
+			case 'update-appearance':
+				sendNotification(updateDBOptions($_POST));
+				break;
+			case 'deleteDB':
+				deleteDatabase();
+				echo json_encode(array('result' => 'success'));
+				break;
+			case 'upgradeInstall':
+				upgradeInstall();
+				echo json_encode(array('result' => 'success'));
+				break;
+			case 'deleteLog':
+				sendNotification(unlink(FAIL_LOG));
+				break;
 			default:
 				debug_out('Unsupported Action!',1);
 		}
 		break;
 	case 'PUT':
-		
+		debug_out('Unsupported Action!',1);
 		break;
 	case 'DELETE':
-		
+		debug_out('Unsupported Action!',1);
 		break;
 	default:
 		debug_out('Unknown Request Type!',1);

+ 2 - 0
config/.htaccess

@@ -0,0 +1,2 @@
+order deny,allow
+deny from all

+ 11 - 2
config/configDefaults.php

@@ -9,25 +9,33 @@ return array(
 	"plexRecentTV" => "false",
 	"plexRecentMusic" => "false",
 	"plexPlayingNow" => "false",
+	"plexHomeAuth" => false,
 	"embyURL" => "",
 	"embyToken" => "",
 	"embyRecentMovie" => "false",
 	"embyRecentTV" => "false",
 	"embyRecentMusic" => "false",
 	"embyPlayingNow" => "false",
+	"embyHomeAuth" => false,
 	"sonarrURL" => "",
 	"sonarrKey" => "",
+	"sonarrHomeAuth" => false,
 	"sickrageURL" => "",
 	"sickrageKey" => "",
+	"sickrageHomeAuth" => false,
 	"radarrURL" => "",
 	"radarrKey" => "",
+	"radarrHomeAuth" => false,
 	"nzbgetURL" => "",
 	"nzbgetUsername" => "",
 	"nzbgetPassword" => "",
+	"nzbgetHomeAuth" => false,
 	"sabnzbdURL" => "",
 	"sabnzbdKey" => "",
+	"sabnzbdHomeAuth" => false,
 	"headphonesURL" => "",
 	"headphonesKey" => "",
+	"headphonesHomeAuth" => false,
 	"calendarStart" => "0",
 	"calendarView" => "basicWeek",
 	"calendarStartDay" => "30",
@@ -36,7 +44,6 @@ return array(
 	"authBackend" => "",
 	"authBackendCreate" => "true",
 	"authBackendHost" => "",
-	"authBackendPort" => "",
 	"authBackendDomain" => "",
 	"plexUsername" => "",
 	"plexPassword" => "",
@@ -57,5 +64,7 @@ return array(
 	"loadingScreen" => "true",
 	"enableMail" => "false",
 	"slimBar" => "true",
-	"gravatar" => "true"
+	"gravatar" => "true",
+	"calTimeFormat" => "h(:mm)t",
+	"homePageAuthNeeded" => false,
 );

+ 11 - 0
css/settings.css

@@ -0,0 +1,11 @@
+div.colour-field {
+    border: 1px solid #cbd3d5;
+    border-radius: 5px;
+    padding-top: 10px;
+    padding-bottom: 10px;
+    margin-bottom: 15px;
+}
+
+div.colour-field input {
+	color: #000;
+}

+ 3 - 3
error.php

@@ -125,9 +125,9 @@ endif;
 
         <title><?=$errorTitle;?></title>
 
-        <link rel="stylesheet" href="/bower_components/bootstrap/dist/css/bootstrap.min.css">
-        <link rel="stylesheet" href="/bower_components/Waves/dist/waves.min.css"> 
-        <link rel="stylesheet" href="/css/style.css">
+        <link rel="stylesheet" href="<?php echo dirname($_SERVER['SCRIPT_NAME']); ?>/bower_components/bootstrap/dist/css/bootstrap.min.css">
+        <link rel="stylesheet" href="<?php echo dirname($_SERVER['SCRIPT_NAME']); ?>/bower_components/Waves/dist/waves.min.css"> 
+        <link rel="stylesheet" href="<?php echo dirname($_SERVER['SCRIPT_NAME']); ?>/css/style.css">
         
     </head>
 

+ 766 - 44
functions.php

@@ -1,5 +1,10 @@
 <?php
 
+// ===================================
+// Define Version
+ define('INSTALLEDVERSION', '1.33');
+// ===================================
+
 // Debugging output functions
 function debug_out($variable, $die = false) {
 	$trace = debug_backtrace()[0];
@@ -8,12 +13,23 @@ function debug_out($variable, $die = false) {
 }
 
 // ==== Auth Plugins START ====
-
 if (function_exists('ldap_connect')) :
 	// Pass credentials to LDAP backend
 	function plugin_auth_ldap($username, $password) {
+		$ldapServers = explode(',',AUTHBACKENDHOST);
+		foreach($ldapServers as $key => $value) {
+			// Calculate parts
+			$digest = parse_url(trim($value));
+			$scheme = strtolower((isset($digest['scheme'])?$digest['scheme']:'ldap'));
+			$host = (isset($digest['host'])?$digest['host']:(isset($digest['path'])?$digest['path']:''));
+			$port = (isset($digest['port'])?$digest['port']:(strtolower($scheme)=='ldap'?389:636));
+			
+			// Reassign
+			$ldapServers[$key] = $scheme.'://'.$host.':'.$port;
+		}
+		
 		// returns true or false
-		$ldap = ldap_connect(AUTHBACKENDHOST.(AUTHBACKENDPORT?':'.AUTHBACKENDPORT:'389'));
+		$ldap = ldap_connect(implode(' ',$ldapServers));
 		if ($bind = ldap_bind($ldap, AUTHBACKENDDOMAIN.'\\'.$username, $password)) {
 			return true;
 		} else {
@@ -21,19 +37,36 @@ if (function_exists('ldap_connect')) :
 		}
 		return false;
 	}
+else :
+	// Ldap Auth Missing Dependancy
+	function plugin_auth_ldap_disabled() {
+		return 'LDAP - Disabled (Dependancy: php-ldap missing!)';
+	}
 endif;
 
 // Pass credentials to FTP backend
 function plugin_auth_ftp($username, $password) {
-	// returns true or false
+	// Calculate parts
+	$digest = parse_url(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);
 	
-	// Connect to FTP
-	$conn_id = ftp_ssl_connect(AUTHBACKENDHOST, (AUTHBACKENDPORT?AUTHBACKENDPORT:21), 20); // 20 Second Timeout
+	// 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 {
+		debug_out('Invalid FTP scheme. Use ftp or ftps');
+		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) {
@@ -49,13 +82,7 @@ function plugin_auth_ftp($username, $password) {
 
 // Pass credentials to Emby Backend
 function plugin_auth_emby_local($username, $password) {
-	$urlCheck = stripos(AUTHBACKENDHOST, "http");
-	if ($urlCheck === false) {
-		$embyAddress = "http://" . AUTHBACKENDHOST;
-	} else {
-		$embyAddress = AUTHBACKENDHOST;	
-	}
-	if(AUTHBACKENDPORT !== ""){ $embyAddress .= ":" . AUTHBACKENDPORT; }
+	$embyAddress = qualifyURL(EMBYURL);
 	
 	$headers = array(
 		'Authorization'=> 'MediaBrowser UserId="e8837bc1-ad67-520e-8cd2-f629e3155721", Client="None", Device="Organizr", DeviceId="xxx", Version="1.0.0.0"',
@@ -86,18 +113,17 @@ function plugin_auth_emby_local($username, $password) {
 if (function_exists('curl_version')) :
 	// Authenticate Against Emby Local (first) and Emby Connect
 	function plugin_auth_emby_all($username, $password) {
-		return plugin_auth_emby_local($username, $password) || plugin_auth_emby_connect($username, $password);
+		$localResult = plugin_auth_emby_local($username, $password);
+		if ($localResult) {
+			return $localResult;
+		} else {
+			return plugin_auth_emby_connect($username, $password);
+		}
 	}
 	
 	// Authenicate against emby connect
 	function plugin_auth_emby_connect($username, $password) {
-		$urlCheck = stripos(AUTHBACKENDHOST, "http");
-		if ($urlCheck === false) {
-			$embyAddress = "http://" . AUTHBACKENDHOST;
-		} else {
-			$embyAddress = AUTHBACKENDHOST;	
-		}
-		if(AUTHBACKENDPORT !== "") { $embyAddress .= ":" . AUTHBACKENDPORT; }
+		$embyAddress = qualifyURL(EMBYURL);
 		
 		// Get A User
 		$connectId = '';
@@ -129,7 +155,10 @@ if (function_exists('curl_version')) :
 				if (isset($result['content'])) {
 					$json = json_decode($result['content'], true);
 					if (is_array($json) && isset($json['AccessToken']) && isset($json['User']) && $json['User']['Id'] == $connectId) {
-						return true;
+						return array(
+							'email' => $json['User']['Email'],
+							'image' => $json['User']['ImageUrl'],
+						);
 					}
 				}
 			}
@@ -146,7 +175,6 @@ if (function_exists('curl_version')) :
 		}
 		
 		//Get User List
-		$approvedUsers = array();
 		$userURL = 'https://plex.tv/pms/friends/all';
 		$userHeaders = array(
 			'Authorization' => 'Basic '.base64_encode(PLEXUSERNAME.':'.PLEXPASSWORD), 
@@ -154,7 +182,6 @@ if (function_exists('curl_version')) :
 		$userXML = simplexml_load_string(curl_get($userURL, $userHeaders));
 		
 		if (is_array($userXML) || is_object($userXML)) {
-			//Build User List array
 			$isUser = false;
 			$usernameLower = strtolower($username);
 			foreach($userXML AS $child) {
@@ -181,14 +208,32 @@ if (function_exists('curl_version')) :
 				$result = curl_post($connectURL, $body, $headers);
 				if (isset($result['content'])) {
 					$json = json_decode($result['content'], true);
-					if (is_array($json) && isset($json['user']) && isset($json['user']['username']) && $json['user']['username'] == $username) {
-						return true;
+					if (is_array($json) && isset($json['user']) && isset($json['user']['username']) && strtolower($json['user']['username']) == $usernameLower) {
+                        return array(
+							'email' => $json['user']['email'],
+							'image' => $json['user']['thumb']
+						);
 					}
 				}
 			}
 		}
 		return false;
 	}
+else :
+	// Plex Auth Missing Dependancy
+	function plugin_auth_plex_disabled() {
+		return 'Plex - Disabled (Dependancy: php-curl missing!)';
+	}
+	
+	// Emby Connect Auth Missing Dependancy
+	function plugin_auth_emby_connect_disabled() {
+		return 'Emby Connect - Disabled (Dependancy: php-curl missing!)';
+	}
+	
+	// Emby Both Auth Missing Dependancy
+	function plugin_auth_emby_both_disabled() {
+		return 'Emby Both - Disabled (Dependancy: php-curl missing!)';
+	}
 endif;
 // ==== Auth Plugins END ====
 // ==== General Class Definitions START ====
@@ -398,24 +443,24 @@ function resolveEmbyItem($address, $token, $item) {
 	// Get Item Details
 	$itemDetails = json_decode(file_get_contents($address.'/Items?Ids='.$item['Id'].'&Fields=Overview&api_key='.$token),true)['Items'][0];
 	
-	switch ($item['Type']) {
+	switch ($itemDetails['Type']) {
 		case 'Episode':
-			$title = $item['SeriesName'].': '.$item['Name'].' (Season '.$item['ParentIndexNumber'].': Episode '.$item['IndexNumber'].')';
+			$title = $itemDetails['SeriesName'].': '.$itemDetails['Name'].(isset($itemDetails['ParentIndexNumber']) && isset($itemDetails['IndexNumber'])?' (Season '.$itemDetails['ParentIndexNumber'].': Episode '.$itemDetails['IndexNumber'].')':'');
 			$imageId = $itemDetails['SeriesId'];
 			$width = 100;
 			$image = 'carousel-image season';
 			$style = '';
 			break;
 		case 'MusicAlbum':
-			$title = $item['Name'];
+			$title = $itemDetails['Name'];
 			$imageId = $itemDetails['Id'];
 			$width = 150;
 			$image = 'music';
 			$style = 'left: 160px !important;';
 			break;
 		default:
-			$title = $item['Name'];
-			$imageId = $item['Id'];
+			$title = $itemDetails['Name'];
+			$imageId = $itemDetails['Id'];
 			$width = 100;
 			$image = 'carousel-image movie';
 			$style = '';
@@ -427,7 +472,7 @@ function resolveEmbyItem($address, $token, $item) {
 	}
 	
 	// Assemble Item And Cache Into Array 
-	return '<div class="item"><a href="'.$address.'/web/itemdetails.html?id='.$item['Id'].'" target="_blank"><img alt="'.$item['Name'].'" class="'.$image.'" src="ajax.php?a=emby-image&img='.$imageId.'&height='.$height.'&width='.$width.'"></a><div class="carousel-caption" style="'.$style.'"><h4>'.$title.'</h4><small><em>'.$itemDetails['Overview'].'</em></small></div></div>';
+	return '<div class="item"><a href="'.$address.'/web/itemdetails.html?id='.$itemDetails['Id'].'" target="_blank"><img alt="'.$itemDetails['Name'].'" class="'.$image.'" src="ajax.php?a=emby-image&img='.$imageId.'&height='.$height.'&width='.$width.'"></a><div class="carousel-caption" style="'.$style.'"><h4>'.$title.'</h4><small><em>'.$itemDetails['Overview'].'</em></small></div></div>';
 }
 
 // Format item from Plex for Carousel
@@ -444,13 +489,24 @@ function resolvePlexItem($server, $token, $item) {
 			$width = 100;
 			$image = 'carousel-image season';
 			$style = '';
+            $thumb = $item['thumb'];
+			break;
+        case 'episode':
+			$title = $item['grandparentTitle'];
+			$summary = $item['title'];
+			$width = 100;
+			$image = 'carousel-image season';
+			$style = '';
+            $thumb = $item['parentThumb'];
 			break;
 		case 'album':
+		case 'track':
 			$title = $item['parentTitle'];
 			$summary = $item['title'];
 			$width = 150;
 			$image = 'album';
 			$style = 'left: 160px !important;';
+            $thumb = $item['thumb'];
 			break;
 		default:
 			$title = $item['title'];
@@ -458,6 +514,7 @@ function resolvePlexItem($server, $token, $item) {
 			$width = 100;
 			$image = 'carousel-image movie';
 			$style = '';
+            $thumb = $item['thumb'];
 	}
 	
 	// If No Overview
@@ -466,7 +523,7 @@ function resolvePlexItem($server, $token, $item) {
 	}
 	
 	// Assemble Item And Cache Into Array 
-	return '<div class="item"><a href="'.$address.'" target="_blank"><img alt="'.$item['Name'].'" class="'.$image.'" src="ajax.php?a=plex-image&img='.$item['thumb'].'&height='.$height.'&width='.$width.'"></a><div class="carousel-caption" style="'.$style.'"><h4>'.$title.'</h4><small><em>'.$summary.'</em></small></div></div>';
+	return '<div class="item"><a href="'.$address.'" target="_blank"><img alt="'.$item['Name'].'" class="'.$image.'" src="ajax.php?a=plex-image&img='.$thumb.'&height='.$height.'&width='.$width.'"></a><div class="carousel-caption" style="'.$style.'"><h4>'.$title.'</h4><small><em>'.$summary.'</em></small></div></div>';
 }
 
 // Create Carousel
@@ -676,9 +733,8 @@ function translate($string) {
 }
 
 // Generate Random string
-function randString($length = 10) {
+function randString($length = 10, $chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ') {
 	$tmp = '';
-	$chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; // 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
 	for ($i = 0; $i < $length; $i++) {
 		$tmp .= substr(str_shuffle($chars), 0, 1);
 	}
@@ -687,7 +743,16 @@ function randString($length = 10) {
 
 // Create config file in the return syntax
 function createConfig($array, $path = 'config/config.php', $nest = 0) {
+	// Define Initial Value
 	$output = array();
+	
+	// Sort Items
+	ksort($array);
+	
+	// Unset the current version
+	unset($array['CONFIG_VERSION']);
+	
+	// Process Settings
 	foreach ($array as $k => $v) {
 		$allowCommit = true;
 		switch (gettype($v)) {
@@ -715,6 +780,12 @@ function createConfig($array, $path = 'config/config.php', $nest = 0) {
 		}
 	}
 	
+	if (!$nest && !isset($array['CONFIG_VERSION'])) {
+		// Inject Current Version
+		$output[] = "\t".'"CONFIG_VERSION" => "'.INSTALLEDVERSION.'"';
+	}
+	
+	// Build output
 	$output = (!$nest?"<?php\nreturn ":'')."array(\n".implode(",\n",$output)."\n".str_repeat("\t",$nest).')'.(!$nest?';':'');
 	
 	if (!$nest && $path) {
@@ -723,14 +794,13 @@ function createConfig($array, $path = 'config/config.php', $nest = 0) {
 		@mkdir($pathDigest['dirname'], 0770, true);
 		
 		if (file_exists($path)) {
-			rename($path, $path.'.bak');
+			rename($path, $pathDigest['dirname'].'/'.$pathDigest['filename'].'.bak.php');
 		}
 		
 		$file = fopen($path, 'w');
 		fwrite($file, $output);
 		fclose($file);
 		if (file_exists($path)) {
-			@unlink($path.'.bak');
 			return true;
 		}
 		
@@ -803,7 +873,7 @@ function defineConfig($array, $anyCase = true, $nest_prefix = false) {
 }
 
 // This function exists only because I am lazy
-function configLazy($path) {
+function configLazy($path = null) {
 	$config = fillDefaultConfig(loadConfig($path));
 	if (is_array($config)) {
 		defineConfig($config);
@@ -868,7 +938,7 @@ function upgradeCheck() {
 		
 		// Refactor
 		$config['database_Location'] = str_replace('//','/',$config['databaseLocation'].'/');
-		$config['user_home'] = $config['databaseLocation'].'users/';
+		$config['user_home'] = $config['database_Location'].'users/';
 		unset($config['databaseLocation']);
 		
 		// Turn Off Emby And Plex Recent
@@ -883,20 +953,52 @@ function upgradeCheck() {
 		$config["headphonesURL"] = $config["headphonesURL"].(!empty($config["headphonesPort"])?':'.$config["headphonesPort"]:'');
 		unset($config["headphonesPort"]);
 		
-		$createConfigSuccess = createConfig($config, 'config/config.php', $nest = 0);
+		// Write config file
+		$config['CONFIG_VERSION'] = '1.32';
+		$createConfigSuccess = createConfig($config);
 		
 		// Create new config
 		if ($createConfigSuccess) {
-			// Make Config Dir (this should never happen as the dir and defaults file should be there);
-			@mkdir('config', 0775, true);
-			
-			// Remove Old ini file
-			unlink('databaseLocation.ini.php');
+			if (file_exists('config/config.php')) {
+				// Remove Old ini file
+				unlink('databaseLocation.ini.php');
+			} else {
+				debug_out('Something is not right here!');
+			}
 		} else {
 			debug_out('Couldn\'t create updated configuration.' ,1);
 		}
 	}
 	
+	// Upgrade to 1.33
+	$config = loadConfig();
+	if (isset($config['database_Location']) && (!isset($config['CONFIG_VERSION']) || $config['CONFIG_VERSION'] < '1.33')) {
+		// Fix User Directory
+		$config['user_home'] = $config['database_Location'].'users/';
+		unset($config['USER_HOME']);
+		
+		// Backend auth merge
+		if (isset($config['authBackendPort']) && !isset(parse_url($config['authBackendHost'])['port'])) {
+			$config['authBackendHost'] .= ':'.$config['authBackendPort'];
+		}
+		unset($config['authBackendPort']);
+		
+		// If auth is being used move it to embyURL as that is now used in auth functions
+		if ((isset($config['authType']) && $config['authType'] == 'true') && (isset($config['authBackendHost']) && $config['authBackendHost'] == 'true') && (isset($config['authBackend']) && in_array($config['authBackend'], array('emby_all','emby_local','emby_connect')))) {
+			$config['embyURL'] = $config['authBackendHost'];
+		}
+		
+		// Upgrade database to latest version
+		updateSQLiteDB($config['database_Location']);
+		
+		// Update Version and Commit
+		$config['CONFIG_VERSION'] = '1.33';
+		$createConfigSuccess = createConfig($config);
+		
+		$effectiveVersion = $config['CONFIG_VERSION'];
+		unset($config);
+	}
+	
 	return true;
 }
 
@@ -946,6 +1048,626 @@ function removeFiles($path) {
 	}
 }
 
+// Lazy select options
+function resolveSelectOptions($array, $selected = '', $multi = false) {
+	$output = array();
+	$selectedArr = ($multi?explode('|', $selected):array());
+	foreach ($array as $key => $value) {
+		if (is_array($value)) {
+			if (isset($value['optgroup'])) {
+				$output[] = '<optgroup label="'.$key.'">';
+				foreach($value['optgroup'] as $k => $v) {
+					$output[] = '<option value="'.$v['value'].'"'.($selected===$v['value']||in_array($v['value'],$selectedArr)?' selected':'').(isset($v['disabled']) && $v['disabled']?' disabled':'').'>'.$k.'</option>';
+				}
+			} else {
+				$output[] = '<option value="'.$value['value'].'"'.($selected===$value['value']||in_array($value['value'],$selectedArr)?' selected':'').(isset($value['disabled']) && $value['disabled']?' disabled':'').'>'.$key.'</option>';
+			}
+		} else {
+			$output[] = '<option value="'.$value.'"'.($selected===$value||in_array($value,$selectedArr)?' selected':'').'>'.$key.'</option>';
+		}
+		
+	}
+	return implode('',$output);
+}
+
+// Check if user is allowed to continue
+function qualifyUser($type, $errOnFail = false) {
+	if (!isset($GLOBALS['USER'])) {
+		require_once("user.php");
+		$GLOBALS['USER'] = new User('registration_callback');
+	}
+	
+	if (is_bool($type)) {
+		if ($type === true) {
+			$authorized = ($GLOBALS['USER']->authenticated == true);
+		} else {
+			$authorized = true;
+		}
+	} elseif (is_string($type) || is_array($type)) {
+		if ($type !== 'false') {
+			if (!is_array($type)) {
+				$type = explode('|',$type);
+			}
+			$authorized = ($GLOBALS['USER']->authenticated && in_array($GLOBALS['USER']->role,$type));
+		} else {
+			$authorized = true;
+		}
+	} else {
+		debug_out('Invalid Syntax!',1);
+	}
+	
+	if (!$authorized && $errOnFail) {
+		if ($GLOBALS['USER']->authenticated) {
+			header('Location: error.php?error=401');
+			echo '<script>window.location.href = \''.dirname($_SERVER['SCRIPT_NAME']).'/error.php?error=401\'</script>';
+		} else {
+			header('Location: error.php?error=999');
+			echo '<script>window.location.href = \''.dirname($_SERVER['SCRIPT_NAME']).'/error.php?error=999\'</script>';
+		}
+
+		debug_out('Not Authorized' ,1);
+	} else {
+		return $authorized;
+	}
+}
+
+// Build an (optionally) tabbed settings page.
+function buildSettings($array) {
+	/*
+	array(
+		'title' => '',
+		'id' => '',
+		'fields' => array( See buildField() ),
+		'tabs' => array(
+			array(
+				'title' => '',
+				'id' => '',
+				'image' => '',
+				'fields' => array( See buildField() ),
+			),
+		),
+	);
+	*/
+	
+	$fieldFunc = function($fieldArr) {
+		$fields = '<div class="row">';
+		foreach($fieldArr as $key => $value) {
+			$isSingle = isset($value['type']);
+			if ($isSingle) { $value = array($value); }
+			$tmpField = '';
+			$sizeLg = max(floor(12/count($value)),2);
+			$sizeMd = max(floor(($isSingle?12:6)/count($value)),3);
+			foreach($value as $k => $v) {
+				$tmpField .= buildField($v, 12, $sizeMd, $sizeLg);
+			}
+			$fields .= ($isSingle?$tmpField:'<div class="row col-sm-12 content-form">'.$tmpField.'</div>');
+		}
+		$fields .= '</div>';
+		return $fields;
+	};
+	
+	$fields = (isset($array['fields'])?$fieldFunc($array['fields']):'');
+	
+	$tabSelectors = array();
+	$tabContent = array();
+	if (isset($array['tabs'])) {
+		foreach($array['tabs'] as $key => $value) {
+			$id = (isset($value['id'])?$value['id']:randString(32));
+			$tabSelectors[$key] = '<li class="apps'.($tabSelectors?'':' active').'"><a href="#tab-'.$id.'" data-toggle="tab" aria-expanded="true"><img style="height:40px; width:40px;" src="'.(isset($value['image'])?$value['image']:'images/organizr.png').'"></a></li>';
+			$tabContent[$key] = '<div class="tab-pane big-box fade'.($tabContent?'':' active in').'" id="tab-'.$id.'">'.$fieldFunc($value['fields']).'</div>';
+		}
+	}
+	
+	$pageID = (isset($array['id'])?$array['id']:str_replace(array(' ','"',"'"),array('_'),strtolower($array['id'])));
+	
+	return '
+	<div class="email-body">
+		<div class="email-header gray-bg">
+			<button type="button" class="btn btn-danger btn-sm waves close-button"><i class="fa fa-close"></i></button>
+			<h1>'.$array['title'].'</h1>
+		</div>
+		<div class="email-inner small-box">
+			<div class="email-inner-section">
+				<div class="small-box fade in" id="'.$pageID.'_frame">
+					<div class="col-lg-12">
+						'.(isset($array['customBeforeForm'])?$array['customBeforeForm']:'').'
+						<form class="content-form" name="'.$pageID.'" id="'.$pageID.'_form" onsubmit="return false;">
+							<button type="submit" class="btn waves btn-labeled btn-success btn btn-sm pull-right text-uppercase waves-effect waves-float">
+							<span class="btn-label"><i class="fa fa-floppy-o"></i></span>Save
+							</button>
+							'.$fields.($tabContent?'
+							<div class="tabbable tabs-with-bg" id="'.$pageID.'_tabs">
+								<ul class="nav nav-tabs apps">
+									'.implode('', $tabSelectors).'
+								</ul>
+								<div class="clearfix"></div>
+								<div class="tab-content">
+									'.implode('', $tabContent).'
+								</div>
+							</div>':'').'
+						</form>
+						'.(isset($array['customAfterForm'])?$array['customAfterForm']:'').'
+					</div>
+				</div>
+			</div>
+		</div>
+	</div>
+	<script>
+		$(document).ready(function() {
+			$(\'#'.$pageID.'_form\').find(\'input, select, textarea\').on(\'change\', function() { $(this).attr(\'data-changed\', \'true\'); });
+			$(\'#'.$pageID.'_form\').find(\'select[multiple]\').on(\'click\', function() { $(this).attr(\'data-changed\', \'true\'); });
+			
+			$(\'#'.$pageID.'_form\').submit(function () {
+				var newVals = {};
+				var hasVals = false;
+				$(\'#'.$pageID.'_form\').find(\'[data-changed=true]\').each(function() {
+					hasVals = true;
+					if (this.type == \'checkbox\') {
+						newVals[this.name] = this.checked;
+					} else {
+						var fieldVal = $(this).val();
+						if (typeof fieldVal == \'object\') {
+							if (typeof fieldVal.join == \'function\') {
+								fieldVal = fieldVal.join(\'|\');
+							} else {
+								fieldVal = JSON.stringify(fieldVal);
+							}
+						}
+						newVals[this.name] = fieldVal;
+					}
+				});
+				if (hasVals) {
+					console.log(newVals);
+					$.post(\'ajax.php?a='.(isset($array['submitAction'])?$array['submitAction']:'update-config').'\', newVals, function(data) {
+						console.log(data);
+						$(\'#'.$pageID.'_form\').find(\'[data-changed=true]\').removeAttr(\'data-changed\');
+						parent.notify(data.html, data.icon, data.type, data.length, data.layout, data.effect);
+					}, \'json\');
+				} else {
+					parent.notify(\'Nothing to update!\', \'bullhorn\', \'success\', 5000, \'bar\', \'slidetop\');
+				}
+				return false;
+			});
+			'.(isset($array['onready'])?$array['onready']:'').'
+		});
+	</script>
+	';
+}
+
+// Build Settings Fields
+function buildField($params, $sizeSm = 12, $sizeMd = 12, $sizeLg = 12) {
+	/*
+	array(
+		'type' => '',
+		'placeholder' => '',
+		'label' => '',
+		'labelTranslate' => '',
+		'assist' => '',
+		'name' => '',
+		'pattern' => '',
+		'options' => array( // For SELECT only
+			'Display' => 'value',
+		),
+	)
+	*/
+	
+	// Tags
+	$tags = array();
+	foreach(array('placeholder','style','disabled','readonly','pattern','min','max','required','onkeypress','onchange','onfocus','onleave','href') as $value) {
+		$tags[] = (isset($params[$value])?$value.'="'.$params[$value].'"':'');
+	}
+	
+	$format = (isset($params['format']) && in_array($params['format'],array(false,'colour','color'))?$params['format']:false);
+	$name = (isset($params['name'])?$params['name']:(isset($params['id'])?$params['id']:''));
+	$id = (isset($params['id'])?$params['id']:(isset($params['name'])?$params['name'].'_id':randString(32)));
+	$val = (isset($params['value'])?$params['value']:'');
+	$class = (isset($params['class'])?' '.$params['class']:'');
+	$wrapClass = (isset($params['wrapClass'])?$params['wrapClass']:'form-content');
+	$assist = (isset($params['assist'])?' - i.e. '.$params['assist']:'');
+	$label = (isset($params['labelTranslate'])?translate($params['labelTranslate']):(isset($params['label'])?$params['label']:''));
+	$labelOut = '<p class="help-text">'.$label.$assist.'</p>';
+	
+	// Field Design
+	switch ($params['type']) {
+		case 'text':
+		case 'number':
+		case 'password':
+			$field = '<input id="'.$id.'" name="'.$name.'" type="'.$params['type'].'" class="form-control material input-sm'.$class.'" '.implode(' ',$tags).' autocorrect="off" autocapitalize="off" value="'.$val.'">';
+			break;
+		case 'select':
+		case 'dropdown':
+			$field = '<select id="'.$id.'" name="'.$name.'" class="form-control material input-sm" '.implode(' ',$tags).'>'.resolveSelectOptions($params['options'], $val).'</select>';
+			break;
+		case 'select-multi':
+		case 'dropdown-multi':
+			$field = '<select id="'.$id.'" name="'.$name.'" class="form-control input-sm" '.implode(' ',$tags).' multiple="multiple">'.resolveSelectOptions($params['options'], $val, true).'</select>';
+			break;
+		case 'check':
+		case 'checkbox':
+		case 'toggle':
+			$checked = ((is_bool($val) && $val) || trim($val) === 'true'?' checked':'');
+			$labelOut = '<label for="'.$id.'"></label>'.$label;
+			$field = '<input id="'.$id.'" name="'.$name.'" type="checkbox" class="switcher switcher-success'.$class.'" '.implode(' ',$tags).' value="'.$val.'"'.$checked.'>';
+			break;
+		case 'date':
+			$field = 'Unsupported, planned.';
+			break;
+		case 'hidden':
+			$labelOut = '';
+			$field = '<input id="'.$id.'" name="'.$name.'" type="hidden" class="'.$class.'" '.implode(' ',$tags).' value="'.$val.'">';
+			break;
+		case 'header':
+			$labelOut = '';
+			$headType = (isset($params['value'])?$params['value']:3);
+			$field = '<h'.$headType.' class="'.$class.'" '.implode(' ',$tags).'>'.$label.'</h'.$headType.'>';
+			break;
+		case 'button':
+			$labelOut = '';
+			$icon = (isset($params['icon'])?$params['icon']:'flask');
+			$bType = (isset($params['buttonType'])?$params['buttonType']:'success');
+			$bDropdown = (isset($params['buttonDrop'])?$params['buttonDrop']:'');
+			$field = ($bDropdown?'<div class="btn-group">':'').'<button id="'.$id.'" type="button" class="btn waves btn-labeled btn-'.$bType.' btn-sm text-uppercase waves-effect waves-float'.$class.''.($bDropdown?' dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"':'"').' '.implode(' ',$tags).'><span class="btn-label"><i class="fa fa-'.$icon.'"></i></span>'.$label.'</button>'.($bDropdown?$bDropdown.'</div>':'');
+			break;
+		case 'space':
+			$labelOut = '';
+			$field = str_repeat('<br>', (isset($params['value'])?$params['value']:1));
+			break;
+		default:
+			$field = 'Unsupported field type';
+			break;
+	}
+	
+	// Field Formats
+	switch ($format) {
+		case 'colour': // Fuckin Eh, Canada!
+		case 'color':
+			$labelBef = '<center>'.$label.'</center>';
+			$wrapClass = 'gray-bg colour-field';
+			$labelAft = '';
+			$field = str_replace(' material input-sm','',$field);
+			break;
+		default:
+			$labelBef = '';
+			$labelAft = $labelOut;
+	}
+	
+	return '<div class="'.$wrapClass.' col-sm-'.$sizeSm.' col-md-'.$sizeMd.' col-lg-'.$sizeLg.'">'.$labelBef.$field.$labelAft.'</div>';
+}
+
+// Timezone array
+function timezoneOptions() {
+	$output = array();
+	$timezones = array();
+    $regions = array(
+        'Africa' => DateTimeZone::AFRICA,
+        'America' => DateTimeZone::AMERICA,
+        'Antarctica' => DateTimeZone::ANTARCTICA,
+        'Arctic' => DateTimeZone::ARCTIC,
+        'Asia' => DateTimeZone::ASIA,
+        'Atlantic' => DateTimeZone::ATLANTIC,
+        'Australia' => DateTimeZone::AUSTRALIA,
+        'Europe' => DateTimeZone::EUROPE,
+        'Indian' => DateTimeZone::INDIAN,
+        'Pacific' => DateTimeZone::PACIFIC
+    );
+    
+    foreach ($regions as $name => $mask) {
+        $zones = DateTimeZone::listIdentifiers($mask);
+        foreach($zones as $timezone) {
+            $time = new DateTime(NULL, new DateTimeZone($timezone));
+            $ampm = $time->format('H') > 12 ? ' ('. $time->format('g:i a'). ')' : '';
+			
+			$output[$name]['optgroup'][substr($timezone, strlen($name) + 1) . ' - ' . $time->format('H:i') . $ampm]['value'] = $timezone;
+        }
+    }   
+	
+	return $output;
+}
+
+// Build Database
+function createSQLiteDB($path = false) {
+	if ($path === false) {
+		if (defined('DATABASE_LOCATION')) {
+			$path = DATABASE_LOCATION;
+		} else {
+			debug_out('No Path Specified!');
+		}
+	}
+	
+	if (!is_file($path.'users.db')) {
+		if (!isset($GLOBALS['file_db'])) {
+			$GLOBALS['file_db'] = new PDO('sqlite:'.$path.'users.db');
+			$GLOBALS['file_db']->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+		}
+		
+		// Create Users
+		$users = $GLOBALS['file_db']->query('CREATE TABLE `users` (
+			`id`	INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
+			`username`	TEXT UNIQUE,
+			`password`	TEXT,
+			`email`	TEXT,
+			`token`	TEXT,
+			`role`	TEXT,
+			`active`	TEXT,
+			`last`	TEXT,
+			`auth_service`	TEXT DEFAULT \'internal\'
+		);');
+		
+		// Create Tabs
+		$tabs = $GLOBALS['file_db']->query('CREATE TABLE `tabs` (
+			`id`	INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
+			`users_id`	INTEGER,
+			`name`	TEXT,
+			`url`	TEXT,
+			`defaultz`	TEXT,
+			`active`	TEXT,
+			`user`	TEXT,
+			`guest`	TEXT,
+			`icon`	TEXT,
+			`iconurl`	TEXT,
+			`window`	TEXT
+		);');
+		
+		// Create Options
+		$options = $GLOBALS['file_db']->query('CREATE TABLE `options` (
+			`id`	INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
+			`users_id`	INTEGER UNIQUE,
+			`title`	TEXT UNIQUE,
+			`topbar`	TEXT,
+			`bottombar`	TEXT,
+			`sidebar`	TEXT,
+			`hoverbg`	TEXT,
+			`topbartext`	TEXT,
+			`activetabBG`	TEXT,
+			`activetabicon`	TEXT,
+			`activetabtext`	TEXT,
+			`inactiveicon`	TEXT,
+			`inactivetext`	TEXT,
+			`loading`	TEXT,
+			`hovertext`	TEXT
+		);');
+		
+		return $users && $tabs && $options;
+	} else {
+		return false;
+	}
+}
+
+// Upgrade Database
+function updateSQLiteDB($db_path = false) {
+	if (!$db_path) {
+		if (defined('DATABASE_LOCATION')) {
+			$db_path = DATABASE_LOCATION;
+		} else {
+			debug_out('No Path Specified',1);
+		}
+	}
+	if (!isset($GLOBALS['file_db'])) {
+		$GLOBALS['file_db'] = new PDO('sqlite:'.$db_path.'users.db');
+		$GLOBALS['file_db']->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+	}
+	
+	// Cache current DB
+	$cache = array();
+	foreach($GLOBALS['file_db']->query('SELECT name FROM sqlite_master WHERE type="table";') as $table) {
+		foreach($GLOBALS['file_db']->query('SELECT * FROM '.$table['name'].';') as $key => $row) {
+			foreach($row as $k => $v) {
+				if (is_string($k)) {
+					$cache[$table['name']][$key][$k] = $v;
+				}
+			}
+		}
+	}
+	
+	// Remove Current Database
+	$GLOBALS['file_db'] = null;
+	$pathDigest = pathinfo($db_path.'users.db');
+	if (file_exists($db_path.'users.db')) {
+		rename($db_path.'users.db', $pathDigest['dirname'].'/'.$pathDigest['filename'].'.bak.db');
+	}
+	
+	// Create New Database
+	$success = createSQLiteDB($db_path);
+	
+	// Restore Items
+	if ($success) {
+		foreach($cache as $table => $tableData) {
+			if ($tableData) {
+				$queryBase = 'INSERT INTO '.$table.' (`'.implode('`,`',array_keys(current($tableData))).'`) values ';
+				$insertValues = array();
+				reset($tableData);
+				foreach($tableData as $key => $value) {
+					$insertValues[] = '('.implode(',',array_map(function($d) { 
+						return (isset($d)?"'".addslashes($d)."'":'null');
+					}, $value)).')';
+				}
+				$GLOBALS['file_db']->query($queryBase.implode(',',$insertValues).';');
+			}
+		}
+		return true;
+	} else {
+		return false;
+	}
+}
+
+// Commit colours to database
+function updateDBOptions($values) {
+	if (!isset($GLOBALS['file_db'])) {
+		$GLOBALS['file_db'] = new PDO('sqlite:'.DATABASE_LOCATION.'users.db');
+		$GLOBALS['file_db']->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+	}
+	
+	// Commit new values to database
+	$GLOBALS['file_db']->exec('UPDATE options SET '.implode(',',array_map(function($d, $k) { 
+		return '`'.$k.'` = '.(isset($d)?"'".addslashes($d)."'":'null');
+	}, $values, array_keys($values))).';'); // WHERE user_id = '';
+	
+	return true;
+}
+
+// Send AJAX notification
+function sendNotification($success, $message = false) {
+	header('Content-Type: application/json');
+	$notifyExplode = explode("-", NOTIFYEFFECT);
+	if ($success) {
+		$msg = array(
+			'html' => '<strong>'.translate("SETTINGS_SAVED").'</strong>'.($message?'<br>'.$message:''),
+			'icon' => 'floppy-o',
+			'type' => 'success',
+			'length' => '5000',
+			'layout' => $notifyExplode[0],
+			'effect' => $notifyExplode[1],
+		);
+	} else {
+		$msg = array(
+			'html' => '<strong>'.translate("SETTINGS_NOT_SAVED").'</strong>'.($message?'<br>'.$message:''),
+			'icon' => 'floppy-o',
+			'type' => 'failed',
+			'length' => '5000',
+			'layout' => $notifyExplode[0],
+			'effect' => $notifyExplode[1],
+		);
+	}
+	echo json_encode($msg);
+	die();
+}
+
+// Load colours from the database
+function loadAppearance() {
+	if (!isset($GLOBALS['file_db'])) {
+		$GLOBALS['file_db'] = new PDO('sqlite:'.DATABASE_LOCATION.'users.db');
+		$GLOBALS['file_db']->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+	}
+	
+	// Defaults
+	$defaults = array(
+		'title' => 'Organizr',
+		'topbartext' => '#66D9EF',
+		'topbar' => '#333333',
+		'bottombar' => '#333333',
+		'sidebar' => '#393939',
+		'hoverbg' => '#AD80FD',
+		'activetabBG' => '#F92671',
+		'activetabicon' => '#FFFFFF',
+		'activetabtext' => '#FFFFFF',
+		'inactiveicon' => '#66D9EF',
+		'inactivetext' => '#66D9EF',
+		'loading' => '#66D9EF',
+		'hovertext' => '#000000',
+	);
+	
+	// Database Lookup
+	$options = $GLOBALS['file_db']->query('SELECT * FROM options');
+	
+	// Replace defaults with filled options
+	foreach($options as $row) {
+		foreach($defaults as $key => $value) {
+			if (isset($row[$key]) && $row[$key]) {
+				$defaults[$key] = $row[$key];
+			}
+		}
+	}
+	
+	// Return the Results
+	return $defaults;
+}
+
+// Delete Database
+function deleteDatabase() {
+    unset($_COOKIE['Organizr']);
+    setcookie('Organizr', '', time() - 3600, '/');
+    unset($_COOKIE['OrganizrU']);
+    setcookie('OrganizrU', '', time() - 3600, '/');
+	
+    $GLOBALS['file_db'] = null;
+
+    unlink(DATABASE_LOCATION.'users.db'); 
+	
+    foreach(glob(substr_replace($userdirpath, "", -1).'/*') as $file) {
+        if(is_dir($file)) {
+            rmdir($file); 
+        } elseif (!is_dir($file)) {
+            unlink($file);
+        }
+	}
+
+    rmdir($userdirpath);
+	
+	return true;
+}
+
+// Upgrade the installation
+function upgradeInstall() {
+    function downloadFile($url, $path){
+        $folderPath = "upgrade/";
+        if(!mkdir($folderPath)) : echo "can't make dir"; endif;
+        $newfname = $folderPath . $path;
+        $file = fopen ($url, 'rb');
+        if ($file) {
+            $newf = fopen ($newfname, 'wb');
+            if ($newf) {
+                while(!feof($file)) {
+                    fwrite($newf, fread($file, 1024 * 8), 1024 * 8);
+                }
+            }
+        }
+
+        if ($file) {
+            fclose($file);
+        }
+
+        if ($newf) {
+            fclose($newf);
+        }
+    }
+
+    function unzipFile($zipFile){
+        $zip = new ZipArchive;
+        $extractPath = "upgrade/";
+        if($zip->open($extractPath . $zipFile) != "true"){
+            echo "Error :- Unable to open the Zip File";
+        }
+
+        /* Extract Zip File */
+        $zip->extractTo($extractPath);
+        $zip->close();
+    }
+
+    // Function to remove folders and files 
+    function rrmdir($dir) {
+        if (is_dir($dir)) {
+            $files = scandir($dir);
+            foreach ($files as $file)
+                if ($file != "." && $file != "..") rrmdir("$dir/$file");
+            rmdir($dir);
+        }
+        else if (file_exists($dir)) unlink($dir);
+    }
+
+    // Function to Copy folders and files       
+    function rcopy($src, $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" );
+        } else if (file_exists ( $src ))
+            copy ( $src, $dst );
+    }
+
+    $url = "https://github.com/causefx/Organizr/archive/master.zip";
+    $file = "upgrade.zip";
+    $source = __DIR__ . "/upgrade/Organizr-master/";
+    $cleanup = __DIR__ . "/upgrade/";
+    $destination = __DIR__ . "/";
+    downloadFile($url, $file);
+    unzipFile($file);
+    rcopy($source, $destination);
+    rrmdir($cleanup);
+	
+	return true;
+}
 
 // ==============
 

+ 31 - 112
homepage.php

@@ -14,6 +14,9 @@ $radarr = new Sonarr(RADARRURL, RADARRKEY);
 $sickrage = new SickRage(SICKRAGEURL, SICKRAGEKEY);
 $USER = new User("registration_callback");
 
+// Check if connection to homepage is allowed
+qualifyUser(HOMEPAGEAUTHNEEDED, true);
+
 $dbfile = DATABASE_LOCATION.'users.db';
 
 $file_db = new PDO("sqlite:" . $dbfile);
@@ -26,9 +29,7 @@ $hasOptions = "No";
 foreach($dbOptions as $row) :
 
     if (in_array("options", $row)) :
-    
         $hasOptions = "Yes";
-    
     endif;
 
 endforeach;
@@ -54,7 +55,6 @@ endif;
 if($hasOptions == "Yes") :
 
     $resulto = $file_db->query('SELECT * FROM options'); 
-                                    
     foreach($resulto as $row) : 
 
         $title = isset($row['title']) ? $row['title'] : "Organizr";
@@ -85,7 +85,6 @@ $endDate = date('Y-m-d',strtotime("+".CALENDARENDDAY." days"));
 <html lang="en" class="no-js">
 
     <head>
-        
         <meta charset="UTF-8">
         <meta name="viewport" content="width=device-width, initial-scale=1.0">
         <meta http-equiv="X-UA-Compatible" content="IE=edge">
@@ -96,7 +95,6 @@ $endDate = date('Y-m-d',strtotime("+".CALENDARENDDAY." days"));
         <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.min.css">
         <link rel="stylesheet" href="bower_components/font-awesome/css/font-awesome.min.css">
         <link rel="stylesheet" href="bower_components/malihu-custom-scrollbar-plugin/jquery.mCustomScrollbar.css"> 
-        
         <script src="js/menu/modernizr.custom.js"></script>
 
         <link rel="stylesheet" href="bower_components/animate.css/animate.min.css">
@@ -109,7 +107,6 @@ $endDate = date('Y-m-d',strtotime("+".CALENDARENDDAY." days"));
         <script src="bower_components/html5shiv/dist/html5shiv.min.js"></script>
         <script src="bower_components/respondJs/dest/respond.min.js"></script>
         <![endif]-->
-        
         <style>
             sort {
                 display: none;
@@ -182,24 +179,20 @@ echo fread($file_handle, filesize($template_file));
 fclose($file_handle);
 echo "\n";
 endif; ?>        
-        
         </style>
-        
     </head>
 
     <body class="scroller-body" style="padding: 0px;">
 
         <div class="main-wrapper" style="position: initial;">
-            
             <div id="content" class="container-fluid">
 <!-- <button id="numBnt">Numerical</button> -->
                 <br/>
-                <?php if(($USER->authenticated && $USER->role == "admin") && (NZBGETURL != "" || SABNZBDURL != "" )) : ?>
+                <?php if((NZBGETURL != "" && qualifyUser(NZBGETHOMEAUTH)) || (SABNZBDURL != "" && qualifyUser(SABNZBDHOMEAUTH))) { ?>
                 <div id="downloadClientRow" class="row">
                     <sort>2</sort>
 
                     <div class="col-xs-12 col-md-12">
-                        
                         <div class="content-box">
 
                             <div class="tabbable panel with-nav-tabs panel-default">
@@ -215,11 +208,8 @@ endif; ?>
                                         </a>
 
                                         <a class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
-                                          
                                             <i class="fa fa-chevron-down"></i>
-                                        
                                         </a>
-                                        
                                         <ul id="downloaderSeconds" class="dropdown-menu" style="top: 32px !important">
 
                                             <li data-value="5000"><a>Refresh every 5 seconds</a></li>
@@ -312,100 +302,69 @@ endif; ?>
                                 </div>
 
                             </div>
-                            
                         </div>
 
                     </div>
 
                 </div>
-                <?php endif; ?>
-
+                <?php } ?>
+				<?php if (qualifyUser(PLEXHOMEAUTH)) { ?>
                 <div id="plexRow" class="row">
-                    
                     <sort>3</sort>
 
                     <?php
-                    $plexSize = 0;
-                    if(PLEXRECENTMOVIE == "true"){ $plexSize++; }
-                    if(PLEXRECENTTV == "true"){ $plexSize++; }
-                    if(PLEXRECENTMUSIC == "true"){ $plexSize++; }
-                    if(PLEXPLAYINGNOW == "true"){ $plexSize++; }
-                    if($plexSize >= 4){ $plexSize = 3; }elseif($plexSize == 3){ $plexSize = 4; }elseif($plexSize == 2){ $plexSize = 6; }elseif($plexSize == 1){ $plexSize = 12; }
-                    
-                    if(PLEXRECENTMOVIE == "true"){ echo getPlexRecent("movie", $plexSize); }
-                    if(PLEXRECENTTV == "true"){ echo getPlexRecent("season", $plexSize); }
-                    if(PLEXRECENTMUSIC == "true"){ echo getPlexRecent("album", $plexSize); }
-                    if(PLEXPLAYINGNOW == "true"){ echo getPlexStreams($plexSize); }
+                    $plexSize = (PLEXRECENTMOVIE == "true") + (PLEXRECENTTV == "true") + (PLEXRECENTMUSIC == "true") + (PLEXPLAYINGNOW == "true");
+                    if(PLEXRECENTMOVIE == "true"){ echo getPlexRecent("movie", 12/$plexSize); }
+                    if(PLEXRECENTTV == "true"){ echo getPlexRecent("season", 12/$plexSize); }
+                    if(PLEXRECENTMUSIC == "true"){ echo getPlexRecent("album", 12/$plexSize); }
+                    if(PLEXPLAYINGNOW == "true"){ echo getPlexStreams(12/$plexSize); }
                     ?>
 
                 </div>
-                
+				<?php } ?>
+				<?php if (qualifyUser(EMBYHOMEAUTH)) { ?>
                 <div id="embyRow" class="row">
-                    
                     <sort>3</sort>
 
                     <?php
-                    $embySize = 0;
-                    if(EMBYRECENTMOVIE == "true"){ $embySize++; }
-                    if(EMBYRECENTTV == "true"){ $embySize++; }
-                    if(EMBYRECENTMUSIC == "true"){ $embySize++; }
-                    if(EMBYPLAYINGNOW == "true"){ $embySize++; }
-                    if($embySize >= 4){ $embySize = 3; }elseif($embySize == 3){ $embySize = 4; }elseif($embySize == 2){ $embySize = 6; }elseif($embySize == 1){ $embySize = 12; }
-                    
-                    if(EMBYRECENTMOVIE == "true"){ echo getEmbyRecent("movie", $embySize); }
-                    if(EMBYRECENTTV == "true"){ echo getEmbyRecent("season", $embySize); }
-                    if(EMBYRECENTMUSIC == "true"){ echo getEmbyRecent("album", $embySize); }
-                    if(EMBYPLAYINGNOW == "true"){ echo getEmbyStreams($embySize); }
+                    $embySize = (EMBYRECENTMOVIE == "true") + (EMBYRECENTTV == "true") + (EMBYRECENTMUSIC == "true") + (EMBYPLAYINGNOW == "true");
+                    if(EMBYRECENTMOVIE == "true"){ echo getEmbyRecent("movie", 12/$embySize); }
+                    if(EMBYRECENTTV == "true"){ echo getEmbyRecent("season", 12/$embySize); }
+                    if(EMBYRECENTMUSIC == "true"){ echo getEmbyRecent("album", 12/$embySize); }
+                    if(EMBYPLAYINGNOW == "true"){ echo getEmbyStreams(12/$embySize); }
                     ?>
 
                 </div>
-		    
-                <?php if(SONARRURL != "" || RADARRURL != "" || HEADPHONESURL != "" || SICKRAGEURL != "") : ?>
+				<?php } ?>
+                <?php if ((SONARRURL != "" && qualifyUser(SONARRHOMEAUTH)) || (RADARRURL != "" && qualifyUser(RADARRHOMEAUTH)) || (HEADPHONESURL != "" && qualifyUser(HEADPHONESHOMEAUTH)) || (SICKRAGEURL != "" && qualifyUser(SICKRAGEHOMEAUTH))) { ?>
                 <div id="calendarLegendRow" class="row" style="padding: 0 0 10px 0;">
-                    
                     <sort>1</sort>
-                    
                     <div class="col-lg-12 content-form form-inline">
-                        
                         <div class="form-group">
-                        
                             <select class="form-control" id="imagetype_selector" style="width: auto !important; display: inline-block">
-
                                 <option value="all">View All</option>
                                 <?php if(RADARRURL != ""){ echo '<option value="film">Movies</option>'; }?>
                                 <?php if(SONARRURL != "" || SICKRAGEURL != ""){ echo '<option value="tv">TV Shows</option>'; }?>
                                 <?php if(HEADPHONESURL != ""){ echo '<option value="music">Music</option>'; }?>
-
                             </select>
 
                             <span class="label label-primary well-sm">Available</span>
                             <span class="label label-danger well-sm">Unavailable</span>
                             <span class="label indigo-bg well-sm">Unreleased</span>
                             <span class="label light-blue-bg well-sm">Premier</span>
-                            
                         </div>
-                    
                     </div>
-                    
                 </div>
-                
                 <div id="calendarRow" class="row">
-                    
                     <sort>1</sort>
-        
                     <div class="col-lg-12">
-                    
                         <div id="calendar" class="fc-calendar box-shadow fc fc-ltr fc-unthemed"></div>
-                                        
                     </div>
-                                        
                 </div>
-                <?php endif; ?>
+                <?php } ?>
 
             </div>    
-                
         </div>
-        
         <!--Scripts-->
         <script src="bower_components/jquery/dist/jquery.min.js"></script>
         <script src="bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
@@ -415,16 +374,12 @@ endif; ?>
         <script src="bower_components/malihu-custom-scrollbar-plugin/jquery.mCustomScrollbar.js"></script>
         <script src="bower_components/jquery.nicescroll/jquery.nicescroll.min.js"></script>
         <script src="bower_components/cta/dist/cta.min.js"></script>
-        
         <script src="bower_components/fullcalendar/dist/fullcalendar.js"></script>
 
         <script src="js/jqueri_ui_custom/jquery-ui.min.js"></script>
 	    <script src="js/jquery.mousewheel.min.js" type="text/javascript"></script>
-        
         <script>
-        
         $( document ).ready(function() {
-            
              $('.repeat-btn').click(function(){
                 var refreshBox = $(this).closest('div.content-box');
                 $("<div class='refresh-preloader'><div class='la-timer la-dark'><div></div></div></div>").appendTo(refreshBox).fadeIn(300);
@@ -443,60 +398,44 @@ endif; ?>
                 scrollspeed: 30,
                 mousescrollstep: 60
             });
-            
             $(".table-responsive").niceScroll({
                 railpadding: {top:0,right:0,left:0,bottom:0},
                 scrollspeed: 30,
                 mousescrollstep: 60
             });
-            
             /*$(".carousel-caption").niceScroll({
                 railpadding: {top:0,right:0,left:0,bottom:0},
                 scrollspeed: 30,
                 mousescrollstep: 60
             });*/
-            
             // check if browser support HTML5 local storage
             function localStorageSupport() {
                 return (('localStorage' in window) && window['localStorage'] !== null)
             }
-            
-            <?php if(($USER->authenticated && $USER->role == "admin") && (NZBGETURL != "" || SABNZBDURL != "")){ ?>
-            
+            <?php if((NZBGETURL != "" && qualifyUser(NZBGETHOMEAUTH)) || (SABNZBDURL != "" && qualifyUser(SABNZBDHOMEAUTH))){ ?>
             var downloaderSeconds = localStorage.getItem("downloaderSeconds");
             var myInterval = undefined;
             $("ul").find("[data-value='" + downloaderSeconds + "']").addClass("active");
-            
             if(  downloaderSeconds === null ) {
                 localStorage.setItem("downloaderSeconds",'60000');
                 var downloaderSeconds = "60000";
             }
-            
             $('#downloaderSeconds li').click(function() {
-                
                 $('#downloaderSeconds li').removeClass("active");
                 $(this).addClass("active");
 
                 var newDownloaderSeconds = $(this).attr('data-value');
                 console.log('New Time is ' + newDownloaderSeconds + ' Old Time was ' + downloaderSeconds);
-                
                 if (localStorageSupport) {
                     localStorage.setItem("downloaderSeconds",newDownloaderSeconds);
                 }
-                
                 if(typeof myInterval != 'undefined'){ clearInterval(myInterval); }
                 refreshDownloader(newDownloaderSeconds);
-                
             });
-            
             <?php } ?>
-            
-            
-            <?php if(($USER->authenticated && $USER->role == "admin") && NZBGETURL != ""){ ?>
-            
+            <?php if((NZBGETURL != "" && qualifyUser(NZBGETHOMEAUTH))){ ?>
             $("#downloaderHistory").load("downloader.php?downloader=nzbget&list=history");
             $("#downloaderQueue").load("downloader.php?downloader=nzbget&list=listgroups");
-            
             refreshDownloader = function(secs){
                 myInterval = setInterval(function(){
                     $("#downloaderHistory").load("downloader.php?downloader=nzbget&list=history");
@@ -513,12 +452,9 @@ endif; ?>
             });
 
             <?php } ?>
-            
-            <?php if(($USER->authenticated && $USER->role == "admin") && SABNZBDURL != ""){ ?>
-            
+            <?php if((SABNZBDURL != "" && qualifyUser(SABNZBDHOMEAUTH))){ ?>
             $("#downloaderHistory").load("downloader.php?downloader=sabnzbd&list=history");
             $("#downloaderQueue").load("downloader.php?downloader=sabnzbd&list=queue");
-            
             refreshDownloader = function(secs){
                 myInterval = setInterval(function(){
                     $("#downloaderHistory").load("downloader.php?downloader=sabnzbd&list=history");
@@ -535,13 +471,10 @@ endif; ?>
             });
 
             <?php } ?>
-                        
         });
-             
         </script>
-        <?php if(SONARRURL != "" || RADARRURL != "" || HEADPHONESURL != "" || SICKRAGEURL != "") : ?>
+        <?php if ((SONARRURL != "" && qualifyUser(SONARRHOMEAUTH)) || (RADARRURL != "" && qualifyUser(RADARRHOMEAUTH)) || (HEADPHONESURL != "" && qualifyUser(HEADPHONESHOMEAUTH)) || (SICKRAGEURL != "" && qualifyUser(SICKRAGEHOMEAUTH))) { ?>
         <script>
-            
             $(function () {
 
                 var date = new Date();
@@ -550,52 +483,39 @@ endif; ?>
                 var y = date.getFullYear();
 
                 $('#calendar').fullCalendar({
-                    
                     eventLimit: false, 
                     firstDay: <?php echo CALENDARSTART;?>,
-                  
                     height: "auto",
                     defaultView: '<?php echo CALENDARVIEW;?>',
-                
                     header: {
-                  
                         left: 'prev,next,',
                         center: 'title',
                         right: 'today, month, basicDay,basicWeek,'
-                
                     },
-                
                     views: {
-                    
                         basicDay: { buttonText: '<?php echo $language->translate("DAY");?>', eventLimit: false },
                         basicWeek: { buttonText: '<?php echo $language->translate("WEEK");?>', eventLimit: false },
                         month: { buttonText: '<?php echo $language->translate("MONTH");?>', eventLimit: false },
                         today: { buttonText: '<?php echo $language->translate("TODAY");?>' },
-                
                     },
-                
                     events: [
-<?php if(SICKRAGEURL != ""){ echo getSickrageCalendarWanted($sickrage->future()); echo getSickrageCalendarHistory($sickrage->history("100","downloaded")); } ?>
-<?php if(SONARRURL != ""){ echo getSonarrCalendar($sonarr->getCalendar($startDate, $endDate)); } ?>
-<?php if(RADARRURL != ""){ echo getRadarrCalendar($radarr->getCalendar($startDate, $endDate)); } ?>                 
-<?php if(HEADPHONESURL != ""){ echo getHeadphonesCalendar(HEADPHONESURL, HEADPHONESKEY, "getHistory"); echo getHeadphonesCalendar(HEADPHONESURL, HEADPHONESKEY, "getWanted"); } ?>                                
+<?php if (SICKRAGEURL != "" && qualifyUser(SICKRAGEHOMEAUTH)){ echo getSickrageCalendarWanted($sickrage->future()); echo getSickrageCalendarHistory($sickrage->history("100","downloaded")); } ?>
+<?php if (SONARRURL != "" && qualifyUser(SONARRHOMEAUTH)){ echo getSonarrCalendar($sonarr->getCalendar($startDate, $endDate)); } ?>
+<?php if (RADARRURL != "" && qualifyUser(RADARRHOMEAUTH)){ echo getRadarrCalendar($radarr->getCalendar($startDate, $endDate)); } ?>                 
+<?php if (HEADPHONESURL != "" && qualifyUser(HEADPHONESHOMEAUTH)){ echo getHeadphonesCalendar(HEADPHONESURL, HEADPHONESKEY, "getHistory"); echo getHeadphonesCalendar(HEADPHONESURL, HEADPHONESKEY, "getWanted"); } ?>                                
                     ],
-                                            
                     eventRender: function eventRender( event, element, view ) {
                         return ['all', event.imagetype].indexOf($('#imagetype_selector').val()) >= 0
                     },
 
                     editable: false,
                     droppable: false,
-
+					timeFormat: '<?php echo CALTIMEFORMAT; ?>',
                 });
-            
             });
-            
             $('#imagetype_selector').on('change',function(){
                 $('#calendar').fullCalendar('rerenderEvents');
             })
-            
             var $divs = $("div.row");
 
             $('#numBnt').on('click', function () {
@@ -604,9 +524,8 @@ endif; ?>
                 });
                 $("#content").html(numericallyOrderedDivs);
             });
-        
         </script>
-        <?php endif; ?>
+        <?php } ?>
 
     </body>
 

BIN
images/asusrouter.png


BIN
images/freenas-logo.png


BIN
images/gitlab.png


BIN
images/haproxy.png


BIN
images/hydra2.png


BIN
images/mcmap.png


BIN
images/minecraft.png


BIN
images/motherboard.png


BIN
images/organizr-load-w-thick.gif


BIN
images/power.png


BIN
images/proxmox.png


BIN
images/sugarcrm.png


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 12 - 349
index.php


+ 1 - 1
js/user.js

@@ -137,7 +137,7 @@ User = {
 		if(valid) {
 			form["password"].value = form["password1"].value;
 			form["sha1"].value = Sha1.hash(form["password1"].value);
-			form["password1"].value = this.randomString(16);
+			// form["password1"].value = this.randomString(16);
 			form.submit(); }
 	},
 

+ 5 - 1
lang/en.ini

@@ -238,4 +238,8 @@ ONLY = "Only"
 NO_CREATE = "Do Not Create Accounts"
 YES_CREATE = "Create Accounts As Needed"
 RECENT_CONTENT = "Recently Added Content"
-SETTINGS_SAVED = "Settings have been Saved"
+SETTINGS_SAVED = "Settings have been Saved"
+SETTINGS_NOT_SAVED = "Settings could not be saved"
+CALTIMEFORMAT = "Select time format"
+SHOW_HOMEPAGE = "Minimum authentication level to access homepage"
+SHOW_ON_HOMEPAGE = "Minimum authentication level for homepage"

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 1061 - 1520
settings.php


+ 8 - 16
user.php

@@ -10,9 +10,6 @@
 	
 	// Include functions if not already included
 	require_once('functions.php');
-	
-	// Define Version
-	 define('INSTALLEDVERSION', '1.323');
 	 
     // Autoload frameworks
 	require_once(__DIR__ . '/vendor/autoload.php');
@@ -168,10 +165,8 @@
 		function rebuild_database($dbfile)
 		{
 			$this->info("creating/rebuilding database as ".$dbfile);
-			$this->database->beginTransaction();
-			$create = "CREATE TABLE users (username TEXT UNIQUE, password TEXT, email TEXT UNIQUE, token TEXT, role TEXT, active TEXT, last TEXT);";
-			$this->database->exec($create);
-			$this->database->commit();
+			createSQLiteDB();
+			$this->database = new PDO("sqlite:" . $dbfile);
 		}
 		// process a page request
 		function process(&$registration_callback=false)
@@ -549,7 +544,7 @@ EOT;
 
 			// This user can be registered
 			$insert = "INSERT INTO users (username, email, password, token, role, active, last) ";
-			$insert .= "VALUES ('$username', '$email', '$dbpassword', '', '$newRole', 'false', '') ";
+			$insert .= "VALUES ('".strtolower($username)."', '$email', '$dbpassword', '', '$newRole', 'false', '') ";
 			$this->database->exec($insert);
 			$query = "SELECT * FROM users WHERE username = '$username'";
 			foreach($this->database->query($query) as $data) {
@@ -605,9 +600,8 @@ EOT;
 				default: // Internal
 					if (!$authSuccess) {
 						// perform the internal authentication step
-						$query = "SELECT password FROM users WHERE username = '$username'";
+						$query = "SELECT password FROM users WHERE LOWER(username) = '".strtolower($username)."'";
 						foreach($this->database->query($query) as $data) {
-							
 							if (password_verify($password, $data["password"])) { // Better
 								$authSuccess = true;
 							} else {
@@ -623,13 +617,11 @@ EOT;
 			
 			if ($authSuccess) {
 				// Make sure user exists in database
-				$query = "SELECT username FROM users WHERE username = '$username'";
+				$query = "SELECT username FROM users WHERE LOWER(username) = '".strtolower($username)."'";
 				$userExists = false;
 				foreach($this->database->query($query) as $data) {
-					if ($data['username'] == $username) {
-						$userExists = true;
-						break;
-					}
+					$userExists = true;
+					break;
 				}
 				
 				if ($userExists) {
@@ -650,7 +642,7 @@ EOT;
 				} else if (AUTHBACKENDCREATE !== 'false' && $surface) {
 					// Create User
 					$falseByRef = false;
-					$this->register_user($username, "", $sha1, $falseByRef, !$remember);
+					$this->register_user($username, (is_array($authSuccess) && isset($authSuccess['email']) ? $authSuccess['email'] : ''), $sha1, $falseByRef, !$remember);
 				} else {
 					// authentication failed
 					//$this->info("Successful Backend Auth, No User in DB, Create Set to False");

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно