Browse Source

added new logger system which combines login logs and organizr logs

CauseFX 4 years ago
parent
commit
03f944fabe

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

@@ -81,6 +81,8 @@ class Organizr
 	public $commit;
 	public $fileHash;
 	public $cookieName;
+	public $log;
+	public $logger;
 	public $organizrLog;
 	public $organizrLoginLog;
 	public $timeExecution;
@@ -123,9 +125,12 @@ class Organizr
 		$this->fileHash = trim($this->fileHash);
 		// Load Config file
 		$this->config = $this->config();
-		// Set organizr Log file location
+		// Set organizr Logs and logger
+		$this->log = $this->setOrganizrLog();
+		$this->setLoggerChannel();
+		// Set organizr Log file location - will deprecate soon
 		$this->organizrLog = ($this->hasDB()) ? $this->config['dbLocation'] . 'organizrLog.json' : false;
-		// Set organizr Login Log file location
+		// Set organizr Login Log file location - will deprecate soon
 		$this->organizrLoginLog = ($this->hasDB()) ? $this->config['dbLocation'] . 'organizrLoginLog.json' : false;
 		// Set Paths
 		$this->paths = array(
@@ -204,6 +209,7 @@ class Organizr
 			}
 		}
 		$this->user = ($user) ?: $this->guestUser();
+		$this->setLoggerChannel();
 		if ($validate) {
 			$this->checkUserTokenForValidation();
 		}
@@ -242,7 +248,7 @@ class Organizr
 			$currentIP = $this->userIP();
 			if ($this->config['blacklisted'] !== '') {
 				if (in_array($currentIP, $this->arrayIP($this->config['blacklisted']))) {
-					$this->debug('User was sent to blackhole - Blacklisted IPs: ' . $this->config['blacklisted']);
+					$this->setLoggerChannel('Authentication')->debug('User was sent to black hole', $this->config['blacklisted']);
 					die($this->showHTML('Blacklisted', $this->config['blacklistedMessage']));
 				}
 			}
@@ -948,7 +954,7 @@ class Organizr
 			// Update config.php version if different to the installed version
 			if ($updateSuccess && $this->version !== $this->config['configVersion']) {
 				$this->updateConfig(array('apply_CONFIG_VERSION' => $this->version));
-				$this->debug('Updated config version to ' . $this->version);
+				$this->setLoggerChannel('Update')->debug('Updated config version to ' . $this->version);
 			}
 			if ($updateSuccess == false) {
 				die($this->showHTML('Database update failed', 'Please manually check logs and fix - Then reload this page'));
@@ -1375,9 +1381,9 @@ class Organizr
 	{
 		if (isset($_COOKIE[$this->cookieName])) {
 			if ($token == $_COOKIE[$this->cookieName]) {
+				$this->setLoggerChannel('Authentication')->debug('Token was invalid - deleting cookie and user session');
 				$this->coookie('delete', $this->cookieName);
 				$this->user = null;
-				$this->debug('Token was invalid - deleting cookie and user session');
 			}
 		}
 	}
@@ -1392,7 +1398,7 @@ class Organizr
 			$user = $this->getUserById($userInfo['userID']);
 			$tokenCheck = ($this->searchArray($allTokens, 'token', $token) !== false);
 			if (!$tokenCheck) {
-				$this->debug('Token failed check Token listing: ' . json_encode($allTokens) . ' User Id: ' . $userInfo['userID']);
+				$this->setLoggerChannel('Authentication')->debug('Token failed check against all token listings', $allTokens);
 				$this->invalidToken($token);
 				if ($api) {
 					$this->setResponse(403, 'Token was not in approved list');
@@ -1423,7 +1429,7 @@ class Organizr
 			if ($api) {
 				$this->setResponse(403, 'Token was invalid');
 			}
-			$this->debug('Token was invalid');
+			$this->setLoggerChannel('Authentication')->debug('User  token was invalid', $token);
 			$this->invalidToken($token);
 		}
 		if ($api) {
@@ -2651,6 +2657,38 @@ class Organizr
 					'attr' => 'rows="10"',
 				),
 			),
+			'Logs' => array(
+				array(
+					'type' => 'select',
+					'name' => 'logLevel',
+					'label' => 'Log Level',
+					'value' => $this->config['logLevel'],
+					'options' => $this->logLevels()
+				),
+				array(
+					'type' => 'number',
+					'name' => 'maxLogFiles',
+					'label' => 'Maximum Log Files',
+					'help' => 'Number of log files to preserve',
+					'value' => $this->config['maxLogFiles'],
+					'placeholder' => '',
+					'attr' => 'min="1"'
+				),
+				array(
+					'type' => 'select',
+					'name' => 'logLiveUpdateRefresh',
+					'label' => 'Live Update Refresh',
+					'value' => $this->config['logLiveUpdateRefresh'],
+					'options' => $this->timeOptions()
+				),
+				array(
+					'type' => 'select',
+					'name' => 'logPageSize',
+					'label' => 'Log Page Size',
+					'value' => $this->config['logPageSize'],
+					'options' => [['name' => '10 Items', 'value' => '10'], ['name' => '25 Items', 'value' => '25'], ['name' => '50 Items', 'value' => '50'], ['name' => '100 Items', 'value' => '100']]
+				),
+			),
 			'Login' => array(
 				array(
 					'type' => 'password-alt',
@@ -3684,6 +3722,7 @@ class Organizr
 	
 	public function createToken($username, $email, $days = 1)
 	{
+		$this->setLoggerChannel('Authentication', $username)->debug('Starting token creation function');
 		$days = ($days > 365) ? 365 : $days;
 		//Quick get user ID
 		$result = $this->getUserByUsernameAndEmail($username, $email);
@@ -3727,6 +3766,7 @@ class Organizr
 			),
 		];
 		$token = $this->processQueries($response);
+		$this->logger->debug('Token creation function has finished');
 		return $jwttoken;
 		
 	}
@@ -3744,6 +3784,9 @@ class Organizr
 		$output = $array['output'] ?? null;
 		$username = (strpos($this->config['authBackend'], 'emby') !== false) ? $username : strtolower($username);
 		$days = (isset($remember)) ? $this->config['rememberMeDays'] : 1;
+		// Set logger channel
+		$this->setLoggerChannel('Authentication', $username);
+		$this->logger->debug('Starting login function');
 		// Set  other variables
 		$function = 'plugin_auth_' . $this->config['authBackend'];
 		$authSuccess = false;
@@ -3752,6 +3795,7 @@ class Organizr
 		// Check Login attempts and kill if over limit
 		if ($loginAttempts > $this->config['loginAttempts'] || isset($_COOKIE['lockout'])) {
 			$this->coookieSeconds('set', 'lockout', $this->config['loginLockout'], $this->config['loginLockout']);
+			$this->logger->warning('User is locked out');
 			$this->setAPIResponse('error', 'User is locked out', 403);
 			return false;
 		}
@@ -3760,16 +3804,17 @@ class Organizr
 			if (isset($this->getallheaders()[$this->config['authProxyHeaderName']])) {
 				$usernameHeader = $this->getallheaders()[$this->config['authProxyHeaderName']] ?? $username;
 				$emailHeader = $this->getallheaders()[$this->config['authProxyHeaderNameEmail']] ?? null;
-				$this->writeLog('success', 'Auth Proxy Function - Starting Verification for IP: ' . $this->userIP() . ' for request on: ' . $_SERVER['REMOTE_ADDR'] . ' against IP/Subnet: ' . $this->config['authProxyWhitelist'], $usernameHeader);
+				$this->setLoggerChannel('Authentication', $usernameHeader);
+				$this->logger->debug('Starting Auth Proxy verification');
 				$whitelistRange = $this->analyzeIP($this->config['authProxyWhitelist']);
 				$authProxy = $this->authProxyRangeCheck($whitelistRange['from'], $whitelistRange['to']);
 				$username = ($authProxy) ? $usernameHeader : $username;
 				$password = ($password == null) ? $this->random_ascii_string(10) : $password;
 				$addEmailToAuthProxy = ($authProxy && $emailHeader) ? ['email' => $emailHeader] : true;
 				if ($authProxy) {
-					$this->writeLog('success', 'Auth Proxy Function - IP: ' . $this->userIP() . ' has been verified', $usernameHeader);
+					$this->logger->info('User has been verified using Auth Proxy');
 				} else {
-					$this->writeLog('error', 'Auth Proxy Function - IP: ' . $this->userIP() . ' has failed verification', $usernameHeader);
+					$this->logger->warning('User has failed verification using Auth Proxy');
 				}
 			}
 		}
@@ -3794,6 +3839,7 @@ class Organizr
 					if (!$authSuccess) {
 						// perform the internal authentication step
 						if (password_verify($password, $result['password'])) {
+							$this->logger->debug('User password has been verified');
 							$authSuccess = true;
 						}
 					}
@@ -3804,6 +3850,7 @@ class Organizr
 			switch ($oAuthType) {
 				case 'plex':
 					if ($this->config['plexoAuth']) {
+						$this->logger->debug('Starting Plex oAuth verification');
 						$tokenInfo = $this->checkPlexToken($oAuth);
 						if ($tokenInfo) {
 							$authSuccess = array(
@@ -3813,10 +3860,14 @@ class Organizr
 								'token' => $tokenInfo['user']['authToken'],
 								'oauth' => 'plex'
 							);
+							$this->logger->debug('User\'s Plex Token has been verified');
 							$this->coookie('set', 'oAuth', 'true', $this->config['rememberMeDays']);
 							$authSuccess = ((!empty($this->config['plexAdmin']) && strtolower($this->config['plexAdmin']) == strtolower($tokenInfo['user']['username'])) || (!empty($this->config['plexAdmin']) && strtolower($this->config['plexAdmin']) == strtolower($tokenInfo['user']['email'])) || $this->checkPlexUser($tokenInfo['user']['username'])) ? $authSuccess : false;
+						} else {
+							$this->logger->warning('User\'s Plex Token has failed verification');
 						}
 					} else {
+						$this->logger->debug('Plex oAuth is not setup');
 						$this->setAPIResponse('error', 'Plex oAuth is not setup', 422);
 						return false;
 					}
@@ -3842,12 +3893,12 @@ class Organizr
 				//does org password need to be updated
 				if (!$passwordMatches) {
 					$this->updateUserPassword($password, $result['id']);
-					$this->writeLog('success', 'Login Function - User Password updated from backend', $username);
+					$this->setLoggerChannel('Authentication', $username)->info('User Password updated from backend');
 				}
 				if ($token !== '') {
 					if ($token !== $result['plex_token']) {
 						$this->updateUserPlexToken($token, $result['id']);
-						$this->writeLog('success', 'Login Function - User Plex Token updated from backend', $username);
+						$this->setLoggerChannel('Authentication', $username)->info('User Plex Token updated from backend');
 					}
 				}
 				// 2FA might go here
@@ -3866,46 +3917,55 @@ class Organizr
 						}
 					}
 					if ($tfaProceed) {
+						$this->setLoggerChannel('Authentication', $username)->debug('Starting 2FA verification');
 						$TFA = explode('::', $result['auth_service']);
 						// Is code with login info?
 						if ($tfaCode == '') {
+							$this->logger->debug('Sending 2FA response to login UI');
 							$this->setAPIResponse('warning', '2FA Code Needed', 422);
 							return false;
 						} else {
 							if (!$this->verify2FA($TFA[1], $tfaCode, $TFA[0])) {
-								$this->writeLoginLog($username, 'error');
-								$this->writeLog('error', 'Login Function - Wrong 2FA', $username);
+								$this->logger->warning('Incorrect 2FA');
 								$this->setAPIResponse('error', 'Wrong 2FA', 422);
 								return false;
+							} else {
+								$this->logger->info('2FA verification passed');
 							}
 						}
 					}
 				}
 				// End 2FA
 				// authentication passed - 1) mark active and update token
+				$this->logger->debug('Starting token creation function');
 				$createToken = $this->createToken($result['username'], $result['email'], $days);
 				if ($createToken) {
-					$this->writeLoginLog($username, 'success');
-					$this->writeLog('success', 'Login Function - A User has logged in', $username);
+					$this->logger->debug('Token has been created');
+					$this->logger->debug('Token creation function has finished');
+					$this->logger->info('User has logged in');
+					$this->logger->debug('Starting SSO check function');
 					$this->ssoCheck($result, $password, $token); //need to work on this
 					return ($output) ? array('name' => $this->cookieName, 'token' => (string)$createToken) : true;
 				} else {
+					$this->logger->warning('Token creation error');
 					$this->setAPIResponse('error', 'Token creation error', 500);
 					return false;
 				}
 			} else {
 				// Create User
+				$this->setLoggerChannel('Authentication', (is_array($authSuccess) && isset($authSuccess['username']) ? $authSuccess['username'] : $username))->debug('Starting Registration function');
 				return $this->authRegister((is_array($authSuccess) && isset($authSuccess['username']) ? $authSuccess['username'] : $username), $password, (is_array($authSuccess) && isset($authSuccess['email']) ? $authSuccess['email'] : ''), $token);
 			}
 		} else {
 			// authentication failed
-			$this->writeLoginLog($username, 'error');
-			$this->writeLog('error', 'Login Function - Wrong Password', $username);
+			$this->setLoggerChannel('Authentication', $username)->warning('Wrong Password');
 			if ($loginAttempts >= $this->config['loginAttempts']) {
+				$this->logger->warning('User exceeded maximum login attempts');
 				$this->coookieSeconds('set', 'lockout', $this->config['loginLockout'], $this->config['loginLockout']);
 				$this->setAPIResponse('error', 'User is locked out', 403);
 				return false;
 			} else {
+				$this->logger->debug('User has not exceeded maximum login attempts');
 				$this->setAPIResponse('error', 'User credentials incorrect', 401);
 				return false;
 			}
@@ -3914,6 +3974,9 @@ class Organizr
 	
 	public function logout()
 	{
+		$this->setLoggerChannel('Authentication');
+		$this->logger->debug('Starting log out process');
+		$this->logger->info('User has logged out');
 		$this->coookie('delete', $this->cookieName);
 		$this->coookie('delete', 'mpt');
 		$this->coookie('delete', 'Auth');
@@ -3924,6 +3987,7 @@ class Organizr
 		$this->clearJellyfinTokens();
 		$this->revokeTokenCurrentUser($this->user['token']);
 		$this->clearKomgaToken();
+		$this->logger->debug('Log out process has finished');
 		$this->user = null;
 		return true;
 	}
@@ -4011,6 +4075,7 @@ class Organizr
 	
 	public function authRegister($username, $password, $email, $token = null)
 	{
+		$this->setLoggerChannel('Authentication', $username);
 		if ($this->config['authBackend'] !== '') {
 			$this->ombiImport($this->config['authBackend']);
 		}
@@ -4401,72 +4466,6 @@ class Organizr
 		);
 	}
 	
-	public function getLog($log, $reverse = true)
-	{
-		switch ($log) {
-			case 'login':
-			case 'loginLog':
-			case 'loginlog':
-				$file = $this->organizrLoginLog;
-				$parent = 'auth';
-				break;
-			case 'org':
-			case 'organizr':
-			case 'organizrLog':
-			case 'orglog':
-				$file = $this->organizrLog;
-				$parent = 'log_items';
-				break;
-			default:
-				$this->setAPIResponse('error', 'Log not defined', 404);
-				return null;
-		}
-		if (!file_exists($file)) {
-			$this->setAPIResponse('error', 'Log does not exist', 404);
-			return null;
-		}
-		$getLog = str_replace("\r\ndate", "date", file_get_contents($file));
-		$gotLog = json_decode($getLog, true);
-		return ($reverse) ? array_reverse($gotLog[$parent]) : $gotLog[$parent];
-	}
-	
-	public function purgeLog($log)
-	{
-		
-		switch ($log) {
-			case 'login':
-			case 'loginLog':
-			case 'loginlog':
-				$file = $this->organizrLoginLog;
-				break;
-			case 'org':
-			case 'organizr':
-			case 'organizrLog':
-			case 'orgLog':
-			case 'orglog':
-				$file = $this->organizrLog;
-				break;
-			default:
-				$this->setAPIResponse('error', 'Log not defined', 404);
-				return null;
-		}
-		if (file_exists($file)) {
-			if (unlink($file)) {
-				$this->writeLog('success', 'Log Management Function - Log: ' . $log . ' has been purged/deleted', 'SYSTEM');
-				$this->setAPIResponse(null, 'Log purged');
-				return true;
-			} else {
-				$this->writeLog('error', 'Log Management Function - Log: ' . $log . ' - Error Occurred', 'SYSTEM');
-				$this->setAPIResponse('error', 'Log could not be purged', 500);
-				return false;
-			}
-		} else {
-			$this->setAPIResponse('error', 'Log does not exist', 404);
-			return false;
-		}
-		
-	}
-	
 	public function checkLog($path)
 	{
 		if (file_exists($path)) {
@@ -7220,7 +7219,7 @@ class Organizr
 				'options' => $options,
 				'data' => $apiData
 			];
-			//$this->debug(json_encode($debugInformation));
+			$this->setLoggerChannel('Socks')->debug('Sending Socks request', $debugInformation);
 			try {
 				switch ($requestObject->getMethod()) {
 					case 'GET':
@@ -7241,6 +7240,7 @@ class Organizr
 				return $call->body;
 			} catch (Requests_Exception $e) {
 				$this->setAPIResponse('error', $e->getMessage(), 500);
+				$this->setLoggerChannel('Socks')->critical($e, $debugInformation);
 				return null;
 			}
 		} else {
@@ -7375,11 +7375,11 @@ class Organizr
 	
 	protected function processQueries(array $request, $migration = false)
 	{
+		$this->setLoggerChannel('Database')->debug('Submitting query to database', $request);
 		$results = array();
 		$firstKey = '';
 		try {
 			foreach ($request as $k => $v) {
-				
 				$query = ($migration) ? $this->otherDb->query($v['query']) : $this->db->query($v['query']);
 				$keyName = (isset($v['key'])) ? $v['key'] : $k;
 				$firstKey = (isset($v['key']) && $k == 0) ? $v['key'] : $k;
@@ -7410,11 +7410,11 @@ class Organizr
 						return false;
 				}
 			}
-			
 		} catch (Exception $e) {
-			$this->debug($e->getMessage());
+			$this->logger->critical($e, $request);
 			return false;
 		}
+		$this->logger->debug('Results from database', $results);
 		return count($request) > 1 ? $results : $results[$firstKey];
 	}
 	

+ 290 - 0
api/functions/log-functions.php

@@ -21,4 +21,294 @@ trait LogFunctions
 	{
 		$this->writeLog('debug', $msg, $username);
 	}
+	
+	public function setOrganizrLog()
+	{
+		if ($this->hasDB()) {
+			$logPath = $this->config['dbLocation'] . 'logs' . DIRECTORY_SEPARATOR;
+			return $logPath . 'organizr.log';
+		}
+		return false;
+	}
+	
+	public function readLog($file, $pageSize = 10, $offset = 0, $filter = 'NONE')
+	{
+		if (file_exists($file)) {
+			$filter = strtoupper($filter);
+			switch ($filter) {
+				case 'DEBUG':
+				case 'INFO':
+				case 'NOTICE':
+				case 'WARNING':
+				case 'ERROR':
+				case 'CRITICAL':
+				case 'ALERT':
+				case 'EMERGENCY':
+					break;
+				case 'NONE':
+					$filter = null;
+					break;
+				default:
+					$filter = 'DEBUG';
+					break;
+			}
+			$lineGenerator = Bcremer\LineReader\LineReader::readLinesBackwards($file);
+			$lines = iterator_to_array($lineGenerator);
+			if ($filter) {
+				$results = [];
+				foreach ($lines as $line) {
+					if (stripos($line, '"' . $filter . '"') !== false) {
+						$results[] = $line;
+					}
+				}
+				$lines = $results;
+			}
+			return $this->formatLogResults($lines, $pageSize, $offset);
+		}
+		return false;
+	}
+	
+	public function formatLogResults($lines, $pageSize, $offset)
+	{
+		$totalLines = count($lines);
+		$totalPages = $totalLines / $pageSize;
+		$results = array_slice($lines, $offset, $pageSize);
+		$lines = [];
+		foreach ($results as $line) {
+			$lines[] = json_decode($line, true);
+		}
+		return [
+			'pageInfo' => [
+				'results' => $totalLines,
+				'totalPages' => ceil($totalPages),
+				'pageSize' => $pageSize,
+				'page' => $offset >= $totalPages ? -1 : ceil($offset / $pageSize) + 1
+			],
+			'results' => $lines
+		];
+	}
+	
+	public function getLatestLogFile()
+	{
+		if ($this->log) {
+			if (isset($this->log)) {
+				$folder = $this->config['dbLocation'] . 'logs' . DIRECTORY_SEPARATOR;
+				$directoryIterator = new RecursiveDirectoryIterator($folder, FilesystemIterator::SKIP_DOTS);
+				$iteratorIterator = new RecursiveIteratorIterator($directoryIterator);
+				$files = [];
+				foreach ($iteratorIterator as $info) {
+					$files[] = $info->getPathname();
+				}
+				if (count($files) > 0) {
+					usort($files, function ($x, $y) {
+						preg_match('/[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])/', $x, $xArray);
+						preg_match('/[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])/', $y, $yArray);
+						return strtotime($xArray[0]) < strtotime($yArray[0]);
+					});
+					if (file_exists($files[0])) {
+						return $files[0];
+					}
+				}
+			}
+		}
+		return false;
+	}
+	
+	public function getLogFiles()
+	{
+		if ($this->log) {
+			if (isset($this->log)) {
+				$folder = $this->config['dbLocation'] . 'logs' . DIRECTORY_SEPARATOR;
+				$directoryIterator = new RecursiveDirectoryIterator($folder, FilesystemIterator::SKIP_DOTS);
+				$iteratorIterator = new RecursiveIteratorIterator($directoryIterator);
+				$files = [];
+				foreach ($iteratorIterator as $info) {
+					$files[] = $info->getPathname();
+				}
+				if (count($files) > 0) {
+					usort($files, function ($x, $y) {
+						preg_match('/[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])/', $x, $xArray);
+						preg_match('/[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])/', $y, $yArray);
+						return strtotime($xArray[0]) < strtotime($yArray[0]);
+					});
+					return $files;
+				}
+			}
+		}
+		return false;
+	}
+	
+	public function setLoggerChannel($channel = 'Organizr', $username = null)
+	{
+		$setLogger = false;
+		if ($this->logger) {
+			if (strtolower($this->logger->getChannel()) !== strtolower($channel)) {
+				$setLogger = true;
+			}
+			if ($username) {
+				if (strtolower($this->logger->getTraceId()) !== strtolower($channel)) {
+					$setLogger = true;
+				}
+			}
+		} else {
+			$setLogger = true;
+		}
+		if ($setLogger) {
+			$this->setupLogger($channel, $username);
+		}
+		return $this->logger;
+	}
+	
+	public function setupLogger($channel = 'Organizr', $username = null)
+	{
+		if ($this->log) {
+			if (!$username) {
+				$username = (isset($this->user['username'])) ? $this->user['username'] : 'System';
+			}
+			$loggerBuilder = new Nekonomokochan\PhpJsonLogger\LoggerBuilder();
+			$loggerBuilder->setMaxFiles($this->config['maxLogFiles']);
+			$loggerBuilder->setFileName($this->log);
+			$loggerBuilder->setTraceId($username);
+			$loggerBuilder->setChannel(ucwords(strtolower($channel)));
+			switch ($this->config['logLevel']) {
+				case 'DEBUG':
+					$logLevel = Nekonomokochan\PhpJsonLogger\LoggerBuilder::DEBUG;
+					break;
+				case 'INFO':
+					$logLevel = Nekonomokochan\PhpJsonLogger\LoggerBuilder::INFO;
+					break;
+				case 'NOTICE':
+					$logLevel = Nekonomokochan\PhpJsonLogger\LoggerBuilder::NOTICE;
+					break;
+				case 'ERROR':
+					$logLevel = Nekonomokochan\PhpJsonLogger\LoggerBuilder::ERROR;
+					break;
+				case 'CRITICAL':
+					$logLevel = Nekonomokochan\PhpJsonLogger\LoggerBuilder::CRITICAL;
+					break;
+				case 'ALERT':
+					$logLevel = Nekonomokochan\PhpJsonLogger\LoggerBuilder::ALERT;
+					break;
+				case 'EMERGENCY':
+					$logLevel = Nekonomokochan\PhpJsonLogger\LoggerBuilder::EMERGENCY;
+					break;
+				default:
+					$logLevel = Nekonomokochan\PhpJsonLogger\LoggerBuilder::WARNING;
+					break;
+			}
+			$loggerBuilder->setLogLevel($logLevel);
+			try {
+				$this->logger = $loggerBuilder->build();
+			} catch (Exception $e) {
+				// nothing so far
+			}
+			/* setup:
+			set the log channel before you send log (You can set an optional Username (2nd Variable) | If user is logged already logged in, it will use their username):
+			$this->setLoggerChannel('Plex Homepage');
+			normal log:
+			$this->logger->info('test');
+			normal log with context ($context must be an array):
+			$this->logger->info('test', $context);
+			exception:
+			$this->logger->critical($exception, $context);
+			*/
+		}
+	}
+	
+	public function getLog($pageSize = 10, $offset = 0, $filter = 'NONE', $number = 0)
+	{
+		if ($this->log) {
+			if (isset($this->log)) {
+				if ($number !== 0) {
+					$logs = $this->getLogFiles();
+					$log = $logs[$number] ?? $this->getLatestLogFile();
+				} else {
+					$log = $this->getLatestLogFile();
+				}
+				$readLog = $this->readLog($log, 1000, 0, $filter);
+				$this->setResponse(200, 'Results for log: ' . $log, $readLog);
+				return $readLog;
+			} else {
+				$this->setResponse(404, 'Log not found');
+				return false;
+			}
+		} else {
+			$this->setResponse(409, 'Logging not setup');
+			return false;
+		}
+	}
+	
+	public function purgeLog($number)
+	{
+		$this->setLoggerChannel('Logger');
+		$this->logger->debug('Starting log purge function');
+		if ($this->log) {
+			$this->logger->debug('Checking if log id exists');
+			if ($number !== 0) {
+				$logs = $this->getLogFiles();
+				$file = $logs[$number] ?? false;
+				if (!$file) {
+					$this->setResponse(404, 'Log not found');
+					return false;
+				}
+			} else {
+				$file = $this->getLatestLogFile();
+			}
+			preg_match('/[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])/', $file, $log);
+			$log = $log[0];
+			$this->logger->debug('Checking if log exists');
+			if (file_exists($file)) {
+				$this->logger->debug('Log: ' . $log . ' does exist');
+				$this->logger->debug('Attempting to purge log: ' . $log);
+				if (unlink($file)) {
+					$this->logger->info('Log: ' . $log . ' has been purged/deleted');
+					$this->setAPIResponse(null, 'Log purged');
+					return true;
+				} else {
+					$this->logger->warning('Log: ' . $log . ' could not be purged/deleted');
+					$this->setAPIResponse('error', 'Log could not be purged', 500);
+					return false;
+				}
+			} else {
+				$this->logger->debug('Log does not exist');
+				$this->setAPIResponse('error', 'Log does not exist', 404);
+				return false;
+			}
+		} else {
+			$this->setResponse(409, 'Logging not setup');
+			return false;
+		}
+	}
+	
+	public function logArray($context)
+	{
+		if (!is_array($context)) {
+			if (is_string($context)) {
+				return ['data' => $context];
+			} else {
+				$context = (string)$context;
+				return ['data' => $context];
+			}
+		} else {
+			return $context;
+		}
+	}
+	
+	function buildLogDropdown()
+	{
+		$logs = $this->getLogFiles();
+		//<select class='form-control settings-dropdown-box system-settings-menu'><option value=''>About</option></select>
+		if (count($logs) > 0) {
+			$options = '';
+			$i = 0;
+			foreach ($logs as $k => $log) {
+				$selected = $i == 0 ? 'selected' : '';
+				preg_match('/[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])/', $log, $name);
+				$options .= '<option data-id="' . $k . '" value="api/v2/log/' . $k . '" ' . $selected . '>' . $name[0] . '</option>';
+				$i++;
+			}
+			return '<select class="form-control choose-organizr-log">' . $options . '</select>';
+		}
+		return false;
+	}
 }

+ 92 - 157
api/pages/settings-settings-logs.php

@@ -11,165 +11,100 @@ function get_page_settings_settings_logs($Organizr)
 	if (!$Organizr->qualifyRequest(1, true)) {
 		return false;
 	}
+	$logsDropdown = $Organizr->buildLogDropdown();
 	return '
-    <script>
-    $(document).on("click", ".swapLog", function(e) {
-    	switch ($(this).attr(\'data-name\')){
-    	case \'loginLog\':
-    		loginLogTable.ajax.reload(null, false);
-    	break;
-    	case \'orgLog\':
-    		organizrLogTable.ajax.reload(null, false);
-    	break;
-    	default:
-    		//nada
-    		//loginLogTable
-    	}
-        var log = $(this).attr(\'data-name\')+\'Div\';
-        $(\'.logTable\').addClass(\'hidden\');
-        $(\'.\'+log).addClass(\'show\').removeClass(\'hidden\');
-    	$(\'.swapLog\').removeClass(\'active\');
-    	$(this).addClass(\'active\');
-    });
-    </script>
-    <div class="btn-group m-b-20 pull-left">
-        <button type="button" class="btn btn-default btn-outline waves-effect bg-org swapLog active" data-name="loginLog" data-path="' . $Organizr->organizrLoginLog . '" lang="en">Login Log</button>
-        <button type="button" class="btn btn-default btn-outline waves-effect bg-org swapLog" data-name="orgLog" data-path="' . $Organizr->organizrLog . '" lang="en">Organizr Log</button>
-    </div>
-    <button class="btn btn-danger btn-sm waves-effect waves-light pull-right purgeLog" type="button"><span class="btn-label"><i class="fa fa-trash"></i></span><span lang="en">Purge Log</span></button>
-    <div class="clearfix"></div>
-    <div class="white-box bg-org logTable loginLogDiv">
-        <h3 class="box-title m-b-0" lang="en">Login Logs</h3>
-        <div class="table-responsive">
-            <table id="loginLogTable" class="table table-striped">
-                <thead>
-                    <tr>
-                        <th lang="en">Date</th>
-                        <th lang="en">Username</th>
-                        <th lang="en">IP Address</th>
-                        <th lang="en">Type</th>
-                    </tr>
-                </thead>
-    			<tfoot>
-                    <tr>
-                        <th lang="en">Date</th>
-                        <th lang="en">Username</th>
-                        <th lang="en">IP Address</th>
-                        <th lang="en">Type</th>
-                    </tr>
-                </tfoot>
-                <tbody></tbody>
-            </table>
-        </div>
-    </div>
-    <div class="white-box bg-org logTable orgLogDiv hidden">
-        <h3 class="box-title m-b-0" lang="en">Organizr Logs</h3>
-        <div class="table-responsive">
-            <table id="organizrLogTable" class="table table-striped">
-                <thead>
-                    <tr>
-                        <th lang="en">Date</th>
-                        <th lang="en">Username</th>
-                        <th lang="en">IP Address</th>
-                        <th lang="en">Message</th>
-                        <th lang="en">Type</th>
-                    </tr>
-                </thead>
-                <tfoot>
-                    <tr>
-                        <th lang="en">Date</th>
-                        <th lang="en">Username</th>
-                        <th lang="en">IP Address</th>
-                        <th lang="en">Message</th>
-                        <th lang="en">Type</th>
-                    </tr>
-                </tfoot>
-                <tbody></tbody>
-            </table>
-        </div>
-    </div>
-    <!-- /.container-fluid -->
-    <script>
-    //$.fn.dataTable.moment(\'DD-MMM-Y HH:mm:ss\');
-    $.fn.dataTable.ext.errMode = \'none\';
-    var loginLogTable = $("#loginLogTable")
-    .on( \'error.dt\', function ( e, settings, techNote, message ) {
-        console.log( \'An error has been reported by DataTables: \', message );
-        loginLogTable.draw();
-    } )
-    .DataTable( {
-    		"ajax": {
-				"url": "api/v2/log/login",
-				"dataSrc": function ( json ) {
-					return json.response.data;
+	<div class="btn-group m-b-20 pull-left">' . $logsDropdown . '</div>
+	<button class="btn btn-danger waves-effect waves-light pull-right purgeLog" type="button" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="Purge Log"><i class="fa fa-trash"></i></span></button>
+	<button onclick="organizrLogTable.clear().draw().ajax.reload(null, false)" class="btn btn-info waves-effect waves-light pull-right reloadLog m-r-5" type="button" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="Reload Log"><i class="fa fa-refresh"></i></span></button>
+	<button onclick="toggleKillOrganizrLiveUpdate(' . $Organizr->config['logLiveUpdateRefresh'] . ');" class="btn btn-primary waves-effect waves-light pull-right organizr-log-live-update m-r-5" type="button" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="Live Update"><i class="fa fa-clock-o"></i></span></button>
+	<div class="clearfix"></div>
+	<div class="white-box bg-org logTable orgLogDiv">
+		<h3 class="box-title m-b-0" lang="en">Organizr Logs</h3>
+		<div class="table-responsive">
+			<table id="organizrLogTable" class="table table-striped compact nowrap">
+				<thead>
+					<tr>
+						<th lang="en">Date</th>
+						<th lang="en">Severity</th>
+						<th lang="en">Function</th>
+						<th lang="en">Message</th>
+						<th lang="en">IP Address</th>
+						<th lang="en">User</th>
+						<th></th>
+					</tr>
+				</thead>
+				<tfoot>
+					<tr>
+						<th lang="en">Date</th>
+						<th lang="en">Severity</th>
+						<th lang="en">Function</th>
+						<th lang="en">Message</th>
+						<th lang="en">IP Address</th>
+						<th lang="en">User</th>
+						<th></th>
+					</tr>
+				</tfoot>
+				<tbody></tbody>
+			</table>
+		</div>
+	</div>
+	<!-- /.container-fluid -->
+	<script>
+	$.fn.dataTable.ext.errMode = "none";
+	var organizrLogTable = $("#organizrLogTable")
+	.on("error.dt", function(e, settings, techNote, message) {
+		console.log("An error has been reported by DataTables: ", message);
+		organizrLogTable.draw();
+	})
+	.DataTable({
+		"ajax": {
+			"url": "api/v2/log/0",
+			"dataSrc": function(json) {
+				return json.response.data.results;
+			}
+		},
+		"deferRender": true,
+		"pageLength": ' . (int)$Organizr->config['logPageSize'] . ',
+		"columns": [{
+			data: "datetime",
+			render: function(data, type, row) {
+				if (type === "display" || type === "filter") {
+					var m = moment.tz(data + "Z", activeInfo.timezone);
+					return moment(m).format("LLL");
 				}
-			},
-            "columns": [
-                { data: \'utc_date\',
-                    render: function ( data, type, row ) {
-                        if ( type === \'display\' || type === \'filter\' ) {
-                            var m = moment.tz(data, activeInfo.timezone);
-                            return moment(m).format(\'LLL\');
-                        }
-                        return data;
-                    }
-                },
-                { "data": "username" },
-                { data: \'ip\',
-                    render: function ( data, type, row ) {
-                        return ipInfoSpan(data);
-                    }
-                },
-                { data: \'auth_type\',
-                    render: function ( data, type, row ) {
-                        if ( type === \'display\' || type === \'filter\' ) {
-                            return logIcon(data);
-                        }
-                        return logIcon(data);
-                    }
-                }
-            ],
-            "order": [[ 0, \'desc\' ]],
-    } );
-    var organizrLogTable = $("#organizrLogTable")
-    .on( \'error.dt\', function ( e, settings, techNote, message ) {
-        console.log( \'An error has been reported by DataTables: \', message );
-        organizrLogTable.draw();
-    } )
-    .DataTable( {
-            "ajax": {
-				"url": "api/v2/log/organizr",
-				"dataSrc": function ( json ) {
-					return json.response.data;
+				return data;
+			}
+		}, {
+			data: "log_level",
+			render: function(data, type, row) {
+				if (type === "display" || type === "filter") {
+					return logIcon(data);
 				}
+				return logIcon(data);
+			}
+		}, {
+			data: "channel"
+		}, {
+			data: "message"
+		}, {
+			data: "remote_ip_address",
+			"width": "5%",
+			render: function(data, type, row) {
+				return ipInfoSpan(data);
+			}
+		}, {
+			"data": "trace_id"
+		}, {
+			data: "context",
+			render: function(data, type, row) {
+				return logContext(row);
 			},
-                "columns": [
-                { data: \'utc_date\',
-                    render: function ( data, type, row ) {
-                        if ( type === \'display\' || type === \'filter\' ) {
-                            var m = moment.tz(data, activeInfo.timezone);
-                            return moment(m).format(\'LLL\');
-                        }
-                    return data;}
-                    },
-                { "data": "username" },
-                { data: \'ip\',
-                    render: function ( data, type, row ) {
-                        return ipInfoSpan(data);
-                    }
-                },
-                { "data": "message" },
-                { data: \'type\',
-                    render: function ( data, type, row ) {
-                        if ( type === \'display\' || type === \'filter\' ) {
-                            return logIcon(data);
-                        }
-                        return logIcon(data);
-                    }
-                }
-            ],
-            "order": [[ 0, \'desc\' ]],
-    } );
-    </script>
-    ';
+			orderable: false
+		}, ],
+		"order": [
+			[0, "desc"]
+		],
+	})
+	</script>
+	';
 }

+ 6 - 4
api/v2/routes/log.php

@@ -1,9 +1,10 @@
 <?php
-$app->get('/log/{log}', function ($request, $response, $args) {
+$app->get('/log[/{number}]', function ($request, $response, $args) {
 	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
 	if ($Organizr->checkRoute($request)) {
 		if ($Organizr->qualifyRequest(1, true)) {
-			$GLOBALS['api']['response']['data'] = $Organizr->getLog($args['log']);
+			$args['number'] = $args['number'] ?? 0;
+			$Organizr->getLog(1000, 0, 'NONE', $args['number']);
 		}
 	}
 	$response->getBody()->write(jsonE($GLOBALS['api']));
@@ -11,11 +12,12 @@ $app->get('/log/{log}', function ($request, $response, $args) {
 		->withHeader('Content-Type', 'application/json;charset=UTF-8')
 		->withStatus($GLOBALS['responseCode']);
 });
-$app->delete('/log/{log}', function ($request, $response, $args) {
+$app->delete('/log[/{number}]', function ($request, $response, $args) {
 	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
 	if ($Organizr->checkRoute($request)) {
 		if ($Organizr->qualifyRequest(1, true)) {
-			$Organizr->purgeLog($args['log']);
+			$args['number'] = $args['number'] ?? 0;
+			$Organizr->purgeLog($args['number']);
 		}
 	}
 	$response->getBody()->write(jsonE($GLOBALS['api']));

+ 20 - 19
js/custom.js

@@ -1460,38 +1460,28 @@ $(document).on("click", ".newAPIKey", function () {
 });
 // purge log
 $(document).on("click", ".purgeLog", function () {
-    var name = $('.swapLog.active').attr('data-name');
-    if(name !== ''){
-	    var post = {
-		    api:'api/v2/log/' + name,
+    let logId = $('.choose-organizr-log option:selected').attr('data-id');
+    if(logId){
+	    let post = {
+		    api:'api/v2/log/' + logId,
 		    messageTitle:'',
-		    messageBody:window.lang.translate('Deleted Log')+': '+name,
+		    messageBody:window.lang.translate('Deleted Log'),
 		    error:'Organizr Function: User API Connection Failed'
 	    };
 	    organizrAPI2('DELETE',post.api,'',true).success(function(data) {
 		    loadSettingsPage2('api/v2/page/settings_settings_logs','#settings-settings-logs','Log Viewer');
 		    try {
-			    var response = data.response;
+			    let response = data.response;
+			    message(post.messageTitle,post.messageBody,activeInfo.settings.notifications.position,"#FFF","success","5000");
 		    }catch(e) {
 			    organizrCatchError(e,data);
 		    }
-		    message(post.messageTitle,post.messageBody,activeInfo.settings.notifications.position,"#FFF","success","5000");
-		    var callbacks = $.Callbacks();
-		    switch ($(this).attr('data-name')){
-			    case 'loginLog':
-				    loginLogTable.ajax.reload(null, false);
-				    break;
-			    case 'orgLog':
-				    organizrLogTable.ajax.reload(null, false);
-				    break;
-			    default:
-		    }
-		    if(callbacks){ callbacks.fire(); }
 	    }).fail(function(xhr) {
 		    OrganizrApiError(xhr, 'API Error');
 	    });
+    }else{
+	    message('','Could not get Log Id',activeInfo.settings.notifications.position,'#FFF','warning','5000');
     }
-
 });
 $(document).on("click", ".delete-backup", function () {
 	$('#settings-settings-backup').block({
@@ -1932,4 +1922,15 @@ $(document).on('click', '.toggle-side-menu', function() {
 // Toggle Side Menu Other
 $(document).on('click', '.ti-shift-left.mouse', function() {
 	toggleSideMenu();
+});
+
+// Log Details
+$(document).on('click', '.log-details', function() {
+	let details = $(this).attr('data-details');
+	formatLogDetails(details);
+});
+
+// Choose Log choose-organizr-log
+$(document).on("change", ".choose-organizr-log", function () {
+	organizrLogTable.ajax.url($(this).val()).load();
 });

File diff suppressed because it is too large
+ 0 - 0
js/custom.min.js


+ 102 - 7
js/functions.js

@@ -4580,27 +4580,75 @@ function language(language){
 	var language = language.split("-");
 	return language[0];
 }
-function logIcon(type){
+function logIcon(type, label = false){
+	type = type.toLowerCase();
+	let info = {"color" : "info", "icon": "fa fa-check"};
 	switch (type) {
 		case "success":
-			return '<i class="fa fa-check text-success"></i><span class="hidden">Success</span>';
+			info.color = 'info';
+			info.icon = 'fa fa-check';
 			break;
 		case "info":
-			return '<i class="fa fa-info text-info"></i><span class="hidden">Info</span>';
+			info.color = 'info';
+			info.icon = 'mdi mdi-information';
+			break;
+		case "notice":
+			info.color = 'inverse';
+			info.icon = 'mdi mdi-information-variant';
 			break;
 		case "debug":
-			return '<i class="fa fa-code text-primary"></i><span class="hidden">Debug</span>';
+			info.color = 'primary';
+			info.icon = 'mdi mdi-code-tags-check';
 			break;
 		case "warning":
-			return '<i class="fa fa-exclamation-triangle text-warning"></i><span class="hidden">Warning</span>';
+			info.color = 'warning';
+			info.icon = 'mdi mdi-alert-box';
 			break;
 		case "error":
-			return '<i class="fa fa-close text-danger"></i><span class="hidden">Error</span>';
+			info.color = 'danger';
+			info.icon = 'mdi mdi-alert-outline';
+			break;
+		case "critical":
+			info.color = 'danger';
+			info.icon = 'mdi mdi-alert';
+			break;
+		case "alert":
+			info.color = 'danger';
+			info.icon = 'mdi mdi-alert-octagon';
+			break;
+		case "emergency":
+			info.color = 'danger';
+			info.icon = 'mdi mdi-alert-octagram';
 			break;
 		default:
-			return '<i class="fa fa-exclamation-triangle text-warning"></i><span class="hidden">Warning</span>';
+			info = {"color" : "info", "icon": "fa fa-check"};
+			break;
+	}
+	if(label){
+		return '<span class="label label-'+info.color+' log-label"> <i class="fa '+info.icon+' m-l-5 fa-fw"></i>&nbsp; <span lang="en" class="text-uppercase">'+type+'</span></span>';
+	}else{
+		return '<button class="btn btn-xs btn-'+info.color+' waves-effect waves-light log-label no-mouse" type="button"><span class="btn-label pull-left"><i class="'+info.icon+' fa-fw"></i></span><span class="text-uppercase" lang="en">'+type+'</span></button>';
+	}
+}
+function toggleKillOrganizrLiveUpdate(interval = 5000){
+	if($('.organizr-log-live-update').hasClass('kill-organizr-log')){
+		clearTimeout(timeouts['organizr-log']);
+		$('.organizr-log-live-update').toggleClass('kill-organizr-log');
+	}else{
+		$('.organizr-log-live-update').toggleClass('kill-organizr-log');
+		organizrLogLiveUpdate(interval);
 	}
 }
+function organizrLogLiveUpdate(interval = 5000){
+	var timeout = interval;
+	let timeoutTitle = 'organizr-log';
+	$('.organizr-log-live-update i').toggleClass('fa-clock-o fa-circle-o-notch fa-spin');
+	organizrLogTable.ajax.reload(null, false);
+	setTimeout(function(){ $('.organizr-log-live-update i').toggleClass('fa-clock-o fa-circle-o-notch fa-spin'); }, 500);
+	if(typeof timeouts[timeoutTitle] !== 'undefined'){ clearTimeout(timeouts[timeoutTitle]); }
+	timeouts[timeoutTitle] = setTimeout(function(){ organizrLogLiveUpdate(timeout); }, timeout);
+	delete timeout;
+}
 function radioLoop(element){
 	$('[type=radio][id!="'+element.id+'"]').each(function() { this.checked=false });
 }
@@ -10902,6 +10950,53 @@ function oAuthLoginNeededCheck() {
 function ipInfoSpan(ip){
     return '<span class="ipInfo mouse">'+ip+'</span>';
 }
+function logContext(row){
+	let buttons = '';
+	buttons += (Object.keys(row).length > 0) ? '<button data-toggle="tooltip" title="" data-original-title="View Details" class="btn btn-xs btn-primary waves-effect waves-light log-details m-r-5" data-details=\''+JSON.stringify(row)+'\'><i class="mdi mdi-file-find"></i></button>' : '';
+	buttons += (Object.keys(row).length > 0) ? '<button data-toggle="tooltip" title="" data-original-title="Copy Log" class="btn btn-xs btn-info waves-effect waves-light clipboard m-r-5" data-clipboard-text=\''+JSON.stringify(row)+'\'><i class="mdi mdi-content-copy"></i></button>' : '';
+	return buttons;
+}
+function formatLogDetails(details){
+	if(!details){
+		return false;
+	}
+	details = JSON.parse(details);
+	let m = moment.tz(details.datetime + 'Z', activeInfo.timezone);
+	details.datetime = moment(m).format('LLL');
+	let items = '';
+	items += `<li><div class="bg-inverse"><i class="mdi mdi-calendar-text text-white"></i></div> ${details.datetime}<span class="text-muted" lang="en">Date</span></li>`;
+	items += `<li><div class="bg-inverse"><i class="mdi mdi-account-box-outline text-white"></i></div> ${details.trace_id}<span class="text-muted" lang="en">User</span></li>`;
+	items += `<li><div class="bg-info"><i class="mdi mdi-function text-white"></i></div> ${details.channel}<span class="text-muted" lang="en">Function</span></li>`;
+	items += `<li><div class="bg-plex"><i class="mdi mdi-language-php text-white"></i></div> ${details.file}<code>#L${details.line}</code><span class="text-muted" lang="en">File</span></li>`;
+	let items2 = '';
+	items2 += (Object.keys(details.context).length > 0) ? `<div class="sl-item"><div class="sl-left bg-inverse"> <i class="mdi mdi-json"></i></div><div class="sl-right"><div class="p-t-10 desc" lang="en">Context</div></div><pre class="m-5 fc-scroller">${JSON.stringify(details.context,null, 5)}</pre></div>` : '';
+	items2 += (typeof details.errors !== 'undefined') ? `<div class="sl-item"><div class="sl-left bg-danger"> <i class="mdi mdi-code-braces"></i></div><div class="sl-right"><div class="p-t-10 desc" lang="en">Errors</div></div><pre class="m-5 fc-scroller">${JSON.stringify(details.errors,null, 5)}</pre></div>` : '';
+	var div = `
+		<div class="col-lg-12">
+			<div class="panel panel-default text-left">
+				<div class="panel-heading"><i class="mdi mdi-file-find fa-lg fa-2x"></i> <span lang="en">Log Details</span> <span class="pull-right">${logIcon(details.log_level, true)}</span></div>
+				<div class="panel-wrapper collapse in">
+					<div class="panel-body bg-org">
+						<h3>${details.message}</h3>
+						<div class="white-box">
+							<ul class="feeds">
+								${items}
+							</ul>
+						</div>
+						<div class="steamline">
+							${items2}
+						</div>
+					</div>
+				</div>
+			</div>
+		</div>`;
+	swal({
+		content: createElementFromHTML(div),
+		buttons: false,
+		className: 'orgAlertTransparent'
+	});
+	pageLoad();
+}
 function checkToken(activate = false){
     if(typeof activeInfo !== 'undefined'){
         if(typeof activeInfo.settings.misc.uuid !== 'undefined'){

Some files were not shown because too many files changed in this diff