Bladeren bron

Merge pull request #1165 from causefx/v2-develop

V2 develop
causefx 7 jaren geleden
bovenliggende
commit
c02bf09ed0
57 gewijzigde bestanden met toevoegingen van 5231 en 792 verwijderingen
  1. 13 1
      api/config/default.php
  2. 1 0
      api/functions.php
  3. 56 4
      api/functions/api-functions.php
  4. 307 35
      api/functions/homepage-connect-functions.php
  5. 76 0
      api/functions/homepage-functions.php
  6. 17 2
      api/functions/log-functions.php
  7. 77 9
      api/functions/normal-functions.php
  8. 210 4
      api/functions/organizr-functions.php
  9. 4 4
      api/functions/sso-functions.php
  10. 3 1
      api/functions/static-globals.php
  11. 30 6
      api/index.php
  12. 8 1
      api/pages/homepage.php
  13. 1 1
      api/pages/login.php
  14. 4 1
      api/pages/settings.php
  15. 3 3
      api/pages/wizard.php
  16. 4 3
      api/plugins/config/invites.php
  17. 34 5
      api/plugins/invites.php
  18. 100 3
      api/plugins/js/invites.js
  19. 1 1
      css/colors/blue-dark.css
  20. 2733 6
      css/organizr.css
  21. 0 0
      css/organizr.min.css
  22. 5 4
      index.php
  23. 96 67
      js/custom.js
  24. 0 0
      js/custom.min.js
  25. 314 24
      js/functions.js
  26. 520 0
      js/langpack/ca[Catalan].json
  27. 37 37
      js/langpack/da[Danish].json
  28. 37 37
      js/langpack/de-ch[German (Switzerland)].json
  29. 37 37
      js/langpack/el[Greek].json
  30. 37 37
      js/langpack/en[English].json
  31. 37 37
      js/langpack/es[Spanish].json
  32. 39 39
      js/langpack/fr[French].json
  33. 37 37
      js/langpack/hu[Hungarian].json
  34. 4 4
      js/langpack/it[Italian].json
  35. 37 37
      js/langpack/lt[Lithuanian].json
  36. 37 37
      js/langpack/nb[Bokmål].json
  37. 9 9
      js/langpack/nl[Dutch].json
  38. 10 10
      js/langpack/pl[Polish].json
  39. 37 37
      js/langpack/ro[Romanian].json
  40. 101 101
      js/langpack/ru[Russian].json
  41. 37 37
      js/langpack/sk[Slovak].json
  42. 37 37
      js/langpack/sv[Swedish].json
  43. 37 37
      js/langpack/zh[Chinese].json
  44. 7 0
      js/version.json
  45. 0 0
      plugins/bower_components/sweetalert/sweetalert.min.js
  46. BIN
      plugins/images/tabs/AdGuardHome.png
  47. BIN
      plugins/images/tabs/cockpit.png
  48. BIN
      plugins/images/tabs/healthchecks.png
  49. 0 0
      plugins/images/tabs/homeassistant.png
  50. BIN
      plugins/images/tabs/jupyter.png
  51. BIN
      plugins/images/tabs/nodered.png
  52. BIN
      plugins/images/tabs/opnsense.png
  53. BIN
      plugins/images/tabs/radarr4k.png
  54. BIN
      plugins/images/tabs/sonarr4k.png
  55. BIN
      plugins/images/tabs/vera.png
  56. BIN
      plugins/images/tabs/vscode.png
  57. BIN
      plugins/images/tabs/xenorchestra.png

+ 13 - 1
api/config/default.php

@@ -131,6 +131,10 @@ return array(
 	'ombiLimitUser' => false,
 	'ombiRefresh' => '600000',
 	'ombiTvDefault' => 'all',
+	'homepageHealthChecksEnabled' => false,
+	'healthChecksToken' => '',
+	'healthChecksTags' => '',
+	'homepageHealthChecksAuth' => '1',
 	'homepageOrdercustomhtml' => '1',
 	'homepageOrdercustomhtmlTwo' => '2',
 	'homepageOrdertransmission' => '3',
@@ -147,11 +151,13 @@ return array(
 	'homepageOrdercalendar' => '14',
 	'homepageOrderrTorrent' => '15',
 	'homepageOrderdownloader' => '16',
+	'homepageOrderhealthchecks' => '17',
 	'homepageShowStreamNames' => false,
 	'homepageShowStreamNamesAuth' => '1',
 	'homepageStreamRefresh' => '60000',
 	'homepageRecentRefresh' => '60000',
 	'homepageDownloadRefresh' => '60000',
+	'homepageHealthChecksRefresh' => '60000',
 	'homepagePlexStreams' => false,
 	'homepagePlexStreamsAuth' => '1',
 	'homepagePlexRecent' => false,
@@ -211,5 +217,11 @@ return array(
 	'plexStrictFriends' => true,
 	'debugAreaAuth' => '1',
 	'commit' => 'n/a',
-	'ombiLimit' => '50'
+	'ombiLimit' => '50',
+	'localIPFrom' => '',
+	'localIPTo' => '',
+	'sandbox' => 'allow-presentation,allow-forms,allow-same-origin,allow-pointer-lock,allow-scripts,allow-popups,allow-modals,allow-top-navigation',
+	'description' => 'Organizr - Accept no others',
+	'debugErrors' => true,
+	'healthChecksURL' => 'https://healthchecks.io/api/v1/checks/'
 );

+ 1 - 0
api/functions.php

@@ -11,6 +11,7 @@ foreach (glob(__DIR__ . DIRECTORY_SEPARATOR . 'functions' . DIRECTORY_SEPARATOR
 $GLOBALS['root'] = dirname(__DIR__, 1);
 $GLOBALS['uuid'] = '';
 $GLOBALS['rememberMeDays'] = '99';
+$GLOBALS['timeExecution'] = timeExecution();
 // Add in default and custom settings
 configLazy();
 // Define Logs and files after db location is set

+ 56 - 4
api/functions/api-functions.php

@@ -3,6 +3,56 @@
 /** @noinspection SqlResolve */
 /** @noinspection SqlResolve */
 /** @noinspection SyntaxError */
+function apiLogin()
+{
+	$array = array(
+		'data' => array(
+			array(
+				'name' => 'username',
+				'value' => (isset($_POST['username'])) ? $_POST['username'] : false
+			),
+			array(
+				'name' => 'password',
+				'value' => (isset($_POST['password'])) ? $_POST['password'] : false
+			),
+			array(
+				'name' => 'remember',
+				'value' => (isset($_POST['remember'])) ? true : false
+			),
+			array(
+				'name' => 'oAuth',
+				'value' => (isset($_POST['oAuth'])) ? $_POST['oAuth'] : false
+			),
+			array(
+				'name' => 'oAuthType',
+				'value' => (isset($_POST['oAuthType'])) ? $_POST['oAuthType'] : false
+			),
+			array(
+				'name' => 'tfaCode',
+				'value' => (isset($_POST['tfaCode'])) ? $_POST['tfaCode'] : false
+			),
+			array(
+				'name' => 'output',
+				'value' => true
+			),
+		)
+	);
+	foreach ($array['data'] as $items) {
+		foreach ($items as $key => $value) {
+			if ($key == 'name') {
+				$newKey = $value;
+			}
+			if ($key == 'value') {
+				$newValue = $value;
+			}
+			if (isset($newKey) && isset($newValue)) {
+				$$newKey = $newValue;
+			}
+		}
+	}
+	return login($array);
+}
+
 function login($array)
 {
 	// Grab username and Password from login form
@@ -23,6 +73,7 @@ function login($array)
 	$username = (strpos($GLOBALS['authBackend'], 'emby') !== false) ? $username : strtolower($username);
 	$days = (isset($remember)) ? $GLOBALS['rememberMeDays'] : 1;
 	$oAuth = (isset($oAuth)) ? $oAuth : false;
+	$output = (isset($output)) ? $output : false;
 	try {
 		$database = new Dibi\Connection([
 			'driver' => 'sqlite3',
@@ -71,7 +122,7 @@ function login($array)
 					}
 					break;
 				default:
-					return 'error';
+					return ($output) ? 'No oAuthType defined' : 'error';
 					break;
 			}
 			$result = ($authSuccess) ? $database->fetch('SELECT * FROM users WHERE username = ? COLLATE NOCASE OR email = ? COLLATE NOCASE', $authSuccess['username'], $authSuccess['email']) : '';
@@ -124,12 +175,13 @@ function login($array)
 				}
 				// End 2FA
 				// authentication passed - 1) mark active and update token
-				if (createToken($result['username'], $result['email'], $result['image'], $result['group'], $result['group_id'], $GLOBALS['organizrHash'], $days)) {
+				$createToken = createToken($result['username'], $result['email'], $result['image'], $result['group'], $result['group_id'], $GLOBALS['organizrHash'], $days);
+				if ($createToken) {
 					writeLoginLog($username, 'success');
 					writeLog('success', 'Login Function - A User has logged in', $username);
 					$ssoUser = (empty($result['email'])) ? $result['username'] : (strpos($result['email'], 'placeholder') !== false) ? $result['username'] : $result['email'];
 					ssoCheck($ssoUser, $password, $token); //need to work on this
-					return true;
+					return ($output) ? array('name' => $GLOBALS['cookieName'], 'token' => (string)$createToken) : true;
 				} else {
 					return 'Token Creation Error';
 				}
@@ -1182,4 +1234,4 @@ function getSchema()
 	} else {
 		return 'DB not set yet...';
 	}
-}
+}

+ 307 - 35
api/functions/homepage-connect-functions.php

@@ -51,6 +51,9 @@ function homepageConnect($array)
 		case 'getRequests':
 			return getOmbiRequests($GLOBALS['ombiLimit']);
 			break;
+		case 'getHealthChecks':
+			return (qualifyRequest($GLOBALS['homepageHealthChecksAuth'])) ? getHealthChecks($array['data']['tags']) : false;
+			break;
 		default:
 			# code...
 			break;
@@ -58,6 +61,52 @@ function homepageConnect($array)
 	return false;
 }
 
+function healthChecksTags($tags)
+{
+	$return = 'tag=';
+	if (!$tags) {
+		return '';
+	} elseif ($tags == '*') {
+		return '';
+	} else {
+		if (strpos($tags, ',') !== false) {
+			$list = explode(',', $tags);
+			return $return . implode("&tag=", $list);
+		} else {
+			return $return . $tags;
+		}
+	}
+}
+
+function getHealthChecks($tags = null)
+{
+	if ($GLOBALS['homepageHealthChecksEnabled'] && !empty($GLOBALS['healthChecksToken']) && !empty($GLOBALS['healthChecksURL']) && qualifyRequest($GLOBALS['homepageHealthChecksAuth'])) {
+		$api['content']['checks'] = array();
+		$tags = ($tags) ? healthChecksTags($tags) : '';
+		$healthChecks = explode(',', $GLOBALS['healthChecksToken']);
+		foreach ($healthChecks as $token) {
+			$url = qualifyURL($GLOBALS['healthChecksURL']) . '/' . $tags;
+			try {
+				$headers = array('X-Api-Key' => $token);
+				$options = (localURL($url)) ? array('verify' => false) : array();
+				$response = Requests::get($url, $headers, $options);
+				if ($response->success) {
+					$healthResults = json_decode($response->body, true);
+					$api['content']['checks'] = array_merge($api['content']['checks'], $healthResults['checks']);
+				}
+			} catch (Requests_Exception $e) {
+				writeLog('error', 'HealthChecks Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
+			};
+		}
+		usort($api['content']['checks'], function ($a, $b) {
+			return $a['status'] <=> $b['status'];
+		});
+		$api['content']['checks'] = isset($api['content']['checks']) ? $api['content']['checks'] : false;
+		return $api;
+	}
+	return false;
+}
+
 function streamType($value)
 {
 	if ($value == "transcode" || $value == "Transcode") {
@@ -216,14 +265,14 @@ function resolveEmbyItem($itemDetails)
 		'audioChannels' => @$itemDetails['TranscodingInfo']['AudioChannels']
 	);
 	// Genre catch all
-	if ($item['Genres']) {
+	if (isset($item['Genres'])) {
 		$genres = array();
 		foreach ($item['Genres'] as $genre) {
 			$genres[] = $genre;
 		}
 	}
 	// Actor catch all
-	if ($item['People']) {
+	if (isset($item['People'])) {
 		$actors = array();
 		foreach ($item['People'] as $key => $value) {
 			if (@$value['PrimaryImageTag'] && @$value['Role']) {
@@ -251,8 +300,8 @@ function resolveEmbyItem($itemDetails)
 		'year' => (string)isset($item['ProductionYear']) ? $item['ProductionYear'] : '',
 		//'studio' => (string)$item['studio'],
 		'tagline' => @(string)$item['Taglines'][0],
-		'genres' => ($item['Genres']) ? $genres : '',
-		'actors' => ($item['People']) ? $actors : ''
+		'genres' => (isset($item['Genres'])) ? $genres : '',
+		'actors' => (isset($item['People'])) ? $actors : ''
 	);
 	if (file_exists($cacheDirectory . $embyItem['nowPlayingKey'] . '.jpg')) {
 		$embyItem['nowPlayingImageURL'] = $cacheDirectoryWeb . $embyItem['nowPlayingKey'] . '.jpg';
@@ -1043,7 +1092,11 @@ function getCalendar()
 					$sonarr = new Kryptonit3\Sonarr\Sonarr($value['url'], $value['token']);
 					$sonarr = $sonarr->getCalendar($startDate, $endDate, $GLOBALS['sonarrUnmonitored']);
 					$result = json_decode($sonarr, true);
-					$sonarrCalendar = (array_key_exists('error', $result)) ? '' : getSonarrCalendar($sonarr, $key);;
+					if (is_array($result) || is_object($result)) {
+						$sonarrCalendar = (array_key_exists('error', $result)) ? '' : getSonarrCalendar($sonarr, $key);
+					} else {
+						$sonarrCalendar = '';
+					}
 				} catch (Exception $e) {
 					writeLog('error', 'Sonarr Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
 				}
@@ -1070,7 +1123,11 @@ function getCalendar()
 					$lidarr = new Kryptonit3\Sonarr\Sonarr($value['url'], $value['token'], true);
 					$lidarr = $lidarr->getCalendar($startDate, $endDate);
 					$result = json_decode($lidarr, true);
-					$lidarrCalendar = (array_key_exists('error', $result)) ? '' : getLidarrCalendar($lidarr, $key);;
+					if (is_array($result) || is_object($result)) {
+						$lidarrCalendar = (array_key_exists('error', $result)) ? '' : getLidarrCalendar($lidarr, $key);
+					} else {
+						$lidarrCalendar = '';
+					}
 				} catch (Exception $e) {
 					writeLog('error', 'Lidarr Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
 				}
@@ -1097,7 +1154,11 @@ function getCalendar()
 					$radarr = new Kryptonit3\Sonarr\Sonarr($value['url'], $value['token']);
 					$radarr = $radarr->getCalendar($startDate, $endDate);
 					$result = json_decode($radarr, true);
-					$radarrCalendar = (array_key_exists('error', $result)) ? '' : getRadarrCalendar($radarr, $key, $value['url']);
+					if (is_array($result) || is_object($result)) {
+						$radarrCalendar = (array_key_exists('error', $result)) ? '' : getRadarrCalendar($radarr, $key, $value['url']);
+					} else {
+						$radarrCalendar = '';
+					}
 				} catch (Exception $e) {
 					writeLog('error', 'Radarr Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
 				}
@@ -1172,33 +1233,102 @@ function getCalendar()
 				$timeZone = isset($icsEvents [1] ['X-WR-TIMEZONE']) ? trim($icsEvents[1]['X-WR-TIMEZONE']) : date_default_timezone_get();
 				unset($icsEvents [1]);
 				foreach ($icsEvents as $icsEvent) {
-					if ((isset($icsEvent['DTSTART']) || isset($icsEvent['DTSTART;VALUE=DATE'])) && (isset($icsEvent['DTEND']) || isset($icsEvent['DTEND;VALUE=DATE'])) && isset($icsEvent['SUMMARY'])) {
+					$startKeys = array_filter_key($icsEvent, function ($key) {
+						return strpos($key, 'DTSTART') === 0;
+					});
+					$endKeys = array_filter_key($icsEvent, function ($key) {
+						return strpos($key, 'DTEND') === 0;
+					});
+					if (!empty($startKeys) && !empty($endKeys) && isset($icsEvent['SUMMARY'])) {
 						/* Getting start date and time */
-						$start = isset($icsEvent ['DTSTART;VALUE=DATE']) ? $icsEvent ['DTSTART;VALUE=DATE'] : $icsEvent ['DTSTART'];
-						/* Converting to datetime and apply the timezone to get proper date time */
-						$startDt = new DateTime ($start);
-						$startDt->setTimeZone(new DateTimezone ($timeZone));
-						$startDate = $startDt->format(DateTime::ATOM);
-						/* Getting end date with time */
-						$end = isset($icsEvent ['DTEND;VALUE=DATE']) ? $icsEvent ['DTEND;VALUE=DATE'] : $icsEvent ['DTEND'];
-						$endDt = new DateTime ($end);
-						$endDate = $endDt->format(DateTime::ATOM);
-						if (new DateTime() < $endDt) {
-							$extraClass = 'text-info';
+						$repeat = isset($icsEvent ['RRULE']) ? $icsEvent ['RRULE'] : false;
+						$start = reset($startKeys);
+						$end = reset($endKeys);
+						$totalDays = $GLOBALS['calendarStart'] + $GLOBALS['calendarEnd'];
+						if ($repeat) {
+							$repeatOverride = getCalenderRepeatCount(trim($icsEvent["RRULE"]));
+							switch (trim(strtolower(getCalenderRepeat($repeat)))) {
+								case 'daily':
+									$repeat = ($repeatOverride) ? $repeatOverride : $totalDays;
+									$term = 'days';
+									break;
+								case 'weekly':
+									$repeat = ($repeatOverride) ? $repeatOverride : round($totalDays / 7);
+									$term = 'weeks';
+									break;
+								case 'monthly':
+									$repeat = ($repeatOverride) ? $repeatOverride : round($totalDays / 30);
+									$term = 'months';
+									break;
+								case 'yearly':
+									$repeat = ($repeatOverride) ? $repeatOverride : round($totalDays / 365);
+									$term = 'years';
+									break;
+								default:
+									$repeat = ($repeatOverride) ? $repeatOverride : $totalDays;
+									$term = 'days';
+									break;
+							}
 						} else {
-							$extraClass = 'text-success';
+							$repeat = 1;
+							$term = 'day';
+						}
+						$calendarTimes = 0;
+						while ($calendarTimes < $repeat) {
+							$currentDate = new DateTime ($GLOBALS['currentTime']);
+							$oldestDay = new DateTime ($GLOBALS['currentTime']);
+							$oldestDay->modify('-' . $GLOBALS['calendarStart'] . ' days');
+							$newestDay = new DateTime ($GLOBALS['currentTime']);
+							$newestDay->modify('+' . $GLOBALS['calendarEnd'] . ' days');
+							/* Converting to datetime and apply the timezone to get proper date time */
+							$startDt = new DateTime ($start);
+							/* Getting end date with time */
+							$endDt = new DateTime ($end);
+							if ($calendarTimes !== 0) {
+								$dateDiff = date_diff($startDt, $currentDate);
+								$startDt->modify($dateDiff->format('%R') . (round(($dateDiff->days) / 7)) . ' weeks');
+								$startDt->modify('+' . $calendarTimes . ' ' . $term);
+								$endDt->modify($dateDiff->format('%R') . (round(($dateDiff->days) / 7)) . ' weeks');
+								$endDt->modify('+' . $calendarTimes . ' ' . $term);
+							} elseif ($calendarTimes == 0 && $repeat !== 1) {
+								$dateDiff = date_diff($startDt, $currentDate);
+								$startDt->modify($dateDiff->format('%R') . (round(($dateDiff->days) / 7)) . ' weeks');
+								$endDt->modify($dateDiff->format('%R') . (round(($dateDiff->days) / 7)) . ' weeks');
+							}
+							$calendarStartDiff = date_diff($startDt, $newestDay);
+							$calendarEndDiff = date_diff($startDt, $oldestDay);
+							$startDt->setTimeZone(new DateTimezone ($timeZone));
+							$endDt->setTimeZone(new DateTimezone ($timeZone));
+							$startDate = $startDt->format(DateTime::ATOM);
+							$endDate = $endDt->format(DateTime::ATOM);
+							if (new DateTime() < $endDt) {
+								$extraClass = 'text-info';
+							} else {
+								$extraClass = 'text-success';
+							}
+							/* Getting the name of event */
+							$eventName = $icsEvent['SUMMARY'];
+							if (!calendarDaysCheck($calendarStartDiff->format('%R') . $calendarStartDiff->days, $calendarEndDiff->format('%R') . $calendarEndDiff->days)) {
+								break;
+							}
+							if (isset($icsEvent["RRULE"]) && getCalenderRepeatUntil(trim($icsEvent["RRULE"]))) {
+								$untilDate = new DateTime (getCalenderRepeatUntil(trim($icsEvent["RRULE"])));
+								$untilDiff = date_diff($currentDate, $untilDate);
+								if ($untilDiff->days > 0) {
+									break;
+								}
+							}
+							$icalEvents[] = array(
+								'title' => $eventName,
+								'imagetype' => 'calendar-o text-warning text-custom-calendar ' . $extraClass,
+								'imagetypeFilter' => 'ical',
+								'className' => 'bg-calendar calendar-item bg-custom-calendar',
+								'start' => $startDate,
+								'end' => $endDate,
+								'bgColor' => str_replace('text', 'bg', $extraClass),
+							);
+							$calendarTimes = $calendarTimes + 1;
 						}
-						/* Getting the name of event */
-						$eventName = $icsEvent['SUMMARY'];
-						$icalEvents[] = array(
-							'title' => $eventName,
-							'imagetype' => 'calendar-o text-warning text-custom-calendar ' . $extraClass,
-							'imagetypeFilter' => 'ical',
-							'className' => 'bg-calendar calendar-item bg-custom-calendar',
-							'start' => $startDate,
-							'end' => $endDate,
-							'bgColor' => str_replace('text', 'bg', $extraClass),
-						);
 					}
 				}
 			}
@@ -1209,6 +1339,54 @@ function getCalendar()
 	return ($calendarSources) ? $calendarSources : false;
 }
 
+function calendarDaysCheck($entryStart, $entryEnd)
+{
+	$success = false;
+	$entryStart = intval($entryStart);
+	$entryEnd = intval($entryEnd);
+	if ($entryStart >= 0 && $entryEnd <= 0) {
+		$success = true;
+	}
+	return $success;
+}
+
+function getCalenderRepeat($value)
+{
+	//FREQ=DAILY
+	//RRULE:FREQ=WEEKLY;BYDAY=TH
+	$first = explode('=', $value);
+	if (count($first) > 1) {
+		$second = explode(';', $first[1]);
+	} else {
+		return $value;
+	}
+	if ($second) {
+		return $second[0];
+	} else {
+		return $first[1];
+	}
+}
+
+function getCalenderRepeatUntil($value)
+{
+	$first = explode('UNTIL=', $value);
+	if (count($first) > 1) {
+		return $first[1];
+	} else {
+		return false;
+	}
+}
+
+function getCalenderRepeatCount($value)
+{
+	$first = explode('COUNT=', $value);
+	if (count($first) > 1) {
+		return $first[1];
+	} else {
+		return false;
+	}
+}
+
 function getSonarrCalendar($array, $number)
 {
 	$array = json_decode($array, true);
@@ -1319,7 +1497,15 @@ function getLidarrCalendar($array, $number)
 		if (new DateTime() < new DateTime($releaseDate)) {
 			$unaired = true;
 		}
-		$downloaded = (isset($child['statistics']['percentOfEpisodes']) && $child['statistics']['percentOfEpisodes'] !== '100.0') ? '0' : '1';
+		if (isset($child['statistics']['percentOfTracks'])) {
+			if ($child['statistics']['percentOfTracks'] == '100.0') {
+				$downloaded = '1';
+			} else {
+				$downloaded = '0';
+			}
+		} else {
+			$downloaded = '0';
+		}
 		if ($downloaded == "0" && isset($unaired)) {
 			$downloaded = "text-info";
 		} elseif ($downloaded == "1") {
@@ -1358,7 +1544,8 @@ function getLidarrCalendar($array, $number)
 			"imagetypeFilter" => "music",
 			"downloadFilter" => $downloaded,
 			"bgColor" => str_replace('text', 'bg', $downloaded),
-			"details" => $details
+			"details" => $details,
+			"data" => $child
 		));
 	}
 	if ($i != 0) {
@@ -2271,6 +2458,90 @@ function testAPIConnection($array)
 				};
 			}
 			break;
+		case 'ldap_login':
+			$username = $array['data']['data']['username'];
+			$password = $array['data']['data']['password'];
+			if (empty($username) || empty($password)) {
+				return 'Missing Username or Password';
+			}
+			if (!empty($GLOBALS['authBaseDN']) && !empty($GLOBALS['authBackendHost'])) {
+				$ad = new \Adldap\Adldap();
+				// Create a configuration array.
+				$ldapServers = explode(',', $GLOBALS['authBackendHost']);
+				$i = 0;
+				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
+					$ldapHosts[] = $host;
+					$ldapServersNew[$key] = $scheme . '://' . $host . ':' . $port; // May use this later
+					if ($i == 0) {
+						$ldapPort = $port;
+					}
+					$i++;
+				}
+				$config = [
+					// Mandatory Configuration Options
+					'hosts' => $ldapHosts,
+					'base_dn' => $GLOBALS['authBaseDN'],
+					'username' => (empty($GLOBALS['ldapBindUsername'])) ? null : $GLOBALS['ldapBindUsername'],
+					'password' => (empty($GLOBALS['ldapBindPassword'])) ? null : decrypt($GLOBALS['ldapBindPassword']),
+					// Optional Configuration Options
+					'schema' => (($GLOBALS['ldapType'] == '1') ? Adldap\Schemas\ActiveDirectory::class : (($GLOBALS['ldapType'] == '2') ? Adldap\Schemas\OpenLDAP::class : Adldap\Schemas\FreeIPA::class)),
+					'account_prefix' => (empty($GLOBALS['authBackendHostPrefix'])) ? null : $GLOBALS['authBackendHostPrefix'],
+					'account_suffix' => (empty($GLOBALS['authBackendHostSuffix'])) ? null : $GLOBALS['authBackendHostSuffix'],
+					'port' => $ldapPort,
+					'follow_referrals' => false,
+					'use_ssl' => false,
+					'use_tls' => false,
+					'version' => 3,
+					'timeout' => 5,
+					// Custom LDAP Options
+					'custom_options' => [
+						// See: http://php.net/ldap_set_option
+						//LDAP_OPT_X_TLS_REQUIRE_CERT => LDAP_OPT_X_TLS_HARD
+					]
+				];
+				// Add a connection provider to Adldap.
+				$ad->addProvider($config);
+				try {
+					// If a successful connection is made to your server, the provider will be returned.
+					$provider = $ad->connect();
+					//prettyPrint($provider);
+					if ($provider->auth()->attempt($username, $password, true)) {
+						// Passed.
+						$user = $provider->search()->find($username);
+						//return $user->getFirstAttribute('cn');
+						//return $user->getGroups(['cn']);
+						//return $user;
+						//return $user->getUserPrincipalName();
+						//return $user->getGroups(['cn']);
+						return true;
+					} else {
+						// Failed.
+						return 'Username/Password Failed to authenticate';
+					}
+				} catch (\Adldap\Auth\BindException $e) {
+					$detailedError = $e->getDetailedError();
+					writeLog('error', 'LDAP Function - Error: ' . $detailedError->getErrorMessage(), $username);
+					return $detailedError->getErrorMessage();
+					// There was an issue binding / connecting to the server.
+				} catch (Adldap\Auth\UsernameRequiredException $e) {
+					$detailedError = $e->getDetailedError();
+					writeLog('error', 'LDAP Function - Error: ' . $detailedError->getErrorMessage(), $username);
+					return $detailedError->getErrorMessage();
+					// The user didn't supply a username.
+				} catch (Adldap\Auth\PasswordRequiredException $e) {
+					$detailedError = $e->getDetailedError();
+					writeLog('error', 'LDAP Function - Error: ' . $detailedError->getErrorMessage(), $username);
+					return $detailedError->getErrorMessage();
+					// The user didn't supply a password.
+				}
+			}
+			break;
 		case 'ldap':
 			if (!empty($GLOBALS['authBaseDN']) && !empty($GLOBALS['authBackendHost'])) {
 				$ad = new \Adldap\Adldap();
@@ -2318,8 +2589,9 @@ function testAPIConnection($array)
 					// If a successful connection is made to your server, the provider will be returned.
 					$provider = $ad->connect();
 				} catch (\Adldap\Auth\BindException $e) {
-					writeLog('error', 'LDAP Function - Error: ' . $e->getMessage(), 'SYSTEM');
-					return $e->getMessage();
+					$detailedError = $e->getDetailedError();
+					writeLog('error', 'LDAP Function - Error: ' . $detailedError->getErrorMessage(), 'SYSTEM');
+					return $detailedError->getErrorMessage();
 					// There was an issue binding / connecting to the server.
 				}
 				return ($provider) ? true : false;

+ 76 - 0
api/functions/homepage-functions.php

@@ -19,6 +19,7 @@ function homepageOrder()
 		"homepageOrderdeluge" => $GLOBALS['homepageOrderdeluge'],
 		"homepageOrderrTorrent" => $GLOBALS['homepageOrderrTorrent'],
 		"homepageOrderdownloader" => $GLOBALS['homepageOrderdownloader'],
+		"homepageOrderhealthchecks" => $GLOBALS['homepageOrderhealthchecks'],
 	);
 	asort($homepageOrder);
 	return $homepageOrder;
@@ -280,6 +281,18 @@ function buildHomepageItem($homepageItem)
 				';
 			}
 			break;
+		case 'homepageOrderhealthchecks':
+			if ($GLOBALS['homepageHealthChecksEnabled'] && qualifyRequest($GLOBALS['homepageHealthChecksAuth'])) {
+				$item .= '<div class="white-box"><h2 class="text-center" lang="en">Loading Health Checks...</h2></div>';
+				$item .= '
+				<script>
+				// Health Checks
+				homepageHealthChecks("' . $GLOBALS['healthChecksTags'] . '","' . $GLOBALS['homepageHealthChecksRefresh'] . '");
+				// End Health Checks
+				</script>
+				';
+			}
+			break;
 		default:
 			# code...
 			break;
@@ -2045,6 +2058,62 @@ function getHomepageList()
 				)
 			)
 		),
+		array(
+			'name' => 'HealthChecks',
+			'enabled' => true,
+			'image' => 'plugins/images/tabs/healthchecks.png',
+			'category' => 'Monitor',
+			'settings' => array(
+				'Enable' => array(
+					array(
+						'type' => 'switch',
+						'name' => 'homepageHealthChecksEnabled',
+						'label' => 'Enable',
+						'value' => $GLOBALS['homepageHealthChecksEnabled']
+					),
+					array(
+						'type' => 'select',
+						'name' => 'homepageHealthChecksAuth',
+						'label' => 'Minimum Authentication',
+						'value' => $GLOBALS['homepageHealthChecksAuth'],
+						'options' => $groups
+					)
+				),
+				'Connection' => array(
+					array(
+						'type' => 'input',
+						'name' => 'healthChecksURL',
+						'label' => 'URL',
+						'value' => $GLOBALS['healthChecksURL'],
+						'help' => 'URL for HealthChecks API',
+						'placeholder' => 'HealthChecks API URL'
+					),
+					array(
+						'type' => 'password-alt',
+						'name' => 'healthChecksToken',
+						'label' => 'Token',
+						'value' => $GLOBALS['healthChecksToken']
+					)
+				),
+				'Misc Options' => array(
+					array(
+						'type' => 'input',
+						'name' => 'healthChecksTags',
+						'label' => 'Tags',
+						'value' => $GLOBALS['healthChecksTags'],
+						'help' => 'Pull only checks with this tag - Blank for all',
+						'placeholder' => 'Multiple tags using CSV - tag1,tag2'
+					),
+					array(
+						'type' => 'select',
+						'name' => 'homepageHealthChecksRefresh',
+						'label' => 'Refresh Seconds',
+						'value' => $GLOBALS['homepageHealthChecksRefresh'],
+						'options' => optionTime()
+					),
+				),
+			)
+		),
 		array(
 			'name' => 'CustomHTML-1',
 			'enabled' => (strpos('personal,business', $GLOBALS['license']) !== false) ? true : false,
@@ -2225,6 +2294,13 @@ function buildHomepageSettings()
 					$class .= ' faded';
 				}
 				break;
+			case 'homepageOrderhealthchecks':
+				$class = 'bg-healthchecks';
+				$image = 'plugins/images/tabs/healthchecks.png';
+				if (!$GLOBALS['homepageHealthChecksEnabled']) {
+					$class .= ' faded';
+				}
+				break;
 			default:
 				$class = 'blue-bg';
 				$image = '';

+ 17 - 2
api/functions/log-functions.php

@@ -1,7 +1,20 @@
 <?php
+function checkLog($path)
+{
+	if (file_exists($path)) {
+		if (filesize($path) > 500000) {
+			rename($path, $path . '[' . date('Y-m-d') . '].json');
+			return false;
+		}
+		return true;
+	} else {
+		return false;
+	}
+}
+
 function writeLoginLog($username, $authType)
 {
-	if (file_exists($GLOBALS['organizrLoginLog'])) {
+	if (checkLog($GLOBALS['organizrLoginLog'])) {
 		$getLog = str_replace("\r\ndate", "date", file_get_contents($GLOBALS['organizrLoginLog']));
 		$gotLog = json_decode($getLog, true);
 	}
@@ -18,8 +31,10 @@ function writeLoginLog($username, $authType)
 
 function writeLog($type = 'error', $message, $username = null)
 {
+	$GLOBALS['timeExecution'] = timeExecution($GLOBALS['timeExecution']);
+	$message = $message . ' [Execution Time: ' . formatSeconds($GLOBALS['timeExecution']) . ']';
 	$username = ($username) ? $username : $GLOBALS['organizrUser']['username'];
-	if (file_exists($GLOBALS['organizrLog'])) {
+	if (checkLog($GLOBALS['organizrLog'])) {
 		$getLog = str_replace("\r\ndate", "date", file_get_contents($GLOBALS['organizrLog']));
 		$gotLog = json_decode($getLog, true);
 	}

+ 77 - 9
api/functions/normal-functions.php

@@ -82,7 +82,7 @@ function coookie($type, $name, $value = '', $days = -1, $http = true)
 	if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == "https") {
 		$Secure = true;
 		$HTTPOnly = true;
-	} elseif (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') {
+	} elseif (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' && $_SERVER['HTTPS'] !== '') {
 		$Secure = true;
 		$HTTPOnly = true;
 	} else {
@@ -392,14 +392,10 @@ function qualifyURL($url, $return = false)
 		$url = $protocol . getServer() . $url;
 	}
 	// Get Digest
-	$digest = parse_url($url);
+	$digest = parse_url(rtrim(preg_replace('/\s+/', '', $url), '/'));
 	// http/https
 	if (!isset($digest['scheme'])) {
-		if (isset($digest['port']) && in_array($digest['port'], array(80, 8080, 8096, 32400, 7878, 8989, 8182, 8081, 6789))) {
-			$scheme = 'http';
-		} else {
-			$scheme = 'https';
-		}
+		$scheme = 'http';
 	} else {
 		$scheme = $digest['scheme'];
 	}
@@ -408,7 +404,7 @@ function qualifyURL($url, $return = false)
 	// Port
 	$port = (isset($digest['port']) ? ':' . $digest['port'] : '');
 	// Path
-	$path = (isset($digest['path']) && $digest['path'] !== '/' ? $digest['path'] : '');
+	$path = (isset($digest['path']) ? $digest['path'] : '');
 	// Output
 	$array = array(
 		'scheme' => $scheme,
@@ -587,7 +583,7 @@ function dbExtension($string)
 
 function localIPRanges()
 {
-	return array(
+	$mainArray = array(
 		array(
 			'from' => '10.0.0.0',
 			'to' => '10.255.255.255'
@@ -605,6 +601,22 @@ function localIPRanges()
 			'to' => '127.255.255.255'
 		),
 	);
+	$override = false;
+	if ($GLOBALS['localIPFrom']) {
+		$from = trim($GLOBALS['localIPFrom']);
+		$override = true;
+	}
+	if ($GLOBALS['localIPTo']) {
+		$to = trim($GLOBALS['localIPTo']);
+	}
+	if ($override) {
+		$newArray = array(
+			'from' => $from,
+			'to' => (isset($to)) ? $to : $from
+		);
+		array_push($mainArray, $newArray);
+	}
+	return $mainArray;
 }
 
 function isLocal($checkIP = null)
@@ -629,4 +641,60 @@ function checkOverrideURL($url, $override)
 	} else {
 		return $url . $override;
 	}
+}
+
+function clearPOSTPassword($array)
+{
+	if (isset($array['data'])) {
+		foreach ($array['data'] as $k => $v) {
+			// clear password from array
+			if ($k == 'password') {
+				$array['data'][$k] = '*******';
+			}
+		}
+	}
+	if (isset($array['data']['data'])) {
+		foreach ($array['data']['data'] as $k => $v) {
+			// clear password from array
+			if ($k == 'password') {
+				$array['data']['data'][$k] = '*******';
+			}
+		}
+	}
+	return $array;
+}
+
+function timeExecution($previous = null)
+{
+	if (!$previous) {
+		return microtime(true) - $_SERVER["REQUEST_TIME_FLOAT"];
+	} else {
+		return (microtime(true) - $_SERVER["REQUEST_TIME_FLOAT"]) - $previous;
+	}
+}
+
+function formatSeconds($seconds)
+{
+	$hours = 0;
+	$milliseconds = str_replace("0.", '', $seconds - floor($seconds));
+	if ($seconds > 3600) {
+		$hours = floor($seconds / 3600);
+	}
+	$seconds = $seconds % 3600;
+	$time = str_pad($hours, 2, '0', STR_PAD_LEFT)
+		. gmdate(':i:s', $seconds)
+		. ($milliseconds ? '.' . $milliseconds : '');
+	$parts = explode(':', $time);
+	$timeExtra = explode('.', $parts[2]);
+	if ($parts[0] !== '00') { // hours
+		return $time;
+	} elseif ($parts[1] !== '00') { // mins
+		return $parts[1] . 'min(s) ' . $timeExtra[0] . 's';
+	} elseif ($timeExtra[0] !== '00') { // secs
+		return substr($parts[2], 0, 5) . 's | ' . substr($parts[2], 0, 7) * 1000 . 'ms';
+	} else {
+		return substr($parts[2], 0, 7) * 1000 . 'ms';
+	}
+	//return $timeExtra[0] . 's ' . (number_format(('0.' . substr($timeExtra[1], 0, 4)), 4, '.', '') * 1000) . 'ms';
+	//return (number_format(('0.' . substr($timeExtra[1], 0, 4)), 4, '.', '') * 1000) . 'ms';
 }

+ 210 - 4
api/functions/organizr-functions.php

@@ -25,6 +25,7 @@ function organizrSpecialSettings()
 			),
 			'options' => array(
 				'alternateHomepageHeaders' => $GLOBALS['alternateHomepageHeaders'],
+				'healthChecksTags' => $GLOBALS['healthChecksTags'],
 			)
 		),
 		'sso' => array(
@@ -98,7 +99,9 @@ function organizrSpecialSettings()
 			'docker' => qualifyRequest(1) ? $GLOBALS['docker'] : '',
 			'githubCommit' => qualifyRequest(1) ? $GLOBALS['commit'] : '',
 			'schema' => qualifyRequest(1) ? getSchema() : '',
-			'debugArea' => qualifyRequest($GLOBALS['debugAreaAuth'])
+			'debugArea' => qualifyRequest($GLOBALS['debugAreaAuth']),
+			'debugErrors' => $GLOBALS['debugErrors'],
+			'sandbox' => $GLOBALS['sandbox'],
 		)
 	);
 }
@@ -655,6 +658,15 @@ function getSettingsMain()
 				'attr' => 'onclick="testAPIConnection(\'ldap\')"',
 				'help' => 'Remember! Please save before using the test button!'
 			),
+			array(
+				'type' => 'button',
+				'name' => 'test-button-ldap-login',
+				'label' => 'Test Login',
+				'icon' => 'fa fa-flask',
+				'class' => 'ldapAuth switchAuth',
+				'text' => 'Test Login',
+				'attr' => 'onclick="showLDAPLoginTest()"'
+			),
 			array(
 				'type' => 'input',
 				'name' => 'embyURL',
@@ -722,6 +734,48 @@ function getSettingsMain()
 				'help' => 'Important! Do not keep this enabled for too long as this opens up Authentication while testing.',
 				'value' => $GLOBALS['authDebug'],
 				'class' => 'authDebug'
+			),
+			array(
+				'type' => 'select2',
+				'class' => 'select2-multiple',
+				'id' => 'sandbox-select',
+				'name' => 'sandbox',
+				'label' => 'iFrame Sandbox',
+				'value' => $GLOBALS['sandbox'],
+				'help' => 'WARNING! This can potentially mess up your iFrames',
+				'options' => array(
+					array(
+						'name' => 'Allow Presentation',
+						'value' => 'allow-presentation'
+					),
+					array(
+						'name' => 'Allow Forms',
+						'value' => 'allow-forms'
+					),
+					array(
+						'name' => 'Allow Same Origin',
+						'value' => 'allow-same-origin'
+					),
+					array(
+						'name' => 'Allow Pointer Lock',
+						'value' => 'allow-pointer-lock'
+					),
+					array(
+						'name' => 'Allow Scripts',
+						'value' => 'allow-scripts'
+					), array(
+						'name' => 'Allow Popups',
+						'value' => 'allow-popups'
+					),
+					array(
+						'name' => 'Allow Modals',
+						'value' => 'allow-modals'
+					),
+					array(
+						'name' => 'Allow Top Navigation',
+						'value' => 'allow-top-navigation'
+					),
+				)
 			)
 		),
 		'Login' => array(
@@ -755,6 +809,22 @@ function getSettingsMain()
 				'help' => 'Default status of Remember Me button on login screen',
 				'value' => $GLOBALS['rememberMe'],
 			),
+			array(
+				'type' => 'input',
+				'name' => 'localIPFrom',
+				'label' => 'Override Local IP From',
+				'value' => $GLOBALS['localIPFrom'],
+				'placeholder' => 'i.e. 123.123.123.123',
+				'help' => 'IPv4 only at the moment - This will set your login as local if your IP falls within the From and To'
+			),
+			array(
+				'type' => 'input',
+				'name' => 'localIPTo',
+				'label' => 'Override Local IP To',
+				'value' => $GLOBALS['localIPTo'],
+				'placeholder' => 'i.e. 123.123.123.123',
+				'help' => 'IPv4 only at the moment - This will set your login as local if your IP falls within the From and To'
+			),
 		),
 		'Ping' => array(
 			array(
@@ -992,8 +1062,16 @@ function getCustomizeAppearance()
 					'type' => 'switch',
 					'name' => 'useLogo',
 					'label' => 'Use Logo instead of Title',
-					'value' => $GLOBALS['useLogo']
-				)
+					'value' => $GLOBALS['useLogo'],
+					'help' => 'Also sets the title of your site'
+				),
+				array(
+					'type' => 'input',
+					'name' => 'description',
+					'label' => 'Meta Description',
+					'value' => $GLOBALS['description'],
+					'help' => 'Used to set the description for SEO meta tags'
+				),
 			),
 			'Login Page' => array(
 				array(
@@ -1016,6 +1094,12 @@ function getCustomizeAppearance()
 					'label' => 'Alternate Homepage Titles',
 					'value' => $GLOBALS['alternateHomepageHeaders']
 				),
+				array(
+					'type' => 'switch',
+					'name' => 'debugErrors',
+					'label' => 'Show Debug Errors',
+					'value' => $GLOBALS['debugErrors']
+				),
 				array(
 					'type' => 'select',
 					'name' => 'unsortedTabs',
@@ -1463,6 +1547,7 @@ function auth()
 	}
 	if ($group !== null) {
 		if (qualifyRequest($group) && $unlocked) {
+			header("X-Organizr-User: $currentUser");
 			!$debug ? exit(http_response_code(200)) : die("$userInfo Authorized");
 		} else {
 			!$debug ? exit(http_response_code(401)) : die("$userInfo Not Authorized");
@@ -1995,6 +2080,11 @@ function plexJoinAPI($array)
 	return plexJoin($array['data']['username'], $array['data']['email'], $array['data']['password']);
 }
 
+function embyJoinAPI($array)
+{
+	return embyJoin($array['data']['username'], $array['data']['email'], $array['data']['password']);
+}
+
 function plexJoin($username, $email, $password)
 {
 	try {
@@ -2038,6 +2128,86 @@ function plexJoin($username, $email, $password)
 	return false;
 }
 
+function embyJoin($username, $email, $password)
+{
+	try {
+		#create user in emby.
+		$headers = array(
+			"Accept" => "application/json"
+		);
+		$data = array();
+		$url = $GLOBALS['embyURL'] . '/emby/Users/New?name=' . $username . '&api_key=' . $GLOBALS['embyToken'];
+		$response = Requests::Post($url, $headers, json_encode($data), array());
+		$response = $response->body;
+		//return($response);
+		$response = json_decode($response, true);
+		//return($response);
+		$userID = $response["Id"];
+		//return($userID);
+		#authenticate as user to update password.
+		//randomizer four digits of DeviceId
+		// I dont think ther would be security problems with hardcoding deviceID but randomizing it would mitigate any issue.
+		$deviceIdSeceret = rand(0, 9) . "" . rand(0, 9) . "" . rand(0, 9) . "" . rand(0, 9);
+		//hardcoded device id with the first three digits random 0-9,0-9,0-9,0-9
+		$embyAuthHeader = 'MediaBrowser Client="Emby Mobile", Device="Firefox", DeviceId="' . $deviceIdSeceret . 'aWxssS81LgAggFdpbmRvd3MgTlQgMTAuMDsgV2luNjxx7IHf2NCkgQXBwbGVXZWJLaXQvNTM3LjM2IChLSFRNTCwgbGlrZSBHZWNrbykgQ2hyb21lLzcyLjAuMzYyNi4xMTkgU2FmYXJpLzUzNy4zNnwxNTUxNTczMTAyNDI4", Version="4.0.2.0"';
+		$headers = array(
+			"Accept" => "application/json",
+			"Content-Type" => "application/json",
+			"X-Emby-Authorization" => $embyAuthHeader
+		);
+		$data = array(
+			"Pw" => "",
+			"Username" => $username
+		);
+		$url = $GLOBALS['embyURL'] . '/emby/Users/AuthenticateByName';
+		$response = Requests::Post($url, $headers, json_encode($data), array());
+		$response = $response->body;
+		$response = json_decode($response, true);
+		$userToken = $response["AccessToken"];
+		#update password
+		$embyAuthHeader = 'MediaBrowser Client="Emby Mobile", Device="Firefox", Token="' . $userToken . '", DeviceId="' . $deviceIdSeceret . 'aWxssS81LgAggFdpbmRvd3MgTlQgMTAuMDsgV2luNjxx7IHf2NCkgQXBwbGVXZWJLaXQvNTM3LjM2IChLSFRNTCwgbGlrZSBHZWNrbykgQ2hyb21lLzcyLjAuMzYyNi4xMTkgU2FmYXJpLzUzNy4zNnwxNTUxNTczMTAyNDI4", Version="4.0.2.0"';
+		$headers = array(
+			"Accept" => "application/json",
+			"Content-Type" => "application/json",
+			"X-Emby-Authorization" => $embyAuthHeader
+		);
+		$data = array(
+			"CurrentPw" => "",
+			"NewPw" => $password,
+			"Id" => $userID
+		);
+		$url = $GLOBALS['embyURL'] . '/emby/Users/' . $userID . '/Password';
+		Requests::Post($url, $headers, json_encode($data), array());
+		#update config
+		$headers = array(
+			"Accept" => "application/json",
+			"Content-Type" => "application/json"
+		);
+		$url = $GLOBALS['embyURL'] . '/emby/Users/' . $userID . '/Policy?api_key=' . $GLOBALS['embyToken'];
+		$response = Requests::Post($url, $headers, getEmbyTemplateUserJson(), array());
+		#add emby.media
+		try {
+			#seperate because this is not required
+			$headers = array(
+				"Accept" => "application/json",
+				"X-Emby-Authorization" => $embyAuthHeader
+			);
+			$data = array(
+				"ConnectUsername " => $email
+			);
+			$url = $GLOBALS['embyURL'] . '/emby/Users/' . $userID . '/Connect/Link';
+			Requests::Post($url, $headers, json_encode($data), array());
+		} catch (Requests_Exception $e) {
+			writeLog('error', 'Emby Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
+		}
+		return (true);
+		//return( "USERID:".$userID);
+	} catch (Requests_Exception $e) {
+		writeLog('error', 'Emby create Function - Error: ' . $e->getMessage(), 'SYSTEM');
+	};
+	return false;
+}
+
 function checkFrame($array, $url)
 {
 	if (array_key_exists("x-frame-options", $array)) {
@@ -2060,6 +2230,42 @@ function checkFrame($array, $url)
 	}
 }
 
+/*loads users from emby and returns a correctly formated policy for a new user.
+*/
+function getEmbyTemplateUserJson()
+{
+	$headers = array(
+		"Accept" => "application/json"
+	);
+	$data = array();
+	$url = $GLOBALS['embyURL'] . '/emby/Users?api_key=' . $GLOBALS['embyToken'];
+	$response = Requests::Get($url, $headers, array());
+	$response = $response->body;
+	$response = json_decode($response, true);
+	//error_Log("response ".json_encode($response));
+	writeLog('error', 'userList:' . json_encode($response), 'SYSTEM');
+	//$correct stores the template users object
+	$correct = null;
+	foreach ($response as $element) {
+		if ($element['Name'] == $GLOBALS['INVITES-EmbyTemplate']) {
+			$correct = $element;
+		}
+	}
+	writeLog('error', 'Correct user:' . json_encode($correct), 'SYSTEM');
+	if ($correct == null) {
+		//return empty JSON if user incorectly configured template
+		return "{}";
+	}
+	//select policy section and remove possibly dangeours rows.
+	$policy = $correct['Policy'];
+	//writeLog('error', 'policy update'.$policy, 'SYSTEM');
+	unset($policy['AuthenticationProviderId']);
+	unset($policy['InvalidLoginAttemptCount']);
+	unset($policy['DisablePremiumFeatures']);
+	unset($policy['DisablePremiumFeatures']);
+	return (json_encode($policy));
+}
+
 function frameTest($url)
 {
 	$array = array_change_key_case(get_headers(qualifyURL($url), 1));
@@ -2185,4 +2391,4 @@ function checkHostPrefix($s)
 		return $s;
 	}
 	return (substr($s, -1, 1) == '\\') ? $s : $s . '\\';
-}
+}

+ 4 - 4
api/functions/sso-functions.php

@@ -32,8 +32,8 @@ function getOmbiToken($username, $password, $oAuthToken = null)
 			"Content-Type" => "application/json"
 		);
 		$data = array(
-			"username" => $username,
-			"password" => $password,
+			"username" => ($oAuthToken ? "" : $username),
+			"password" => ($oAuthToken ? "" : $password),
 			"rememberMe" => "true",
 			"plexToken" => $oAuthToken
 		);
@@ -66,8 +66,8 @@ function getTautulliToken($username, $password, $plexToken = null)
 					"User-Agent" => isset($_SERVER ['HTTP_USER_AGENT']) ? $_SERVER ['HTTP_USER_AGENT'] : null
 				);
 				$data = array(
-					"username" => $username,
-					"password" => $password,
+			                "username" => ($plexToken ? "" : $username),
+			                "password" => ($plexToken ? "" : $password),
 					"token" => $plexToken,
 					"remember_me" => 1,
 				);

+ 3 - 1
api/functions/static-globals.php

@@ -1,7 +1,7 @@
 <?php
 // ===================================
 // Organizr Version
-$GLOBALS['installedVersion'] = '2.0.70';
+$GLOBALS['installedVersion'] = '2.0.180';
 // ===================================
 // Quick php Version check
 $GLOBALS['minimumPHP'] = '7.1.3';
@@ -20,6 +20,8 @@ if ($GLOBALS['docker']) {
 }
 $GLOBALS['fileHash'] = (isset($GLOBALS['quickCommit'])) ? $GLOBALS['quickCommit'] : $GLOBALS['installedVersion'];
 $GLOBALS['quickConfig'] = (file_exists($GLOBALS['userConfigPath'])) ? loadConfigOnce($GLOBALS['userConfigPath']) : null;
+$GLOBALS['organizrIndexTitle'] = (isset($GLOBALS['quickConfig']['title'])) ? $GLOBALS['quickConfig']['title'] : 'Organizr v2';
+$GLOBALS['organizrIndexDescription'] = (isset($GLOBALS['quickConfig']['description'])) ? $GLOBALS['quickConfig']['description'] : 'Organizr v2';
 // Quick function for plugins
 function pluginFiles($type)
 {

+ 30 - 6
api/index.php

@@ -1,5 +1,4 @@
 <?php
-$generationTime = -microtime(true);
 //include functions
 require_once 'functions.php';
 //Set result array
@@ -23,6 +22,7 @@ $approvedFunctionsBypass = array(
 	'v1_wizard_config',
 	'v1_login',
 	'v1_wizard_path',
+	'v1_login_api'
 );
 if (!in_array($function, $approvedFunctionsBypass)) {
 	if (isApprovedRequest($method) === false) {
@@ -650,6 +650,19 @@ switch ($function) {
 				break;
 		}
 		break;
+	case 'v1_login_api':
+		switch ($method) {
+			case 'POST':
+				$result['status'] = 'success';
+				$result['statusText'] = 'success';
+				$result['data'] = apiLogin();
+				break;
+			default:
+				$result['status'] = 'error';
+				$result['statusText'] = 'The function requested is not defined for method: ' . $method;
+				break;
+		}
+		break;
 	case 'v1_register':
 		switch ($method) {
 			case 'POST':
@@ -1144,8 +1157,8 @@ switch ($function) {
 				auth();
 				break;
 			default:
-				$result['status'] = 'error';
-				$result['statusText'] = 'The function requested is not defined for method: ' . $method;
+				//exit(http_response_code(401));
+				auth();
 				break;
 		}
 		break;
@@ -1233,6 +1246,19 @@ switch ($function) {
 				break;
 		}
 		break;
+	case 'v1_emby_join':
+		switch ($method) {
+			case 'POST':
+				$result['status'] = 'success';
+				$result['statusText'] = 'success';
+				$result['data'] = embyJoinAPI($_POST);
+				break;
+			default:
+				$result['status'] = 'error';
+				$result['statusText'] = 'The function requested is not defined for method: ' . $method;
+				break;
+		}
+		break;
 	case 'v1_token_revoke':
 		switch ($method) {
 			case 'POST':
@@ -1341,12 +1367,10 @@ if (!$result) {
 	$result['error'] = "An error has occurred";
 }
 $result['generationDate'] = $GLOBALS['currentTime'];
-$generationTime += microtime(true);
-$result['generationTime'] = (sprintf('%f', $generationTime) * 1000) . 'ms';
+$result['generationTime'] = formatSeconds(timeExecution());
 //return JSON array
 if ($pretty) {
 	echo '<pre>' . safe_json_encode($result, JSON_PRETTY_PRINT) . '</pre>';
 } else {
 	exit(safe_json_encode($result, JSON_HEX_QUOT | JSON_HEX_TAG));
 }
-

+ 8 - 1
api/pages/homepage.php

@@ -31,6 +31,13 @@ if (file_exists('config' . DIRECTORY_SEPARATOR . 'config.php')) {
 			      text: \'Filter\',
 			      click: function() {
 			        $(\'#calendar-filter-modal\').modal(\'show\');
+			      },
+			      //icon: \'x\'
+			    },
+			    refreshCalendar: {
+			      text: \'Refresh\',
+			      click: function() {
+			        homepageCalendar();
 			      }
 			    }
 			  },
@@ -41,7 +48,7 @@ if (file_exists('config' . DIRECTORY_SEPARATOR . 'config.php')) {
             header: {
                left: "prev,next,today",
                center: "title",
-               right: (activeInfo.mobile) ? "filterCalendar" : "filterCalendar,month,basicWeek,basicDay,list",
+               right: (activeInfo.mobile) ? "refreshCalendar,filterCalendar" : "refreshCalendar,filterCalendar,month,basicWeek,basicDay,list",
             },
             views: {
                basicDay: { buttonText: window.lang.translate("Day"), eventLimit: ' . $GLOBALS['calendarLimit'] . ' },

+ 1 - 1
api/pages/login.php

@@ -53,7 +53,7 @@ if(activeInfo.settings.login.rememberMe){
 	                
 	                	<div class="form-group">
 				          <div class="col-xs-12">
-				            <input id="login-username-Input" class="form-control" name="username" type="text" required="" placeholder="Username" lang="en" autofocus>
+				            <input id="login-username-Input" class="form-control" name="username" type="text" required="" placeholder="Username" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" lang="en" autofocus>
 				          </div>
 				        </div>
 				        <div class="form-group">

+ 4 - 1
api/pages/settings.php

@@ -186,7 +186,10 @@ if (file_exists('config' . DIRECTORY_SEPARATOR . 'config.php')) {
 	                                <div class="col-lg-12">
 							            <div class="panel panel-default">
 											<div class="panel-heading bg-org p-t-10 p-b-10">
-												<span class="pull-left m-t-5"><span lang="en">Organizr News</span></span>
+												<span class="pull-left m-t-5">
+													<img class="lazyload loginTitle" data-src="plugins/images/organizr/logo-no-border.png"> &nbsp;
+													<span class="text-uppercase fw300" lang="en">Organizr News</span>
+												</span>
 												<div class="clearfix"></div>
 											</div>
 							                <div class="panel-wrapper p-b-0 collapse in bg-org">

+ 3 - 3
api/pages/wizard.php

@@ -295,9 +295,9 @@ $pageWizard = '
                                     <div class="panel-wrapper collapse in" aria-expanded="true">
                                         <div class="panel-body">
                                             <p lang="en">The Database will contain sensitive information.  Please place in directory outside of root Web Directory.</p>
-                                            <p lang="en">Suggested Directory: <code>' . dirname(__DIR__, 3) . DIRECTORY_SEPARATOR . 'db</code> <a class="btn default btn-outline clipboard p-5" data-clipboard-text="' . dirname(__DIR__, 3) . DIRECTORY_SEPARATOR . 'db" href="javascript:void(0);"><i class="ti-clipboard"></i></a></p>
-                                            <p lang="en">Current Directory: <code>' . dirname(__DIR__, 2) . '</code> <a class="btn default btn-outline clipboard p-5" data-clipboard-text="' . dirname(__DIR__, 2) . '" href="javascript:void(0);"><i class="ti-clipboard"></i></a></p>
-                                            <p lang="en">Parent Directory: <code>' . dirname(__DIR__, 3) . '</code> <a class="btn default btn-outline clipboard p-5" data-clipboard-text="' . dirname(__DIR__, 3) . '" href="javascript:void(0);"><i class="ti-clipboard"></i></a></p>
+                                            <p lang="en">Suggested Directory: <code>' . dirname(__DIR__, 3) . DIRECTORY_SEPARATOR . 'db</code> <a class="btn default btn-outline clipboard p-a-5" data-clipboard-text="' . dirname(__DIR__, 3) . DIRECTORY_SEPARATOR . 'db" href="javascript:void(0);"><i class="ti-clipboard"></i></a></p>
+                                            <p lang="en">Current Directory: <code>' . dirname(__DIR__, 2) . '</code> <a class="btn default btn-outline clipboard p-a-5" data-clipboard-text="' . dirname(__DIR__, 2) . '" href="javascript:void(0);"><i class="ti-clipboard"></i></a></p>
+                                            <p lang="en">Parent Directory: <code>' . dirname(__DIR__, 3) . '</code> <a class="btn default btn-outline clipboard p-a-5" data-clipboard-text="' . dirname(__DIR__, 3) . '" href="javascript:void(0);"><i class="ti-clipboard"></i></a></p>
                                         </div>
                                     </div>
                                 </div>

+ 4 - 3
api/plugins/config/invites.php

@@ -1,6 +1,7 @@
 <?php
 return array(
-    'INVITES-enabled' => false,
-    'INVITES-type-include' => 'plex',
-    'INVITES-plexLibraries' => '',
+	'INVITES-enabled' => false,
+	'INVITES-type-include' => 'plex',
+	'INVITES-plexLibraries' => '',
+	'INVITES-EmbyTemplate' => ''
 );

+ 34 - 5
api/plugins/invites.php

@@ -160,7 +160,7 @@ function inviteCodes($array)
 /* GET PHPMAILER SETTINGS */
 function invitesGetSettings()
 {
-	if ($GLOBALS['plexID'] !== '' && $GLOBALS['plexToken'] !== '' && $GLOBALS['INVITES-type-include'] !== '') {
+	if ($GLOBALS['plexID'] !== '' && $GLOBALS['plexToken'] !== '' && $GLOBALS['INVITES-type-include'] == 'plex') {
 		$loop = libraryList($GLOBALS['INVITES-type-include'])['libraries'];
 		foreach ($loop as $key => $value) {
 			$libraryList[] = array(
@@ -194,7 +194,7 @@ function invitesGetSettings()
 						'value' => 'plex'
 					),
 					array(
-						'name' => 'Emby [Not Ready]',
+						'name' => 'Emby',
 						'value' => 'emby'
 					)
 				)
@@ -217,14 +217,37 @@ function invitesGetSettings()
 			),
 			array(
 				'type' => 'select2',
-				'class' => 'select2-multiple invite-select',
+				'class' => 'select2-multiple',
+				'id' => 'invite-select',
 				'name' => 'INVITES-plexLibraries',
 				'label' => 'Libraries',
 				'value' => $GLOBALS['INVITES-plexLibraries'],
 				'options' => $libraryList
 			)
 		),
-		'Emby Settings' => array(),
+		'Emby Settings' => array(
+			array(
+				'type' => 'password-alt',
+				'name' => 'embyToken',
+				'label' => 'Emby API key',
+				'value' => $GLOBALS['embyToken'],
+				'placeholder' => 'enter key from emby'
+			),
+			array(
+				'type' => 'text',
+				'name' => 'embyURL',
+				'label' => 'Emby server adress',
+				'value' => $GLOBALS['embyURL'],
+				'placeholder' => 'localhost:8086'
+			),
+			array(
+				'type' => 'text',
+				'name' => 'INVITES-EmbyTemplate',
+				'label' => 'Emby User to be used as template for new users',
+				'value' => $GLOBALS['INVITES-EmbyTemplate'],
+				'placeholder' => 'AdamSmith'
+			)
+		),
 		'FYI' => array(
 			array(
 				'type' => 'html',
@@ -308,7 +331,13 @@ function inviteAction($username, $action = null, $type = null)
 			}
 			break;
 		case 'emby':
-			# code...
+			try {
+				#add emby user to sytem
+				return true;
+			} catch (Requests_Exception $e) {
+				writeLog('error', 'Emby Invite Function - Error: ' . $e->getMessage(), 'SYSTEM');
+				return false;
+			}
 			break;
 		default:
 			return false;

+ 100 - 3
api/plugins/js/invites.js

@@ -111,6 +111,39 @@ function joinPlex(){
     	});
     }
 }
+
+function joinEmby(){
+    var username = $('#inviteEmbyJoinUsername');
+    var email = $('#inviteEmbyJoinEmail');
+    var password = $('#inviteEmbyJoinPassword');
+    if(username.val() == ''){
+        username.focus();
+        message('Invite Error',' Please Enter Username',activeInfo.settings.notifications.position,'#FFF','warning','5000');
+    }else if(email.val() == ''){
+        email.focus();
+        message('Invite Error',' Please Enter Email',activeInfo.settings.notifications.position,'#FFF','warning','5000');
+    }else if(password.val() == ''){
+        password.focus();
+        message('Invite Error',' Please Enter Passowrd',activeInfo.settings.notifications.position,'#FFF','warning','5000');
+    }
+    if(email.val() !== '' && username.val() !== '' && password.val() !== ''){
+        organizrAPI('POST','api/?v1/emby/join',{username:username.val(), email:email.val(), password:password.val()}).success(function(data) {
+    		var response = JSON.parse(data);
+            if(response.data === true){
+                $('.invite-step-3-emby-no').toggleClass('hidden');
+                $('.invite-step-3-emby-yes').toggleClass('hidden');
+                message('Invite Function',' User Created',activeInfo.settings.notifications.position,'#FFF','success','5000');
+                $('#inviteUsernameInviteEmby').val(username.val());
+                hasEmbyUsername();
+            }else{
+                message('Invite Error',' '+response.data,activeInfo.settings.notifications.position,'#FFF','warning','5000');
+            }
+    	}).fail(function(xhr) {
+    		console.error("Organizr Function: API Connection Failed");
+    	});
+    }
+}
+
 function inviteHasAccount(type,value){
     switch (type) {
         case 'plex':
@@ -122,6 +155,15 @@ function inviteHasAccount(type,value){
                 $('.invite-step-3-plex-no').toggleClass('hidden');
             }
             break;
+        case 'emby' :
+          if(value){
+            $('.invite-step-2').toggleClass('hidden');
+            $('.invite-step-3-emby-yes').toggleClass('hidden');
+          }else{
+            $('.invite-step-2').toggleClass('hidden');
+            $('.invite-step-3-emby-no').toggleClass('hidden');
+          }
+          break;
         default:
         alert(type+' is not set up yet');
     }
@@ -158,6 +200,38 @@ function hasPlexUsername(){
         });
     }
 }
+function hasEmbyUsername(){
+    var code = $('#inviteCodeInput').val().toUpperCase();
+    var username = $('#inviteUsernameInviteEmby');
+    if(username.val() == ''){
+        username.focus();
+        message('Invite Error',' Please Enter Username',activeInfo.settings.notifications.position,'#FFF','warning','5000');
+    }else{
+        var post = {
+            plugin:'Invites/codes',
+            action:'use',
+            code:code,
+            usedby:username.val()
+        };
+        ajaxloader(".content-wrap","in");
+        organizrAPI('POST','api/?v1/plugin',post).success(function(data) {
+            var response = JSON.parse(data);
+            if(response.data === true){
+                $('.invite-step-3-emby-yes').toggleClass('hidden');
+                $('.invite-step-4-emby-accept').toggleClass('hidden');
+                if(local('get', 'invite')){
+            		local('remove', 'invite');
+            	}
+            }else{
+                message('Invite Error',' Code Incorrect',activeInfo.settings.notifications.position,'#FFF','warning','5000');
+            }
+            ajaxloader();;
+        }).fail(function(xhr) {
+            console.error("Organizr Function: API Connection Failed");
+            ajaxloader();
+        });
+    }
+}
 function verifyInvite(){
     var code = $('#inviteCodeInput').val().toUpperCase();
     var post = {
@@ -381,12 +455,36 @@ $(document).on('click', '.inviteModal', function() {
                             <br />
                             <button class="btn btn-block btn-info" onclick="joinPlex();">Submit</button>
                         </div>
+                        <div class="form-group invite-step-4-plex-accept hidden">
+                            <h4 class="" lang="en">You have been invited.  Please goto <a href="https://plex.tv" target="_blank">PLEX.TV</a> and login to accept the invite.  Once you have done that, you may head back here and login with your credentials.</h4>
+                        </div>
+                        <!-- Begin Emby Invites -->
                         <div class="form-group invite-step-3-emby-yes hidden">
+                            <div class="input-group" style="width: 100%;">
+                                <div class="input-group-addon hidden-xs"><i class="ti-user"></i></div>
+                                <input type="text" class="form-control" id="inviteUsernameInviteEmby" placeholder="Emby Username" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" autofocus="" required="">
+                            </div>
+                            <br />
+                            <button class="btn btn-block btn-info" onclick="hasEmbyUsername();">Submit</button>
                         </div>
                         <div class="form-group invite-step-3-emby-no hidden">
+                            <div class="input-group" style="width: 100%;">
+                                <div class="input-group-addon hidden-xs"><i class="ti-user"></i></div>
+                                <input type="text" class="form-control" id="inviteEmbyJoinUsername" lang="en" placeholder="Username" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" autofocus="" required="">
+                            </div>
+                            <div class="input-group" style="width: 100%;">
+                                <div class="input-group-addon hidden-xs"><i class="ti-email"></i></div>
+                                <input type="text" class="form-control" id="inviteEmbyJoinEmail" lang="en" placeholder="E-Mail" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" required="">
+                            </div>
+                            <div class="input-group" style="width: 100%;">
+                                <div class="input-group-addon hidden-xs"><i class="ti-user"></i></div>
+                                <input type="password" class="form-control" id="inviteEmbyJoinPassword" lang="en" placeholder="Password" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"  required="">
+                            </div>
+                            <br />
+                            <button class="btn btn-block btn-info" onclick="joinEmby();">Submit</button>
                         </div>
-                        <div class="form-group invite-step-4-plex-accept hidden">
-                            <h4 class="" lang="en">You have been invited.  Please goto <a href="https://plex.tv" target="_blank">PLEX.TV</a> and login to accept the invite.  Once you have done that, you may head back here and login with your credentials.</h4>
+                        <div class="form-group invite-step-4-emby-accept hidden">
+                            <h4 class="" lang="en">You Have been added to emby!</h4>
                         </div>
                     </div>
                 </div>
@@ -440,7 +538,6 @@ $(document).on('click', '#INVITES-settings-button', function() {
     organizrAPI('POST','api/?v1/plugin',post).success(function(data) {
         var response = JSON.parse(data);
         $('#INVITES-settings-items').html(buildFormGroup(response.data));
-        $(".invite-select").select2();
         $('.selectpicker').selectpicker();
     }).fail(function(xhr) {
         console.error("Organizr Function: API Connection Failed");

+ 1 - 1
css/colors/blue-dark.css

@@ -413,7 +413,7 @@ body.stop-scrolling {
   overflow: hidden;
   display: block;
 }
-.p-5{
+.p-a-5{
   padding:5px!important
 }
 .el-element-overlay .el-card-item .el-overlay-1 .el-info>li a:hover {

+ 2733 - 6
css/organizr.css

@@ -144,7 +144,7 @@ body.stop-scrolling {
     display: block;
 }
 
-.p-5 {
+.p-a-5 {
     padding: 5px !important
 }
 
@@ -491,7 +491,9 @@ input#inviteCodeInput {
     font-size: 400%;
     height: 100%;
 }
-
+.bg-org-alt {
+    background: #2d2c2c;
+}
 .bg-plex {
     background: #E5A00D;
 }
@@ -499,7 +501,9 @@ input#inviteCodeInput {
 .bg-emby {
     background: #4CAF50;
 }
-
+.bg-healthchecks {
+    background: #56b059;
+}
 .bg-sab {
     background: #ffb300;
 }
@@ -1130,14 +1134,30 @@ img.dark-logo-side {
 span.select2-selection.select2-selection--single,
 span.select2-selection.select2-selection--multiple{
     background: inherit;
-    height: 38px;
 }
 img.img-chooser {
     width: 20px;
 }
+.select2-container--default .select2-selection--single .select2-selection__arrow b {
+    border-color: #888 transparent transparent transparent;
+    border-style: solid;
+    border-width: 5px 4px 0 4px;
+    height: 0;
+    left: 50%;
+    margin-left: -4px;
+    margin-top: -2px;
+    position: absolute;
+    top: 38%;
+    width: 0;
+}
+.select2-search--dropdown {
+    display: block;
+    padding: 4px;
+    background-color: #1f1f1f;
+}
 .select2-container--default .select2-selection--single .select2-selection__rendered {
     color: inherit;
-    line-height: 38px;
+    /*line-height: 38px;*/
 }
 .select2-container--default .select2-selection--single .select2-selection__arrow {
     height: 38px;
@@ -1157,6 +1177,17 @@ img.img-chooser {
     width: 100%;
     z-index: 1051;
 }
+.select2-container--default .select2-selection--multiple .select2-selection__choice {
+    background-color: #2babe3;
+    border: 1px solid #1b1a1a;
+    color: #1b1a1a;
+}
+.select2-container--default .select2-selection--multiple .select2-selection__choice__remove {
+    color: #1f1f1f;
+}
+.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {
+    color: #f96262;
+}
 .fc-scroller .simplebar-track.horizontal {
     display: none;
 }
@@ -1226,7 +1257,7 @@ span.fc-title {
     font-weight: 600;
 }
 .dropdown-menu {
-    width: 100%;
+    width: inherit;
 }
 #organizrNewsPanel .panel-body {
     background: #2d2c2c;
@@ -1286,6 +1317,35 @@ span.fc-title {
     border-top-right-radius: 3px;
     border-bottom-right-radius: 3px;
 }
+.left-health {
+    width: 5px;
+    height: 100%;
+    position: absolute;
+    left: 0;
+}
+.animated-2 {
+    -webkit-animation-duration: 2s;
+    animation-duration: 2s;
+    -webkit-animation-fill-mode: both;
+    animation-fill-mode: both;
+}
+.animated-3 {
+    -webkit-animation-duration: 3s;
+    animation-duration: 3s;
+    -webkit-animation-fill-mode: both;
+    animation-fill-mode: both;
+}
+.select2-container--default.select2-container--focus .select2-selection--multiple {
+    height: 100%;
+}
+.healthPosition {
+    position: absolute;
+    z-index: 10;
+    width: 100%;
+}
+.btn-danger i.ti-trash {
+    margin-left: -1.5px;
+}
 /*
 body,
 html {
@@ -1304,3 +1364,2670 @@ html {
     display: none;
 }
 */
+.display-1 {
+    font-size: 6rem;
+    font-weight: 300;
+    line-height: 1.2; }
+
+.display-2 {
+    font-size: 5.5rem;
+    font-weight: 300;
+    line-height: 1.2; }
+
+.display-3 {
+    font-size: 4.5rem;
+    font-weight: 300;
+    line-height: 1.2; }
+
+.display-4 {
+    font-size: 3.5rem;
+    font-weight: 300;
+    line-height: 1.2; }
+
+.display-5 {
+    font-size: 3rem;
+    font-weight: 300;
+    line-height: 1.2; }
+
+.display-6 {
+    font-size: 2.5rem;
+    font-weight: 300;
+    line-height: 1.2; }
+
+.display-7 {
+    font-size: 2rem;
+    font-weight: 300;
+    line-height: 1.2; }
+
+.font-16 {
+    font-size: 16px; }
+
+.font-12 {
+    font-size: 12px; }
+
+.font-14 {
+    font-size: 14px; }
+
+.font-10 {
+    font-size: 10px; }
+
+.font-18 {
+    font-size: 18px; }
+
+.font-20 {
+    font-size: 20px; }
+
+.font-22 {
+    font-size: 22px; }
+
+.font-24 {
+    font-size: 24px; }
+
+.w-25 {
+    width: 25% !important; }
+
+.w-50 {
+    width: 50% !important; }
+
+.w-75 {
+    width: 75% !important; }
+
+.w-100 {
+    width: 100% !important; }
+
+.w-auto {
+    width: auto !important; }
+
+.h-25 {
+    height: 25% !important; }
+
+.h-50 {
+    height: 50% !important; }
+
+.h-75 {
+    height: 75% !important; }
+
+.h-100 {
+    height: 100% !important; }
+
+.h-auto {
+    height: auto !important; }
+
+.mw-100 {
+    max-width: 100% !important; }
+
+.mh-100 {
+    max-height: 100% !important; }
+
+.min-vw-100 {
+    min-width: 100vw !important; }
+
+.min-vh-100 {
+    min-height: 100vh !important; }
+
+.vw-100 {
+    width: 100vw !important; }
+
+.vh-100 {
+    height: 100vh !important; }
+
+.stretched-link::after {
+    position: absolute;
+    top: 0;
+    right: 0;
+    bottom: 0;
+    left: 0;
+    z-index: 1;
+    pointer-events: auto;
+    content: "";
+    background-color: rgba(0, 0, 0, 0); }
+
+.m-0 {
+    margin: 0 !important; }
+
+.mt-0,
+.my-0 {
+    margin-top: 0 !important; }
+
+.mr-0,
+.mx-0 {
+    margin-right: 0 !important; }
+
+.mb-0,
+.my-0 {
+    margin-bottom: 0 !important; }
+
+.ml-0,
+.mx-0 {
+    margin-left: 0 !important; }
+
+.m-1 {
+    margin: 0.25rem !important; }
+
+.mt-1,
+.my-1 {
+    margin-top: 0.25rem !important; }
+
+.mr-1,
+.mx-1 {
+    margin-right: 0.25rem !important; }
+
+.mb-1,
+.my-1 {
+    margin-bottom: 0.25rem !important; }
+
+.ml-1,
+.mx-1 {
+    margin-left: 0.25rem !important; }
+
+.m-2 {
+    margin: 0.5rem !important; }
+
+.mt-2,
+.my-2 {
+    margin-top: 0.5rem !important; }
+
+.mr-2,
+.mx-2 {
+    margin-right: 0.5rem !important; }
+
+.mb-2,
+.my-2 {
+    margin-bottom: 0.5rem !important; }
+
+.ml-2,
+.mx-2 {
+    margin-left: 0.5rem !important; }
+
+.m-3 {
+    margin: 1rem !important; }
+
+.mt-3,
+.my-3 {
+    margin-top: 1rem !important; }
+
+.mr-3,
+.mx-3 {
+    margin-right: 1rem !important; }
+
+.mb-3,
+.my-3 {
+    margin-bottom: 1rem !important; }
+
+.ml-3,
+.mx-3 {
+    margin-left: 1rem !important; }
+
+.m-4 {
+    margin: 1.5rem !important; }
+
+.mt-4,
+.my-4 {
+    margin-top: 1.5rem !important; }
+
+.mr-4,
+.mx-4 {
+    margin-right: 1.5rem !important; }
+
+.mb-4,
+.my-4 {
+    margin-bottom: 1.5rem !important; }
+
+.ml-4,
+.mx-4 {
+    margin-left: 1.5rem !important; }
+
+.m-5 {
+    margin: 3rem !important; }
+
+.mt-5,
+.my-5 {
+    margin-top: 3rem !important; }
+
+.mr-5,
+.mx-5 {
+    margin-right: 3rem !important; }
+
+.mb-5,
+.my-5 {
+    margin-bottom: 3rem !important; }
+
+.ml-5,
+.mx-5 {
+    margin-left: 3rem !important; }
+
+.p-0 {
+    padding: 0 !important; }
+
+.pt-0,
+.py-0 {
+    padding-top: 0 !important; }
+
+.pr-0,
+.px-0 {
+    padding-right: 0 !important; }
+
+.pb-0,
+.py-0 {
+    padding-bottom: 0 !important; }
+
+.pl-0,
+.px-0 {
+    padding-left: 0 !important; }
+
+.p-1 {
+    padding: 0.25rem !important; }
+
+.pt-1,
+.py-1 {
+    padding-top: 0.25rem !important; }
+
+.pr-1,
+.px-1 {
+    padding-right: 0.25rem !important; }
+
+.pb-1,
+.py-1 {
+    padding-bottom: 0.25rem !important; }
+
+.pl-1,
+.px-1 {
+    padding-left: 0.25rem !important; }
+
+.p-2 {
+    padding: 0.5rem !important; }
+
+.pt-2,
+.py-2 {
+    padding-top: 0.5rem !important; }
+
+.pr-2,
+.px-2 {
+    padding-right: 0.5rem !important; }
+
+.pb-2,
+.py-2 {
+    padding-bottom: 0.5rem !important; }
+
+.pl-2,
+.px-2 {
+    padding-left: 0.5rem !important; }
+
+.p-3 {
+    padding: 1rem !important; }
+
+.pt-3,
+.py-3 {
+    padding-top: 1rem !important; }
+
+.pr-3,
+.px-3 {
+    padding-right: 1rem !important; }
+
+.pb-3,
+.py-3 {
+    padding-bottom: 1rem !important; }
+
+.pl-3,
+.px-3 {
+    padding-left: 1rem !important; }
+
+.p-4 {
+    padding: 1.5rem !important; }
+
+.pt-4,
+.py-4 {
+    padding-top: 1.5rem !important; }
+
+.pr-4,
+.px-4 {
+    padding-right: 1.5rem !important; }
+
+.pb-4,
+.py-4 {
+    padding-bottom: 1.5rem !important; }
+
+.pl-4,
+.px-4 {
+    padding-left: 1.5rem !important; }
+
+.p-5 {
+    padding: 3rem !important; }
+
+.pt-5,
+.py-5 {
+    padding-top: 3rem !important; }
+
+.pr-5,
+.px-5 {
+    padding-right: 3rem !important; }
+
+.pb-5,
+.py-5 {
+    padding-bottom: 3rem !important; }
+
+.pl-5,
+.px-5 {
+    padding-left: 3rem !important; }
+
+.m-n1 {
+    margin: -0.25rem !important; }
+
+.mt-n1,
+.my-n1 {
+    margin-top: -0.25rem !important; }
+
+.mr-n1,
+.mx-n1 {
+    margin-right: -0.25rem !important; }
+
+.mb-n1,
+.my-n1 {
+    margin-bottom: -0.25rem !important; }
+
+.ml-n1,
+.mx-n1 {
+    margin-left: -0.25rem !important; }
+
+.m-n2 {
+    margin: -0.5rem !important; }
+
+.mt-n2,
+.my-n2 {
+    margin-top: -0.5rem !important; }
+
+.mr-n2,
+.mx-n2 {
+    margin-right: -0.5rem !important; }
+
+.mb-n2,
+.my-n2 {
+    margin-bottom: -0.5rem !important; }
+
+.ml-n2,
+.mx-n2 {
+    margin-left: -0.5rem !important; }
+
+.m-n3 {
+    margin: -1rem !important; }
+
+.mt-n3,
+.my-n3 {
+    margin-top: -1rem !important; }
+
+.mr-n3,
+.mx-n3 {
+    margin-right: -1rem !important; }
+
+.mb-n3,
+.my-n3 {
+    margin-bottom: -1rem !important; }
+
+.ml-n3,
+.mx-n3 {
+    margin-left: -1rem !important; }
+
+.m-n4 {
+    margin: -1.5rem !important; }
+
+.mt-n4,
+.my-n4 {
+    margin-top: -1.5rem !important; }
+
+.mr-n4,
+.mx-n4 {
+    margin-right: -1.5rem !important; }
+
+.mb-n4,
+.my-n4 {
+    margin-bottom: -1.5rem !important; }
+
+.ml-n4,
+.mx-n4 {
+    margin-left: -1.5rem !important; }
+
+.m-n5 {
+    margin: -3rem !important; }
+
+.mt-n5,
+.my-n5 {
+    margin-top: -3rem !important; }
+
+.mr-n5,
+.mx-n5 {
+    margin-right: -3rem !important; }
+
+.mb-n5,
+.my-n5 {
+    margin-bottom: -3rem !important; }
+
+.ml-n5,
+.mx-n5 {
+    margin-left: -3rem !important; }
+
+.m-auto {
+    margin: auto !important; }
+
+.mt-auto,
+.my-auto {
+    margin-top: auto !important; }
+
+.mr-auto,
+.mx-auto {
+    margin-right: auto !important; }
+
+.mb-auto,
+.my-auto {
+    margin-bottom: auto !important; }
+
+.ml-auto,
+.mx-auto {
+    margin-left: auto !important; }
+
+@media (min-width: 576px) {
+    .m-sm-0 {
+        margin: 0 !important; }
+    .mt-sm-0,
+    .my-sm-0 {
+        margin-top: 0 !important; }
+    .mr-sm-0,
+    .mx-sm-0 {
+        margin-right: 0 !important; }
+    .mb-sm-0,
+    .my-sm-0 {
+        margin-bottom: 0 !important; }
+    .ml-sm-0,
+    .mx-sm-0 {
+        margin-left: 0 !important; }
+    .m-sm-1 {
+        margin: 0.25rem !important; }
+    .mt-sm-1,
+    .my-sm-1 {
+        margin-top: 0.25rem !important; }
+    .mr-sm-1,
+    .mx-sm-1 {
+        margin-right: 0.25rem !important; }
+    .mb-sm-1,
+    .my-sm-1 {
+        margin-bottom: 0.25rem !important; }
+    .ml-sm-1,
+    .mx-sm-1 {
+        margin-left: 0.25rem !important; }
+    .m-sm-2 {
+        margin: 0.5rem !important; }
+    .mt-sm-2,
+    .my-sm-2 {
+        margin-top: 0.5rem !important; }
+    .mr-sm-2,
+    .mx-sm-2 {
+        margin-right: 0.5rem !important; }
+    .mb-sm-2,
+    .my-sm-2 {
+        margin-bottom: 0.5rem !important; }
+    .ml-sm-2,
+    .mx-sm-2 {
+        margin-left: 0.5rem !important; }
+    .m-sm-3 {
+        margin: 1rem !important; }
+    .mt-sm-3,
+    .my-sm-3 {
+        margin-top: 1rem !important; }
+    .mr-sm-3,
+    .mx-sm-3 {
+        margin-right: 1rem !important; }
+    .mb-sm-3,
+    .my-sm-3 {
+        margin-bottom: 1rem !important; }
+    .ml-sm-3,
+    .mx-sm-3 {
+        margin-left: 1rem !important; }
+    .m-sm-4 {
+        margin: 1.5rem !important; }
+    .mt-sm-4,
+    .my-sm-4 {
+        margin-top: 1.5rem !important; }
+    .mr-sm-4,
+    .mx-sm-4 {
+        margin-right: 1.5rem !important; }
+    .mb-sm-4,
+    .my-sm-4 {
+        margin-bottom: 1.5rem !important; }
+    .ml-sm-4,
+    .mx-sm-4 {
+        margin-left: 1.5rem !important; }
+    .m-sm-5 {
+        margin: 3rem !important; }
+    .mt-sm-5,
+    .my-sm-5 {
+        margin-top: 3rem !important; }
+    .mr-sm-5,
+    .mx-sm-5 {
+        margin-right: 3rem !important; }
+    .mb-sm-5,
+    .my-sm-5 {
+        margin-bottom: 3rem !important; }
+    .ml-sm-5,
+    .mx-sm-5 {
+        margin-left: 3rem !important; }
+    .p-sm-0 {
+        padding: 0 !important; }
+    .pt-sm-0,
+    .py-sm-0 {
+        padding-top: 0 !important; }
+    .pr-sm-0,
+    .px-sm-0 {
+        padding-right: 0 !important; }
+    .pb-sm-0,
+    .py-sm-0 {
+        padding-bottom: 0 !important; }
+    .pl-sm-0,
+    .px-sm-0 {
+        padding-left: 0 !important; }
+    .p-sm-1 {
+        padding: 0.25rem !important; }
+    .pt-sm-1,
+    .py-sm-1 {
+        padding-top: 0.25rem !important; }
+    .pr-sm-1,
+    .px-sm-1 {
+        padding-right: 0.25rem !important; }
+    .pb-sm-1,
+    .py-sm-1 {
+        padding-bottom: 0.25rem !important; }
+    .pl-sm-1,
+    .px-sm-1 {
+        padding-left: 0.25rem !important; }
+    .p-sm-2 {
+        padding: 0.5rem !important; }
+    .pt-sm-2,
+    .py-sm-2 {
+        padding-top: 0.5rem !important; }
+    .pr-sm-2,
+    .px-sm-2 {
+        padding-right: 0.5rem !important; }
+    .pb-sm-2,
+    .py-sm-2 {
+        padding-bottom: 0.5rem !important; }
+    .pl-sm-2,
+    .px-sm-2 {
+        padding-left: 0.5rem !important; }
+    .p-sm-3 {
+        padding: 1rem !important; }
+    .pt-sm-3,
+    .py-sm-3 {
+        padding-top: 1rem !important; }
+    .pr-sm-3,
+    .px-sm-3 {
+        padding-right: 1rem !important; }
+    .pb-sm-3,
+    .py-sm-3 {
+        padding-bottom: 1rem !important; }
+    .pl-sm-3,
+    .px-sm-3 {
+        padding-left: 1rem !important; }
+    .p-sm-4 {
+        padding: 1.5rem !important; }
+    .pt-sm-4,
+    .py-sm-4 {
+        padding-top: 1.5rem !important; }
+    .pr-sm-4,
+    .px-sm-4 {
+        padding-right: 1.5rem !important; }
+    .pb-sm-4,
+    .py-sm-4 {
+        padding-bottom: 1.5rem !important; }
+    .pl-sm-4,
+    .px-sm-4 {
+        padding-left: 1.5rem !important; }
+    .p-sm-5 {
+        padding: 3rem !important; }
+    .pt-sm-5,
+    .py-sm-5 {
+        padding-top: 3rem !important; }
+    .pr-sm-5,
+    .px-sm-5 {
+        padding-right: 3rem !important; }
+    .pb-sm-5,
+    .py-sm-5 {
+        padding-bottom: 3rem !important; }
+    .pl-sm-5,
+    .px-sm-5 {
+        padding-left: 3rem !important; }
+    .m-sm-n1 {
+        margin: -0.25rem !important; }
+    .mt-sm-n1,
+    .my-sm-n1 {
+        margin-top: -0.25rem !important; }
+    .mr-sm-n1,
+    .mx-sm-n1 {
+        margin-right: -0.25rem !important; }
+    .mb-sm-n1,
+    .my-sm-n1 {
+        margin-bottom: -0.25rem !important; }
+    .ml-sm-n1,
+    .mx-sm-n1 {
+        margin-left: -0.25rem !important; }
+    .m-sm-n2 {
+        margin: -0.5rem !important; }
+    .mt-sm-n2,
+    .my-sm-n2 {
+        margin-top: -0.5rem !important; }
+    .mr-sm-n2,
+    .mx-sm-n2 {
+        margin-right: -0.5rem !important; }
+    .mb-sm-n2,
+    .my-sm-n2 {
+        margin-bottom: -0.5rem !important; }
+    .ml-sm-n2,
+    .mx-sm-n2 {
+        margin-left: -0.5rem !important; }
+    .m-sm-n3 {
+        margin: -1rem !important; }
+    .mt-sm-n3,
+    .my-sm-n3 {
+        margin-top: -1rem !important; }
+    .mr-sm-n3,
+    .mx-sm-n3 {
+        margin-right: -1rem !important; }
+    .mb-sm-n3,
+    .my-sm-n3 {
+        margin-bottom: -1rem !important; }
+    .ml-sm-n3,
+    .mx-sm-n3 {
+        margin-left: -1rem !important; }
+    .m-sm-n4 {
+        margin: -1.5rem !important; }
+    .mt-sm-n4,
+    .my-sm-n4 {
+        margin-top: -1.5rem !important; }
+    .mr-sm-n4,
+    .mx-sm-n4 {
+        margin-right: -1.5rem !important; }
+    .mb-sm-n4,
+    .my-sm-n4 {
+        margin-bottom: -1.5rem !important; }
+    .ml-sm-n4,
+    .mx-sm-n4 {
+        margin-left: -1.5rem !important; }
+    .m-sm-n5 {
+        margin: -3rem !important; }
+    .mt-sm-n5,
+    .my-sm-n5 {
+        margin-top: -3rem !important; }
+    .mr-sm-n5,
+    .mx-sm-n5 {
+        margin-right: -3rem !important; }
+    .mb-sm-n5,
+    .my-sm-n5 {
+        margin-bottom: -3rem !important; }
+    .ml-sm-n5,
+    .mx-sm-n5 {
+        margin-left: -3rem !important; }
+    .m-sm-auto {
+        margin: auto !important; }
+    .mt-sm-auto,
+    .my-sm-auto {
+        margin-top: auto !important; }
+    .mr-sm-auto,
+    .mx-sm-auto {
+        margin-right: auto !important; }
+    .mb-sm-auto,
+    .my-sm-auto {
+        margin-bottom: auto !important; }
+    .ml-sm-auto,
+    .mx-sm-auto {
+        margin-left: auto !important; } }
+
+@media (min-width: 768px) {
+    .m-md-0 {
+        margin: 0 !important; }
+    .mt-md-0,
+    .my-md-0 {
+        margin-top: 0 !important; }
+    .mr-md-0,
+    .mx-md-0 {
+        margin-right: 0 !important; }
+    .mb-md-0,
+    .my-md-0 {
+        margin-bottom: 0 !important; }
+    .ml-md-0,
+    .mx-md-0 {
+        margin-left: 0 !important; }
+    .m-md-1 {
+        margin: 0.25rem !important; }
+    .mt-md-1,
+    .my-md-1 {
+        margin-top: 0.25rem !important; }
+    .mr-md-1,
+    .mx-md-1 {
+        margin-right: 0.25rem !important; }
+    .mb-md-1,
+    .my-md-1 {
+        margin-bottom: 0.25rem !important; }
+    .ml-md-1,
+    .mx-md-1 {
+        margin-left: 0.25rem !important; }
+    .m-md-2 {
+        margin: 0.5rem !important; }
+    .mt-md-2,
+    .my-md-2 {
+        margin-top: 0.5rem !important; }
+    .mr-md-2,
+    .mx-md-2 {
+        margin-right: 0.5rem !important; }
+    .mb-md-2,
+    .my-md-2 {
+        margin-bottom: 0.5rem !important; }
+    .ml-md-2,
+    .mx-md-2 {
+        margin-left: 0.5rem !important; }
+    .m-md-3 {
+        margin: 1rem !important; }
+    .mt-md-3,
+    .my-md-3 {
+        margin-top: 1rem !important; }
+    .mr-md-3,
+    .mx-md-3 {
+        margin-right: 1rem !important; }
+    .mb-md-3,
+    .my-md-3 {
+        margin-bottom: 1rem !important; }
+    .ml-md-3,
+    .mx-md-3 {
+        margin-left: 1rem !important; }
+    .m-md-4 {
+        margin: 1.5rem !important; }
+    .mt-md-4,
+    .my-md-4 {
+        margin-top: 1.5rem !important; }
+    .mr-md-4,
+    .mx-md-4 {
+        margin-right: 1.5rem !important; }
+    .mb-md-4,
+    .my-md-4 {
+        margin-bottom: 1.5rem !important; }
+    .ml-md-4,
+    .mx-md-4 {
+        margin-left: 1.5rem !important; }
+    .m-md-5 {
+        margin: 3rem !important; }
+    .mt-md-5,
+    .my-md-5 {
+        margin-top: 3rem !important; }
+    .mr-md-5,
+    .mx-md-5 {
+        margin-right: 3rem !important; }
+    .mb-md-5,
+    .my-md-5 {
+        margin-bottom: 3rem !important; }
+    .ml-md-5,
+    .mx-md-5 {
+        margin-left: 3rem !important; }
+    .p-md-0 {
+        padding: 0 !important; }
+    .pt-md-0,
+    .py-md-0 {
+        padding-top: 0 !important; }
+    .pr-md-0,
+    .px-md-0 {
+        padding-right: 0 !important; }
+    .pb-md-0,
+    .py-md-0 {
+        padding-bottom: 0 !important; }
+    .pl-md-0,
+    .px-md-0 {
+        padding-left: 0 !important; }
+    .p-md-1 {
+        padding: 0.25rem !important; }
+    .pt-md-1,
+    .py-md-1 {
+        padding-top: 0.25rem !important; }
+    .pr-md-1,
+    .px-md-1 {
+        padding-right: 0.25rem !important; }
+    .pb-md-1,
+    .py-md-1 {
+        padding-bottom: 0.25rem !important; }
+    .pl-md-1,
+    .px-md-1 {
+        padding-left: 0.25rem !important; }
+    .p-md-2 {
+        padding: 0.5rem !important; }
+    .pt-md-2,
+    .py-md-2 {
+        padding-top: 0.5rem !important; }
+    .pr-md-2,
+    .px-md-2 {
+        padding-right: 0.5rem !important; }
+    .pb-md-2,
+    .py-md-2 {
+        padding-bottom: 0.5rem !important; }
+    .pl-md-2,
+    .px-md-2 {
+        padding-left: 0.5rem !important; }
+    .p-md-3 {
+        padding: 1rem !important; }
+    .pt-md-3,
+    .py-md-3 {
+        padding-top: 1rem !important; }
+    .pr-md-3,
+    .px-md-3 {
+        padding-right: 1rem !important; }
+    .pb-md-3,
+    .py-md-3 {
+        padding-bottom: 1rem !important; }
+    .pl-md-3,
+    .px-md-3 {
+        padding-left: 1rem !important; }
+    .p-md-4 {
+        padding: 1.5rem !important; }
+    .pt-md-4,
+    .py-md-4 {
+        padding-top: 1.5rem !important; }
+    .pr-md-4,
+    .px-md-4 {
+        padding-right: 1.5rem !important; }
+    .pb-md-4,
+    .py-md-4 {
+        padding-bottom: 1.5rem !important; }
+    .pl-md-4,
+    .px-md-4 {
+        padding-left: 1.5rem !important; }
+    .p-md-5 {
+        padding: 3rem !important; }
+    .pt-md-5,
+    .py-md-5 {
+        padding-top: 3rem !important; }
+    .pr-md-5,
+    .px-md-5 {
+        padding-right: 3rem !important; }
+    .pb-md-5,
+    .py-md-5 {
+        padding-bottom: 3rem !important; }
+    .pl-md-5,
+    .px-md-5 {
+        padding-left: 3rem !important; }
+    .m-md-n1 {
+        margin: -0.25rem !important; }
+    .mt-md-n1,
+    .my-md-n1 {
+        margin-top: -0.25rem !important; }
+    .mr-md-n1,
+    .mx-md-n1 {
+        margin-right: -0.25rem !important; }
+    .mb-md-n1,
+    .my-md-n1 {
+        margin-bottom: -0.25rem !important; }
+    .ml-md-n1,
+    .mx-md-n1 {
+        margin-left: -0.25rem !important; }
+    .m-md-n2 {
+        margin: -0.5rem !important; }
+    .mt-md-n2,
+    .my-md-n2 {
+        margin-top: -0.5rem !important; }
+    .mr-md-n2,
+    .mx-md-n2 {
+        margin-right: -0.5rem !important; }
+    .mb-md-n2,
+    .my-md-n2 {
+        margin-bottom: -0.5rem !important; }
+    .ml-md-n2,
+    .mx-md-n2 {
+        margin-left: -0.5rem !important; }
+    .m-md-n3 {
+        margin: -1rem !important; }
+    .mt-md-n3,
+    .my-md-n3 {
+        margin-top: -1rem !important; }
+    .mr-md-n3,
+    .mx-md-n3 {
+        margin-right: -1rem !important; }
+    .mb-md-n3,
+    .my-md-n3 {
+        margin-bottom: -1rem !important; }
+    .ml-md-n3,
+    .mx-md-n3 {
+        margin-left: -1rem !important; }
+    .m-md-n4 {
+        margin: -1.5rem !important; }
+    .mt-md-n4,
+    .my-md-n4 {
+        margin-top: -1.5rem !important; }
+    .mr-md-n4,
+    .mx-md-n4 {
+        margin-right: -1.5rem !important; }
+    .mb-md-n4,
+    .my-md-n4 {
+        margin-bottom: -1.5rem !important; }
+    .ml-md-n4,
+    .mx-md-n4 {
+        margin-left: -1.5rem !important; }
+    .m-md-n5 {
+        margin: -3rem !important; }
+    .mt-md-n5,
+    .my-md-n5 {
+        margin-top: -3rem !important; }
+    .mr-md-n5,
+    .mx-md-n5 {
+        margin-right: -3rem !important; }
+    .mb-md-n5,
+    .my-md-n5 {
+        margin-bottom: -3rem !important; }
+    .ml-md-n5,
+    .mx-md-n5 {
+        margin-left: -3rem !important; }
+    .m-md-auto {
+        margin: auto !important; }
+    .mt-md-auto,
+    .my-md-auto {
+        margin-top: auto !important; }
+    .mr-md-auto,
+    .mx-md-auto {
+        margin-right: auto !important; }
+    .mb-md-auto,
+    .my-md-auto {
+        margin-bottom: auto !important; }
+    .ml-md-auto,
+    .mx-md-auto {
+        margin-left: auto !important; } }
+
+@media (min-width: 992px) {
+    .m-lg-0 {
+        margin: 0 !important; }
+    .mt-lg-0,
+    .my-lg-0 {
+        margin-top: 0 !important; }
+    .mr-lg-0,
+    .mx-lg-0 {
+        margin-right: 0 !important; }
+    .mb-lg-0,
+    .my-lg-0 {
+        margin-bottom: 0 !important; }
+    .ml-lg-0,
+    .mx-lg-0 {
+        margin-left: 0 !important; }
+    .m-lg-1 {
+        margin: 0.25rem !important; }
+    .mt-lg-1,
+    .my-lg-1 {
+        margin-top: 0.25rem !important; }
+    .mr-lg-1,
+    .mx-lg-1 {
+        margin-right: 0.25rem !important; }
+    .mb-lg-1,
+    .my-lg-1 {
+        margin-bottom: 0.25rem !important; }
+    .ml-lg-1,
+    .mx-lg-1 {
+        margin-left: 0.25rem !important; }
+    .m-lg-2 {
+        margin: 0.5rem !important; }
+    .mt-lg-2,
+    .my-lg-2 {
+        margin-top: 0.5rem !important; }
+    .mr-lg-2,
+    .mx-lg-2 {
+        margin-right: 0.5rem !important; }
+    .mb-lg-2,
+    .my-lg-2 {
+        margin-bottom: 0.5rem !important; }
+    .ml-lg-2,
+    .mx-lg-2 {
+        margin-left: 0.5rem !important; }
+    .m-lg-3 {
+        margin: 1rem !important; }
+    .mt-lg-3,
+    .my-lg-3 {
+        margin-top: 1rem !important; }
+    .mr-lg-3,
+    .mx-lg-3 {
+        margin-right: 1rem !important; }
+    .mb-lg-3,
+    .my-lg-3 {
+        margin-bottom: 1rem !important; }
+    .ml-lg-3,
+    .mx-lg-3 {
+        margin-left: 1rem !important; }
+    .m-lg-4 {
+        margin: 1.5rem !important; }
+    .mt-lg-4,
+    .my-lg-4 {
+        margin-top: 1.5rem !important; }
+    .mr-lg-4,
+    .mx-lg-4 {
+        margin-right: 1.5rem !important; }
+    .mb-lg-4,
+    .my-lg-4 {
+        margin-bottom: 1.5rem !important; }
+    .ml-lg-4,
+    .mx-lg-4 {
+        margin-left: 1.5rem !important; }
+    .m-lg-5 {
+        margin: 3rem !important; }
+    .mt-lg-5,
+    .my-lg-5 {
+        margin-top: 3rem !important; }
+    .mr-lg-5,
+    .mx-lg-5 {
+        margin-right: 3rem !important; }
+    .mb-lg-5,
+    .my-lg-5 {
+        margin-bottom: 3rem !important; }
+    .ml-lg-5,
+    .mx-lg-5 {
+        margin-left: 3rem !important; }
+    .p-lg-0 {
+        padding: 0 !important; }
+    .pt-lg-0,
+    .py-lg-0 {
+        padding-top: 0 !important; }
+    .pr-lg-0,
+    .px-lg-0 {
+        padding-right: 0 !important; }
+    .pb-lg-0,
+    .py-lg-0 {
+        padding-bottom: 0 !important; }
+    .pl-lg-0,
+    .px-lg-0 {
+        padding-left: 0 !important; }
+    .p-lg-1 {
+        padding: 0.25rem !important; }
+    .pt-lg-1,
+    .py-lg-1 {
+        padding-top: 0.25rem !important; }
+    .pr-lg-1,
+    .px-lg-1 {
+        padding-right: 0.25rem !important; }
+    .pb-lg-1,
+    .py-lg-1 {
+        padding-bottom: 0.25rem !important; }
+    .pl-lg-1,
+    .px-lg-1 {
+        padding-left: 0.25rem !important; }
+    .p-lg-2 {
+        padding: 0.5rem !important; }
+    .pt-lg-2,
+    .py-lg-2 {
+        padding-top: 0.5rem !important; }
+    .pr-lg-2,
+    .px-lg-2 {
+        padding-right: 0.5rem !important; }
+    .pb-lg-2,
+    .py-lg-2 {
+        padding-bottom: 0.5rem !important; }
+    .pl-lg-2,
+    .px-lg-2 {
+        padding-left: 0.5rem !important; }
+    .p-lg-3 {
+        padding: 1rem !important; }
+    .pt-lg-3,
+    .py-lg-3 {
+        padding-top: 1rem !important; }
+    .pr-lg-3,
+    .px-lg-3 {
+        padding-right: 1rem !important; }
+    .pb-lg-3,
+    .py-lg-3 {
+        padding-bottom: 1rem !important; }
+    .pl-lg-3,
+    .px-lg-3 {
+        padding-left: 1rem !important; }
+    .p-lg-4 {
+        padding: 1.5rem !important; }
+    .pt-lg-4,
+    .py-lg-4 {
+        padding-top: 1.5rem !important; }
+    .pr-lg-4,
+    .px-lg-4 {
+        padding-right: 1.5rem !important; }
+    .pb-lg-4,
+    .py-lg-4 {
+        padding-bottom: 1.5rem !important; }
+    .pl-lg-4,
+    .px-lg-4 {
+        padding-left: 1.5rem !important; }
+    .p-lg-5 {
+        padding: 3rem !important; }
+    .pt-lg-5,
+    .py-lg-5 {
+        padding-top: 3rem !important; }
+    .pr-lg-5,
+    .px-lg-5 {
+        padding-right: 3rem !important; }
+    .pb-lg-5,
+    .py-lg-5 {
+        padding-bottom: 3rem !important; }
+    .pl-lg-5,
+    .px-lg-5 {
+        padding-left: 3rem !important; }
+    .m-lg-n1 {
+        margin: -0.25rem !important; }
+    .mt-lg-n1,
+    .my-lg-n1 {
+        margin-top: -0.25rem !important; }
+    .mr-lg-n1,
+    .mx-lg-n1 {
+        margin-right: -0.25rem !important; }
+    .mb-lg-n1,
+    .my-lg-n1 {
+        margin-bottom: -0.25rem !important; }
+    .ml-lg-n1,
+    .mx-lg-n1 {
+        margin-left: -0.25rem !important; }
+    .m-lg-n2 {
+        margin: -0.5rem !important; }
+    .mt-lg-n2,
+    .my-lg-n2 {
+        margin-top: -0.5rem !important; }
+    .mr-lg-n2,
+    .mx-lg-n2 {
+        margin-right: -0.5rem !important; }
+    .mb-lg-n2,
+    .my-lg-n2 {
+        margin-bottom: -0.5rem !important; }
+    .ml-lg-n2,
+    .mx-lg-n2 {
+        margin-left: -0.5rem !important; }
+    .m-lg-n3 {
+        margin: -1rem !important; }
+    .mt-lg-n3,
+    .my-lg-n3 {
+        margin-top: -1rem !important; }
+    .mr-lg-n3,
+    .mx-lg-n3 {
+        margin-right: -1rem !important; }
+    .mb-lg-n3,
+    .my-lg-n3 {
+        margin-bottom: -1rem !important; }
+    .ml-lg-n3,
+    .mx-lg-n3 {
+        margin-left: -1rem !important; }
+    .m-lg-n4 {
+        margin: -1.5rem !important; }
+    .mt-lg-n4,
+    .my-lg-n4 {
+        margin-top: -1.5rem !important; }
+    .mr-lg-n4,
+    .mx-lg-n4 {
+        margin-right: -1.5rem !important; }
+    .mb-lg-n4,
+    .my-lg-n4 {
+        margin-bottom: -1.5rem !important; }
+    .ml-lg-n4,
+    .mx-lg-n4 {
+        margin-left: -1.5rem !important; }
+    .m-lg-n5 {
+        margin: -3rem !important; }
+    .mt-lg-n5,
+    .my-lg-n5 {
+        margin-top: -3rem !important; }
+    .mr-lg-n5,
+    .mx-lg-n5 {
+        margin-right: -3rem !important; }
+    .mb-lg-n5,
+    .my-lg-n5 {
+        margin-bottom: -3rem !important; }
+    .ml-lg-n5,
+    .mx-lg-n5 {
+        margin-left: -3rem !important; }
+    .m-lg-auto {
+        margin: auto !important; }
+    .mt-lg-auto,
+    .my-lg-auto {
+        margin-top: auto !important; }
+    .mr-lg-auto,
+    .mx-lg-auto {
+        margin-right: auto !important; }
+    .mb-lg-auto,
+    .my-lg-auto {
+        margin-bottom: auto !important; }
+    .ml-lg-auto,
+    .mx-lg-auto {
+        margin-left: auto !important; } }
+
+@media (min-width: 1200px) {
+    .m-xl-0 {
+        margin: 0 !important; }
+    .mt-xl-0,
+    .my-xl-0 {
+        margin-top: 0 !important; }
+    .mr-xl-0,
+    .mx-xl-0 {
+        margin-right: 0 !important; }
+    .mb-xl-0,
+    .my-xl-0 {
+        margin-bottom: 0 !important; }
+    .ml-xl-0,
+    .mx-xl-0 {
+        margin-left: 0 !important; }
+    .m-xl-1 {
+        margin: 0.25rem !important; }
+    .mt-xl-1,
+    .my-xl-1 {
+        margin-top: 0.25rem !important; }
+    .mr-xl-1,
+    .mx-xl-1 {
+        margin-right: 0.25rem !important; }
+    .mb-xl-1,
+    .my-xl-1 {
+        margin-bottom: 0.25rem !important; }
+    .ml-xl-1,
+    .mx-xl-1 {
+        margin-left: 0.25rem !important; }
+    .m-xl-2 {
+        margin: 0.5rem !important; }
+    .mt-xl-2,
+    .my-xl-2 {
+        margin-top: 0.5rem !important; }
+    .mr-xl-2,
+    .mx-xl-2 {
+        margin-right: 0.5rem !important; }
+    .mb-xl-2,
+    .my-xl-2 {
+        margin-bottom: 0.5rem !important; }
+    .ml-xl-2,
+    .mx-xl-2 {
+        margin-left: 0.5rem !important; }
+    .m-xl-3 {
+        margin: 1rem !important; }
+    .mt-xl-3,
+    .my-xl-3 {
+        margin-top: 1rem !important; }
+    .mr-xl-3,
+    .mx-xl-3 {
+        margin-right: 1rem !important; }
+    .mb-xl-3,
+    .my-xl-3 {
+        margin-bottom: 1rem !important; }
+    .ml-xl-3,
+    .mx-xl-3 {
+        margin-left: 1rem !important; }
+    .m-xl-4 {
+        margin: 1.5rem !important; }
+    .mt-xl-4,
+    .my-xl-4 {
+        margin-top: 1.5rem !important; }
+    .mr-xl-4,
+    .mx-xl-4 {
+        margin-right: 1.5rem !important; }
+    .mb-xl-4,
+    .my-xl-4 {
+        margin-bottom: 1.5rem !important; }
+    .ml-xl-4,
+    .mx-xl-4 {
+        margin-left: 1.5rem !important; }
+    .m-xl-5 {
+        margin: 3rem !important; }
+    .mt-xl-5,
+    .my-xl-5 {
+        margin-top: 3rem !important; }
+    .mr-xl-5,
+    .mx-xl-5 {
+        margin-right: 3rem !important; }
+    .mb-xl-5,
+    .my-xl-5 {
+        margin-bottom: 3rem !important; }
+    .ml-xl-5,
+    .mx-xl-5 {
+        margin-left: 3rem !important; }
+    .p-xl-0 {
+        padding: 0 !important; }
+    .pt-xl-0,
+    .py-xl-0 {
+        padding-top: 0 !important; }
+    .pr-xl-0,
+    .px-xl-0 {
+        padding-right: 0 !important; }
+    .pb-xl-0,
+    .py-xl-0 {
+        padding-bottom: 0 !important; }
+    .pl-xl-0,
+    .px-xl-0 {
+        padding-left: 0 !important; }
+    .p-xl-1 {
+        padding: 0.25rem !important; }
+    .pt-xl-1,
+    .py-xl-1 {
+        padding-top: 0.25rem !important; }
+    .pr-xl-1,
+    .px-xl-1 {
+        padding-right: 0.25rem !important; }
+    .pb-xl-1,
+    .py-xl-1 {
+        padding-bottom: 0.25rem !important; }
+    .pl-xl-1,
+    .px-xl-1 {
+        padding-left: 0.25rem !important; }
+    .p-xl-2 {
+        padding: 0.5rem !important; }
+    .pt-xl-2,
+    .py-xl-2 {
+        padding-top: 0.5rem !important; }
+    .pr-xl-2,
+    .px-xl-2 {
+        padding-right: 0.5rem !important; }
+    .pb-xl-2,
+    .py-xl-2 {
+        padding-bottom: 0.5rem !important; }
+    .pl-xl-2,
+    .px-xl-2 {
+        padding-left: 0.5rem !important; }
+    .p-xl-3 {
+        padding: 1rem !important; }
+    .pt-xl-3,
+    .py-xl-3 {
+        padding-top: 1rem !important; }
+    .pr-xl-3,
+    .px-xl-3 {
+        padding-right: 1rem !important; }
+    .pb-xl-3,
+    .py-xl-3 {
+        padding-bottom: 1rem !important; }
+    .pl-xl-3,
+    .px-xl-3 {
+        padding-left: 1rem !important; }
+    .p-xl-4 {
+        padding: 1.5rem !important; }
+    .pt-xl-4,
+    .py-xl-4 {
+        padding-top: 1.5rem !important; }
+    .pr-xl-4,
+    .px-xl-4 {
+        padding-right: 1.5rem !important; }
+    .pb-xl-4,
+    .py-xl-4 {
+        padding-bottom: 1.5rem !important; }
+    .pl-xl-4,
+    .px-xl-4 {
+        padding-left: 1.5rem !important; }
+    .p-xl-5 {
+        padding: 3rem !important; }
+    .pt-xl-5,
+    .py-xl-5 {
+        padding-top: 3rem !important; }
+    .pr-xl-5,
+    .px-xl-5 {
+        padding-right: 3rem !important; }
+    .pb-xl-5,
+    .py-xl-5 {
+        padding-bottom: 3rem !important; }
+    .pl-xl-5,
+    .px-xl-5 {
+        padding-left: 3rem !important; }
+    .m-xl-n1 {
+        margin: -0.25rem !important; }
+    .mt-xl-n1,
+    .my-xl-n1 {
+        margin-top: -0.25rem !important; }
+    .mr-xl-n1,
+    .mx-xl-n1 {
+        margin-right: -0.25rem !important; }
+    .mb-xl-n1,
+    .my-xl-n1 {
+        margin-bottom: -0.25rem !important; }
+    .ml-xl-n1,
+    .mx-xl-n1 {
+        margin-left: -0.25rem !important; }
+    .m-xl-n2 {
+        margin: -0.5rem !important; }
+    .mt-xl-n2,
+    .my-xl-n2 {
+        margin-top: -0.5rem !important; }
+    .mr-xl-n2,
+    .mx-xl-n2 {
+        margin-right: -0.5rem !important; }
+    .mb-xl-n2,
+    .my-xl-n2 {
+        margin-bottom: -0.5rem !important; }
+    .ml-xl-n2,
+    .mx-xl-n2 {
+        margin-left: -0.5rem !important; }
+    .m-xl-n3 {
+        margin: -1rem !important; }
+    .mt-xl-n3,
+    .my-xl-n3 {
+        margin-top: -1rem !important; }
+    .mr-xl-n3,
+    .mx-xl-n3 {
+        margin-right: -1rem !important; }
+    .mb-xl-n3,
+    .my-xl-n3 {
+        margin-bottom: -1rem !important; }
+    .ml-xl-n3,
+    .mx-xl-n3 {
+        margin-left: -1rem !important; }
+    .m-xl-n4 {
+        margin: -1.5rem !important; }
+    .mt-xl-n4,
+    .my-xl-n4 {
+        margin-top: -1.5rem !important; }
+    .mr-xl-n4,
+    .mx-xl-n4 {
+        margin-right: -1.5rem !important; }
+    .mb-xl-n4,
+    .my-xl-n4 {
+        margin-bottom: -1.5rem !important; }
+    .ml-xl-n4,
+    .mx-xl-n4 {
+        margin-left: -1.5rem !important; }
+    .m-xl-n5 {
+        margin: -3rem !important; }
+    .mt-xl-n5,
+    .my-xl-n5 {
+        margin-top: -3rem !important; }
+    .mr-xl-n5,
+    .mx-xl-n5 {
+        margin-right: -3rem !important; }
+    .mb-xl-n5,
+    .my-xl-n5 {
+        margin-bottom: -3rem !important; }
+    .ml-xl-n5,
+    .mx-xl-n5 {
+        margin-left: -3rem !important; }
+    .m-xl-auto {
+        margin: auto !important; }
+    .mt-xl-auto,
+    .my-xl-auto {
+        margin-top: auto !important; }
+    .mr-xl-auto,
+    .mx-xl-auto {
+        margin-right: auto !important; }
+    .mb-xl-auto,
+    .my-xl-auto {
+        margin-bottom: auto !important; }
+    .ml-xl-auto,
+    .mx-xl-auto {
+        margin-left: auto !important; } }
+
+@media (min-width: 1600px) {
+    .m-xlg-0 {
+        margin: 0 !important; }
+    .mt-xlg-0,
+    .my-xlg-0 {
+        margin-top: 0 !important; }
+    .mr-xlg-0,
+    .mx-xlg-0 {
+        margin-right: 0 !important; }
+    .mb-xlg-0,
+    .my-xlg-0 {
+        margin-bottom: 0 !important; }
+    .ml-xlg-0,
+    .mx-xlg-0 {
+        margin-left: 0 !important; }
+    .m-xlg-1 {
+        margin: 0.25rem !important; }
+    .mt-xlg-1,
+    .my-xlg-1 {
+        margin-top: 0.25rem !important; }
+    .mr-xlg-1,
+    .mx-xlg-1 {
+        margin-right: 0.25rem !important; }
+    .mb-xlg-1,
+    .my-xlg-1 {
+        margin-bottom: 0.25rem !important; }
+    .ml-xlg-1,
+    .mx-xlg-1 {
+        margin-left: 0.25rem !important; }
+    .m-xlg-2 {
+        margin: 0.5rem !important; }
+    .mt-xlg-2,
+    .my-xlg-2 {
+        margin-top: 0.5rem !important; }
+    .mr-xlg-2,
+    .mx-xlg-2 {
+        margin-right: 0.5rem !important; }
+    .mb-xlg-2,
+    .my-xlg-2 {
+        margin-bottom: 0.5rem !important; }
+    .ml-xlg-2,
+    .mx-xlg-2 {
+        margin-left: 0.5rem !important; }
+    .m-xlg-3 {
+        margin: 1rem !important; }
+    .mt-xlg-3,
+    .my-xlg-3 {
+        margin-top: 1rem !important; }
+    .mr-xlg-3,
+    .mx-xlg-3 {
+        margin-right: 1rem !important; }
+    .mb-xlg-3,
+    .my-xlg-3 {
+        margin-bottom: 1rem !important; }
+    .ml-xlg-3,
+    .mx-xlg-3 {
+        margin-left: 1rem !important; }
+    .m-xlg-4 {
+        margin: 1.5rem !important; }
+    .mt-xlg-4,
+    .my-xlg-4 {
+        margin-top: 1.5rem !important; }
+    .mr-xlg-4,
+    .mx-xlg-4 {
+        margin-right: 1.5rem !important; }
+    .mb-xlg-4,
+    .my-xlg-4 {
+        margin-bottom: 1.5rem !important; }
+    .ml-xlg-4,
+    .mx-xlg-4 {
+        margin-left: 1.5rem !important; }
+    .m-xlg-5 {
+        margin: 3rem !important; }
+    .mt-xlg-5,
+    .my-xlg-5 {
+        margin-top: 3rem !important; }
+    .mr-xlg-5,
+    .mx-xlg-5 {
+        margin-right: 3rem !important; }
+    .mb-xlg-5,
+    .my-xlg-5 {
+        margin-bottom: 3rem !important; }
+    .ml-xlg-5,
+    .mx-xlg-5 {
+        margin-left: 3rem !important; }
+    .p-xlg-0 {
+        padding: 0 !important; }
+    .pt-xlg-0,
+    .py-xlg-0 {
+        padding-top: 0 !important; }
+    .pr-xlg-0,
+    .px-xlg-0 {
+        padding-right: 0 !important; }
+    .pb-xlg-0,
+    .py-xlg-0 {
+        padding-bottom: 0 !important; }
+    .pl-xlg-0,
+    .px-xlg-0 {
+        padding-left: 0 !important; }
+    .p-xlg-1 {
+        padding: 0.25rem !important; }
+    .pt-xlg-1,
+    .py-xlg-1 {
+        padding-top: 0.25rem !important; }
+    .pr-xlg-1,
+    .px-xlg-1 {
+        padding-right: 0.25rem !important; }
+    .pb-xlg-1,
+    .py-xlg-1 {
+        padding-bottom: 0.25rem !important; }
+    .pl-xlg-1,
+    .px-xlg-1 {
+        padding-left: 0.25rem !important; }
+    .p-xlg-2 {
+        padding: 0.5rem !important; }
+    .pt-xlg-2,
+    .py-xlg-2 {
+        padding-top: 0.5rem !important; }
+    .pr-xlg-2,
+    .px-xlg-2 {
+        padding-right: 0.5rem !important; }
+    .pb-xlg-2,
+    .py-xlg-2 {
+        padding-bottom: 0.5rem !important; }
+    .pl-xlg-2,
+    .px-xlg-2 {
+        padding-left: 0.5rem !important; }
+    .p-xlg-3 {
+        padding: 1rem !important; }
+    .pt-xlg-3,
+    .py-xlg-3 {
+        padding-top: 1rem !important; }
+    .pr-xlg-3,
+    .px-xlg-3 {
+        padding-right: 1rem !important; }
+    .pb-xlg-3,
+    .py-xlg-3 {
+        padding-bottom: 1rem !important; }
+    .pl-xlg-3,
+    .px-xlg-3 {
+        padding-left: 1rem !important; }
+    .p-xlg-4 {
+        padding: 1.5rem !important; }
+    .pt-xlg-4,
+    .py-xlg-4 {
+        padding-top: 1.5rem !important; }
+    .pr-xlg-4,
+    .px-xlg-4 {
+        padding-right: 1.5rem !important; }
+    .pb-xlg-4,
+    .py-xlg-4 {
+        padding-bottom: 1.5rem !important; }
+    .pl-xlg-4,
+    .px-xlg-4 {
+        padding-left: 1.5rem !important; }
+    .p-xlg-5 {
+        padding: 3rem !important; }
+    .pt-xlg-5,
+    .py-xlg-5 {
+        padding-top: 3rem !important; }
+    .pr-xlg-5,
+    .px-xlg-5 {
+        padding-right: 3rem !important; }
+    .pb-xlg-5,
+    .py-xlg-5 {
+        padding-bottom: 3rem !important; }
+    .pl-xlg-5,
+    .px-xlg-5 {
+        padding-left: 3rem !important; }
+    .m-xlg-n1 {
+        margin: -0.25rem !important; }
+    .mt-xlg-n1,
+    .my-xlg-n1 {
+        margin-top: -0.25rem !important; }
+    .mr-xlg-n1,
+    .mx-xlg-n1 {
+        margin-right: -0.25rem !important; }
+    .mb-xlg-n1,
+    .my-xlg-n1 {
+        margin-bottom: -0.25rem !important; }
+    .ml-xlg-n1,
+    .mx-xlg-n1 {
+        margin-left: -0.25rem !important; }
+    .m-xlg-n2 {
+        margin: -0.5rem !important; }
+    .mt-xlg-n2,
+    .my-xlg-n2 {
+        margin-top: -0.5rem !important; }
+    .mr-xlg-n2,
+    .mx-xlg-n2 {
+        margin-right: -0.5rem !important; }
+    .mb-xlg-n2,
+    .my-xlg-n2 {
+        margin-bottom: -0.5rem !important; }
+    .ml-xlg-n2,
+    .mx-xlg-n2 {
+        margin-left: -0.5rem !important; }
+    .m-xlg-n3 {
+        margin: -1rem !important; }
+    .mt-xlg-n3,
+    .my-xlg-n3 {
+        margin-top: -1rem !important; }
+    .mr-xlg-n3,
+    .mx-xlg-n3 {
+        margin-right: -1rem !important; }
+    .mb-xlg-n3,
+    .my-xlg-n3 {
+        margin-bottom: -1rem !important; }
+    .ml-xlg-n3,
+    .mx-xlg-n3 {
+        margin-left: -1rem !important; }
+    .m-xlg-n4 {
+        margin: -1.5rem !important; }
+    .mt-xlg-n4,
+    .my-xlg-n4 {
+        margin-top: -1.5rem !important; }
+    .mr-xlg-n4,
+    .mx-xlg-n4 {
+        margin-right: -1.5rem !important; }
+    .mb-xlg-n4,
+    .my-xlg-n4 {
+        margin-bottom: -1.5rem !important; }
+    .ml-xlg-n4,
+    .mx-xlg-n4 {
+        margin-left: -1.5rem !important; }
+    .m-xlg-n5 {
+        margin: -3rem !important; }
+    .mt-xlg-n5,
+    .my-xlg-n5 {
+        margin-top: -3rem !important; }
+    .mr-xlg-n5,
+    .mx-xlg-n5 {
+        margin-right: -3rem !important; }
+    .mb-xlg-n5,
+    .my-xlg-n5 {
+        margin-bottom: -3rem !important; }
+    .ml-xlg-n5,
+    .mx-xlg-n5 {
+        margin-left: -3rem !important; }
+    .m-xlg-auto {
+        margin: auto !important; }
+    .mt-xlg-auto,
+    .my-xlg-auto {
+        margin-top: auto !important; }
+    .mr-xlg-auto,
+    .mx-xlg-auto {
+        margin-right: auto !important; }
+    .mb-xlg-auto,
+    .my-xlg-auto {
+        margin-bottom: auto !important; }
+    .ml-xlg-auto,
+    .mx-xlg-auto {
+        margin-left: auto !important; } }
+
+.text-monospace {
+    font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !important; }
+
+.text-justify {
+    text-align: justify !important; }
+
+.text-wrap {
+    white-space: normal !important; }
+
+.text-nowrap {
+    white-space: nowrap !important; }
+
+.text-truncate {
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap; }
+
+.text-left {
+    text-align: left !important; }
+
+.text-right {
+    text-align: right !important; }
+
+.text-center {
+    text-align: center !important; }
+
+@media (min-width: 576px) {
+    .text-sm-left {
+        text-align: left !important; }
+    .text-sm-right {
+        text-align: right !important; }
+    .text-sm-center {
+        text-align: center !important; } }
+
+@media (min-width: 768px) {
+    .text-md-left {
+        text-align: left !important; }
+    .text-md-right {
+        text-align: right !important; }
+    .text-md-center {
+        text-align: center !important; } }
+
+@media (min-width: 992px) {
+    .text-lg-left {
+        text-align: left !important; }
+    .text-lg-right {
+        text-align: right !important; }
+    .text-lg-center {
+        text-align: center !important; } }
+
+@media (min-width: 1200px) {
+    .text-xl-left {
+        text-align: left !important; }
+    .text-xl-right {
+        text-align: right !important; }
+    .text-xl-center {
+        text-align: center !important; } }
+
+@media (min-width: 1600px) {
+    .text-xlg-left {
+        text-align: left !important; }
+    .text-xlg-right {
+        text-align: right !important; }
+    .text-xlg-center {
+        text-align: center !important; } }
+
+
+.d-none {
+    display: none !important; }
+
+.d-inline {
+    display: inline !important; }
+
+.d-inline-block {
+    display: inline-block !important; }
+
+.d-block {
+    display: block !important; }
+
+.d-table {
+    display: table !important; }
+
+.d-table-row {
+    display: table-row !important; }
+
+.d-table-cell {
+    display: table-cell !important; }
+
+.d-flex {
+    display: flex !important; }
+
+.d-inline-flex {
+    display: inline-flex !important; }
+
+@media (min-width: 576px) {
+    .d-sm-none {
+        display: none !important; }
+    .d-sm-inline {
+        display: inline !important; }
+    .d-sm-inline-block {
+        display: inline-block !important; }
+    .d-sm-block {
+        display: block !important; }
+    .d-sm-table {
+        display: table !important; }
+    .d-sm-table-row {
+        display: table-row !important; }
+    .d-sm-table-cell {
+        display: table-cell !important; }
+    .d-sm-flex {
+        display: flex !important; }
+    .d-sm-inline-flex {
+        display: inline-flex !important; } }
+
+@media (min-width: 768px) {
+    .d-md-none {
+        display: none !important; }
+    .d-md-inline {
+        display: inline !important; }
+    .d-md-inline-block {
+        display: inline-block !important; }
+    .d-md-block {
+        display: block !important; }
+    .d-md-table {
+        display: table !important; }
+    .d-md-table-row {
+        display: table-row !important; }
+    .d-md-table-cell {
+        display: table-cell !important; }
+    .d-md-flex {
+        display: flex !important; }
+    .d-md-inline-flex {
+        display: inline-flex !important; } }
+
+@media (min-width: 992px) {
+    .d-lg-none {
+        display: none !important; }
+    .d-lg-inline {
+        display: inline !important; }
+    .d-lg-inline-block {
+        display: inline-block !important; }
+    .d-lg-block {
+        display: block !important; }
+    .d-lg-table {
+        display: table !important; }
+    .d-lg-table-row {
+        display: table-row !important; }
+    .d-lg-table-cell {
+        display: table-cell !important; }
+    .d-lg-flex {
+        display: flex !important; }
+    .d-lg-inline-flex {
+        display: inline-flex !important; } }
+
+@media (min-width: 1200px) {
+    .d-xl-none {
+        display: none !important; }
+    .d-xl-inline {
+        display: inline !important; }
+    .d-xl-inline-block {
+        display: inline-block !important; }
+    .d-xl-block {
+        display: block !important; }
+    .d-xl-table {
+        display: table !important; }
+    .d-xl-table-row {
+        display: table-row !important; }
+    .d-xl-table-cell {
+        display: table-cell !important; }
+    .d-xl-flex {
+        display: flex !important; }
+    .d-xl-inline-flex {
+        display: inline-flex !important; } }
+
+@media (min-width: 1600px) {
+    .d-xlg-none {
+        display: none !important; }
+    .d-xlg-inline {
+        display: inline !important; }
+    .d-xlg-inline-block {
+        display: inline-block !important; }
+    .d-xlg-block {
+        display: block !important; }
+    .d-xlg-table {
+        display: table !important; }
+    .d-xlg-table-row {
+        display: table-row !important; }
+    .d-xlg-table-cell {
+        display: table-cell !important; }
+    .d-xlg-flex {
+        display: flex !important; }
+    .d-xlg-inline-flex {
+        display: inline-flex !important; } }
+
+@media print {
+    .d-print-none {
+        display: none !important; }
+    .d-print-inline {
+        display: inline !important; }
+    .d-print-inline-block {
+        display: inline-block !important; }
+    .d-print-block {
+        display: block !important; }
+    .d-print-table {
+        display: table !important; }
+    .d-print-table-row {
+        display: table-row !important; }
+    .d-print-table-cell {
+        display: table-cell !important; }
+    .d-print-flex {
+        display: flex !important; }
+    .d-print-inline-flex {
+        display: inline-flex !important; } }
+
+.flex-row {
+    flex-direction: row !important; }
+
+.flex-column {
+    flex-direction: column !important; }
+
+.flex-row-reverse {
+    flex-direction: row-reverse !important; }
+
+.flex-column-reverse {
+    flex-direction: column-reverse !important; }
+
+.flex-wrap {
+    flex-wrap: wrap !important; }
+
+.flex-nowrap {
+    flex-wrap: nowrap !important; }
+
+.flex-wrap-reverse {
+    flex-wrap: wrap-reverse !important; }
+
+.flex-fill {
+    flex: 1 1 auto !important; }
+
+.flex-grow-0 {
+    flex-grow: 0 !important; }
+
+.flex-grow-1 {
+    flex-grow: 1 !important; }
+
+.flex-shrink-0 {
+    flex-shrink: 0 !important; }
+
+.flex-shrink-1 {
+    flex-shrink: 1 !important; }
+
+.justify-content-start {
+    justify-content: flex-start !important; }
+
+.justify-content-end {
+    justify-content: flex-end !important; }
+
+.justify-content-center {
+    justify-content: center !important; }
+
+.justify-content-between {
+    justify-content: space-between !important; }
+
+.justify-content-around {
+    justify-content: space-around !important; }
+
+.align-items-start {
+    align-items: flex-start !important; }
+
+.align-items-end {
+    align-items: flex-end !important; }
+
+.align-items-center {
+    align-items: center !important; }
+
+.align-items-baseline {
+    align-items: baseline !important; }
+
+.align-items-stretch {
+    align-items: stretch !important; }
+
+.align-content-start {
+    align-content: flex-start !important; }
+
+.align-content-end {
+    align-content: flex-end !important; }
+
+.align-content-center {
+    align-content: center !important; }
+
+.align-content-between {
+    align-content: space-between !important; }
+
+.align-content-around {
+    align-content: space-around !important; }
+
+.align-content-stretch {
+    align-content: stretch !important; }
+
+.align-self-auto {
+    align-self: auto !important; }
+
+.align-self-start {
+    align-self: flex-start !important; }
+
+.align-self-end {
+    align-self: flex-end !important; }
+
+.align-self-center {
+    align-self: center !important; }
+
+.align-self-baseline {
+    align-self: baseline !important; }
+
+.align-self-stretch {
+    align-self: stretch !important; }
+
+@media (min-width: 576px) {
+    .flex-sm-row {
+        flex-direction: row !important; }
+    .flex-sm-column {
+        flex-direction: column !important; }
+    .flex-sm-row-reverse {
+        flex-direction: row-reverse !important; }
+    .flex-sm-column-reverse {
+        flex-direction: column-reverse !important; }
+    .flex-sm-wrap {
+        flex-wrap: wrap !important; }
+    .flex-sm-nowrap {
+        flex-wrap: nowrap !important; }
+    .flex-sm-wrap-reverse {
+        flex-wrap: wrap-reverse !important; }
+    .flex-sm-fill {
+        flex: 1 1 auto !important; }
+    .flex-sm-grow-0 {
+        flex-grow: 0 !important; }
+    .flex-sm-grow-1 {
+        flex-grow: 1 !important; }
+    .flex-sm-shrink-0 {
+        flex-shrink: 0 !important; }
+    .flex-sm-shrink-1 {
+        flex-shrink: 1 !important; }
+    .justify-content-sm-start {
+        justify-content: flex-start !important; }
+    .justify-content-sm-end {
+        justify-content: flex-end !important; }
+    .justify-content-sm-center {
+        justify-content: center !important; }
+    .justify-content-sm-between {
+        justify-content: space-between !important; }
+    .justify-content-sm-around {
+        justify-content: space-around !important; }
+    .align-items-sm-start {
+        align-items: flex-start !important; }
+    .align-items-sm-end {
+        align-items: flex-end !important; }
+    .align-items-sm-center {
+        align-items: center !important; }
+    .align-items-sm-baseline {
+        align-items: baseline !important; }
+    .align-items-sm-stretch {
+        align-items: stretch !important; }
+    .align-content-sm-start {
+        align-content: flex-start !important; }
+    .align-content-sm-end {
+        align-content: flex-end !important; }
+    .align-content-sm-center {
+        align-content: center !important; }
+    .align-content-sm-between {
+        align-content: space-between !important; }
+    .align-content-sm-around {
+        align-content: space-around !important; }
+    .align-content-sm-stretch {
+        align-content: stretch !important; }
+    .align-self-sm-auto {
+        align-self: auto !important; }
+    .align-self-sm-start {
+        align-self: flex-start !important; }
+    .align-self-sm-end {
+        align-self: flex-end !important; }
+    .align-self-sm-center {
+        align-self: center !important; }
+    .align-self-sm-baseline {
+        align-self: baseline !important; }
+    .align-self-sm-stretch {
+        align-self: stretch !important; } }
+
+@media (min-width: 768px) {
+    .flex-md-row {
+        flex-direction: row !important; }
+    .flex-md-column {
+        flex-direction: column !important; }
+    .flex-md-row-reverse {
+        flex-direction: row-reverse !important; }
+    .flex-md-column-reverse {
+        flex-direction: column-reverse !important; }
+    .flex-md-wrap {
+        flex-wrap: wrap !important; }
+    .flex-md-nowrap {
+        flex-wrap: nowrap !important; }
+    .flex-md-wrap-reverse {
+        flex-wrap: wrap-reverse !important; }
+    .flex-md-fill {
+        flex: 1 1 auto !important; }
+    .flex-md-grow-0 {
+        flex-grow: 0 !important; }
+    .flex-md-grow-1 {
+        flex-grow: 1 !important; }
+    .flex-md-shrink-0 {
+        flex-shrink: 0 !important; }
+    .flex-md-shrink-1 {
+        flex-shrink: 1 !important; }
+    .justify-content-md-start {
+        justify-content: flex-start !important; }
+    .justify-content-md-end {
+        justify-content: flex-end !important; }
+    .justify-content-md-center {
+        justify-content: center !important; }
+    .justify-content-md-between {
+        justify-content: space-between !important; }
+    .justify-content-md-around {
+        justify-content: space-around !important; }
+    .align-items-md-start {
+        align-items: flex-start !important; }
+    .align-items-md-end {
+        align-items: flex-end !important; }
+    .align-items-md-center {
+        align-items: center !important; }
+    .align-items-md-baseline {
+        align-items: baseline !important; }
+    .align-items-md-stretch {
+        align-items: stretch !important; }
+    .align-content-md-start {
+        align-content: flex-start !important; }
+    .align-content-md-end {
+        align-content: flex-end !important; }
+    .align-content-md-center {
+        align-content: center !important; }
+    .align-content-md-between {
+        align-content: space-between !important; }
+    .align-content-md-around {
+        align-content: space-around !important; }
+    .align-content-md-stretch {
+        align-content: stretch !important; }
+    .align-self-md-auto {
+        align-self: auto !important; }
+    .align-self-md-start {
+        align-self: flex-start !important; }
+    .align-self-md-end {
+        align-self: flex-end !important; }
+    .align-self-md-center {
+        align-self: center !important; }
+    .align-self-md-baseline {
+        align-self: baseline !important; }
+    .align-self-md-stretch {
+        align-self: stretch !important; } }
+
+@media (min-width: 992px) {
+    .flex-lg-row {
+        flex-direction: row !important; }
+    .flex-lg-column {
+        flex-direction: column !important; }
+    .flex-lg-row-reverse {
+        flex-direction: row-reverse !important; }
+    .flex-lg-column-reverse {
+        flex-direction: column-reverse !important; }
+    .flex-lg-wrap {
+        flex-wrap: wrap !important; }
+    .flex-lg-nowrap {
+        flex-wrap: nowrap !important; }
+    .flex-lg-wrap-reverse {
+        flex-wrap: wrap-reverse !important; }
+    .flex-lg-fill {
+        flex: 1 1 auto !important; }
+    .flex-lg-grow-0 {
+        flex-grow: 0 !important; }
+    .flex-lg-grow-1 {
+        flex-grow: 1 !important; }
+    .flex-lg-shrink-0 {
+        flex-shrink: 0 !important; }
+    .flex-lg-shrink-1 {
+        flex-shrink: 1 !important; }
+    .justify-content-lg-start {
+        justify-content: flex-start !important; }
+    .justify-content-lg-end {
+        justify-content: flex-end !important; }
+    .justify-content-lg-center {
+        justify-content: center !important; }
+    .justify-content-lg-between {
+        justify-content: space-between !important; }
+    .justify-content-lg-around {
+        justify-content: space-around !important; }
+    .align-items-lg-start {
+        align-items: flex-start !important; }
+    .align-items-lg-end {
+        align-items: flex-end !important; }
+    .align-items-lg-center {
+        align-items: center !important; }
+    .align-items-lg-baseline {
+        align-items: baseline !important; }
+    .align-items-lg-stretch {
+        align-items: stretch !important; }
+    .align-content-lg-start {
+        align-content: flex-start !important; }
+    .align-content-lg-end {
+        align-content: flex-end !important; }
+    .align-content-lg-center {
+        align-content: center !important; }
+    .align-content-lg-between {
+        align-content: space-between !important; }
+    .align-content-lg-around {
+        align-content: space-around !important; }
+    .align-content-lg-stretch {
+        align-content: stretch !important; }
+    .align-self-lg-auto {
+        align-self: auto !important; }
+    .align-self-lg-start {
+        align-self: flex-start !important; }
+    .align-self-lg-end {
+        align-self: flex-end !important; }
+    .align-self-lg-center {
+        align-self: center !important; }
+    .align-self-lg-baseline {
+        align-self: baseline !important; }
+    .align-self-lg-stretch {
+        align-self: stretch !important; } }
+
+@media (min-width: 1200px) {
+    .flex-xl-row {
+        flex-direction: row !important; }
+    .flex-xl-column {
+        flex-direction: column !important; }
+    .flex-xl-row-reverse {
+        flex-direction: row-reverse !important; }
+    .flex-xl-column-reverse {
+        flex-direction: column-reverse !important; }
+    .flex-xl-wrap {
+        flex-wrap: wrap !important; }
+    .flex-xl-nowrap {
+        flex-wrap: nowrap !important; }
+    .flex-xl-wrap-reverse {
+        flex-wrap: wrap-reverse !important; }
+    .flex-xl-fill {
+        flex: 1 1 auto !important; }
+    .flex-xl-grow-0 {
+        flex-grow: 0 !important; }
+    .flex-xl-grow-1 {
+        flex-grow: 1 !important; }
+    .flex-xl-shrink-0 {
+        flex-shrink: 0 !important; }
+    .flex-xl-shrink-1 {
+        flex-shrink: 1 !important; }
+    .justify-content-xl-start {
+        justify-content: flex-start !important; }
+    .justify-content-xl-end {
+        justify-content: flex-end !important; }
+    .justify-content-xl-center {
+        justify-content: center !important; }
+    .justify-content-xl-between {
+        justify-content: space-between !important; }
+    .justify-content-xl-around {
+        justify-content: space-around !important; }
+    .align-items-xl-start {
+        align-items: flex-start !important; }
+    .align-items-xl-end {
+        align-items: flex-end !important; }
+    .align-items-xl-center {
+        align-items: center !important; }
+    .align-items-xl-baseline {
+        align-items: baseline !important; }
+    .align-items-xl-stretch {
+        align-items: stretch !important; }
+    .align-content-xl-start {
+        align-content: flex-start !important; }
+    .align-content-xl-end {
+        align-content: flex-end !important; }
+    .align-content-xl-center {
+        align-content: center !important; }
+    .align-content-xl-between {
+        align-content: space-between !important; }
+    .align-content-xl-around {
+        align-content: space-around !important; }
+    .align-content-xl-stretch {
+        align-content: stretch !important; }
+    .align-self-xl-auto {
+        align-self: auto !important; }
+    .align-self-xl-start {
+        align-self: flex-start !important; }
+    .align-self-xl-end {
+        align-self: flex-end !important; }
+    .align-self-xl-center {
+        align-self: center !important; }
+    .align-self-xl-baseline {
+        align-self: baseline !important; }
+    .align-self-xl-stretch {
+        align-self: stretch !important; } }
+
+@media (min-width: 1600px) {
+    .flex-xlg-row {
+        flex-direction: row !important; }
+    .flex-xlg-column {
+        flex-direction: column !important; }
+    .flex-xlg-row-reverse {
+        flex-direction: row-reverse !important; }
+    .flex-xlg-column-reverse {
+        flex-direction: column-reverse !important; }
+    .flex-xlg-wrap {
+        flex-wrap: wrap !important; }
+    .flex-xlg-nowrap {
+        flex-wrap: nowrap !important; }
+    .flex-xlg-wrap-reverse {
+        flex-wrap: wrap-reverse !important; }
+    .flex-xlg-fill {
+        flex: 1 1 auto !important; }
+    .flex-xlg-grow-0 {
+        flex-grow: 0 !important; }
+    .flex-xlg-grow-1 {
+        flex-grow: 1 !important; }
+    .flex-xlg-shrink-0 {
+        flex-shrink: 0 !important; }
+    .flex-xlg-shrink-1 {
+        flex-shrink: 1 !important; }
+    .justify-content-xlg-start {
+        justify-content: flex-start !important; }
+    .justify-content-xlg-end {
+        justify-content: flex-end !important; }
+    .justify-content-xlg-center {
+        justify-content: center !important; }
+    .justify-content-xlg-between {
+        justify-content: space-between !important; }
+    .justify-content-xlg-around {
+        justify-content: space-around !important; }
+    .align-items-xlg-start {
+        align-items: flex-start !important; }
+    .align-items-xlg-end {
+        align-items: flex-end !important; }
+    .align-items-xlg-center {
+        align-items: center !important; }
+    .align-items-xlg-baseline {
+        align-items: baseline !important; }
+    .align-items-xlg-stretch {
+        align-items: stretch !important; }
+    .align-content-xlg-start {
+        align-content: flex-start !important; }
+    .align-content-xlg-end {
+        align-content: flex-end !important; }
+    .align-content-xlg-center {
+        align-content: center !important; }
+    .align-content-xlg-between {
+        align-content: space-between !important; }
+    .align-content-xlg-around {
+        align-content: space-around !important; }
+    .align-content-xlg-stretch {
+        align-content: stretch !important; }
+    .align-self-xlg-auto {
+        align-self: auto !important; }
+    .align-self-xlg-start {
+        align-self: flex-start !important; }
+    .align-self-xlg-end {
+        align-self: flex-end !important; }
+    .align-self-xlg-center {
+        align-self: center !important; }
+    .align-self-xlg-baseline {
+        align-self: baseline !important; }
+    .align-self-xlg-stretch {
+        align-self: stretch !important; } }
+
+.card {
+    margin-bottom: 30px; }
+.card .card-subtitle {
+    font-weight: 300;
+    margin-bottom: 10px;
+    color: #8898aa; }
+.card .card-title {
+    position: relative;
+    font-weight: 500;
+    font-size: 16px; }
+.card .card-actions {
+    float: right; }
+.card .card-actions a {
+    padding: 0 5px;
+    cursor: pointer; }
+.card .card-header .card-title {
+    margin-bottom: 0px; }
+
+.card-alt {
+    margin: 0 -20px;
+    background: #e4e9ef; }
+
+.card-group {
+    margin-bottom: 30px; }
+.card-group .card {
+    border-right: 1px solid rgba(120, 130, 140, 0.13); }
+
+.card-fullscreen {
+    position: fixed;
+    top: 0px;
+    left: 0px;
+    width: 100%;
+    height: 100%;
+    z-index: 9999;
+    overflow: auto; }
+
+.card-hover {
+    -webkit-transition: all 0.25s ease;
+    -o-transition: all 0.25s ease;
+    -moz-transition: all 0.25s ease;
+    transition: all 0.25s ease; }
+.card-hover:hover {
+    -webkit-transform: translateY(-4px) scale(1.01);
+    -moz-transform: translateY(-4px) scale(1.01);
+    -ms-transform: translateY(-4px) scale(1.01);
+    -o-transform: translateY(-4px) scale(1.01);
+    transform: translateY(-4px) scale(1.01);
+    -webkit-box-shadow: 0 14px 24px rgba(62, 57, 107, 0.1);
+    box-shadow: 0 14px 24px rgba(62, 57, 107, 0.1); }
+
+.draggable-cards .card-header {
+    cursor: move; }
+
+.card-moved .card {
+    background: #2cabe3;
+    color: #fff; }
+
+.accordion .card,
+.accordion .btn,
+.accordion .btn:hover {
+    box-shadow: none;
+    margin-bottom: 1px; }
+
+.card {
+    position: relative;
+    display: flex;
+    flex-direction: column;
+    min-width: 0;
+    word-wrap: break-word;
+    background-color: inherit;
+    background-clip: border-box;
+    border: 0px solid transparent;
+    border-radius: 0px; }
+.card > hr {
+    margin-right: 0;
+    margin-left: 0; }
+.card > .list-group:first-child .list-group-item:first-child {
+    border-top-left-radius: 0px;
+    border-top-right-radius: 0px; }
+.card > .list-group:last-child .list-group-item:last-child {
+    border-bottom-right-radius: 0px;
+    border-bottom-left-radius: 0px; }
+
+.card-body {
+    flex: 1 1 auto;
+    padding: 1.57rem; }
+
+.card-title {
+    margin-bottom: 0.75rem; }
+
+.card-subtitle {
+    margin-top: -0.375rem;
+    margin-bottom: 0; }
+
+.card-text:last-child {
+    margin-bottom: 0; }
+
+.card-link:hover {
+    text-decoration: none; }
+
+.card-link + .card-link {
+    margin-left: 1.57rem; }
+
+.card-header {
+    padding: 0.75rem 1.57rem;
+    margin-bottom: 0;
+    background-color: rgba(0, 0, 0, 0.03);
+    border-bottom: 0px solid transparent; }
+.card-header:first-child {
+    border-radius: calc(0px - 0px) calc(0px - 0px) 0 0; }
+.card-header + .list-group .list-group-item:first-child {
+    border-top: 0; }
+
+.card-footer {
+    padding: 0.75rem 1.57rem;
+    background-color: rgba(0, 0, 0, 0.03);
+    border-top: 0px solid transparent; }
+.card-footer:last-child {
+    border-radius: 0 0 calc(0px - 0px) calc(0px - 0px); }
+
+.card-header-tabs {
+    margin-right: -0.785rem;
+    margin-bottom: -0.75rem;
+    margin-left: -0.785rem;
+    border-bottom: 0; }
+
+.card-header-pills {
+    margin-right: -0.785rem;
+    margin-left: -0.785rem; }
+
+.card-img-overlay {
+    position: absolute;
+    top: 0;
+    right: 0;
+    bottom: 0;
+    left: 0;
+    padding: 1.25rem; }
+
+.card-img {
+    width: 100%;
+    border-radius: calc(0px - 0px); }
+
+.card-img-top {
+    width: 100%;
+    border-top-left-radius: calc(0px - 0px);
+    border-top-right-radius: calc(0px - 0px); }
+
+.card-img-bottom {
+    width: 100%;
+    border-bottom-right-radius: calc(0px - 0px);
+    border-bottom-left-radius: calc(0px - 0px); }
+
+.card-deck {
+    display: flex;
+    flex-direction: column; }
+.card-deck .card {
+    margin-bottom: 15px; }
+@media (min-width: 576px) {
+    .card-deck {
+        flex-flow: row wrap;
+        margin-right: -15px;
+        margin-left: -15px; }
+    .card-deck .card {
+        display: flex;
+        flex: 1 0 0%;
+        flex-direction: column;
+        margin-right: 15px;
+        margin-bottom: 0;
+        margin-left: 15px; } }
+
+.card-group {
+    display: flex;
+    flex-direction: column; }
+.card-group > .card {
+    margin-bottom: 15px; }
+@media (min-width: 576px) {
+    .card-group {
+        flex-flow: row wrap; }
+    .card-group > .card {
+        flex: 1 0 0%;
+        margin-bottom: 0; }
+    .card-group > .card + .card {
+        margin-left: 0;
+        border-left: 0; }
+    .card-group > .card:not(:last-child) {
+        border-top-right-radius: 0;
+        border-bottom-right-radius: 0; }
+    .card-group > .card:not(:last-child) .card-img-top,
+    .card-group > .card:not(:last-child) .card-header {
+        border-top-right-radius: 0; }
+    .card-group > .card:not(:last-child) .card-img-bottom,
+    .card-group > .card:not(:last-child) .card-footer {
+        border-bottom-right-radius: 0; }
+    .card-group > .card:not(:first-child) {
+        border-top-left-radius: 0;
+        border-bottom-left-radius: 0; }
+    .card-group > .card:not(:first-child) .card-img-top,
+    .card-group > .card:not(:first-child) .card-header {
+        border-top-left-radius: 0; }
+    .card-group > .card:not(:first-child) .card-img-bottom,
+    .card-group > .card:not(:first-child) .card-footer {
+        border-bottom-left-radius: 0; } }
+
+.card-columns .card {
+    margin-bottom: 0.75rem; }
+
+@media (min-width: 576px) {
+    .card-columns {
+        column-count: 3;
+        column-gap: 1.25rem;
+        orphans: 1;
+        widows: 1; }
+    .card-columns .card {
+        display: inline-block;
+        width: 100%; } }
+
+.accordion > .card {
+    overflow: hidden; }
+.accordion > .card:not(:first-of-type) .card-header:first-child {
+    border-radius: 0; }
+.accordion > .card:not(:first-of-type):not(:last-of-type) {
+    border-bottom: 0;
+    border-radius: 0; }
+.accordion > .card:first-of-type {
+    border-bottom: 0;
+    border-bottom-right-radius: 0;
+    border-bottom-left-radius: 0; }
+.accordion > .card:last-of-type {
+    border-top-left-radius: 0;
+    border-top-right-radius: 0; }
+.accordion > .card .card-header {
+    margin-bottom: 0px; }
+
+@keyframes spinner-border {
+    to {
+        transform: rotate(360deg); } }
+
+.spinner-border {
+    display: inline-block;
+    width: 2rem;
+    height: 2rem;
+    vertical-align: text-bottom;
+    border: 0.25em solid currentColor;
+    border-right-color: transparent;
+    border-radius: 50%;
+    animation: spinner-border .75s linear infinite; }
+
+.spinner-border-sm {
+    width: 1rem;
+    height: 1rem;
+    border-width: 0.2em; }
+
+@keyframes spinner-grow {
+    0% {
+        transform: scale(0); }
+    50% {
+        opacity: 1; } }
+
+.spinner-grow {
+    display: inline-block;
+    width: 2rem;
+    height: 2rem;
+    vertical-align: text-bottom;
+    background-color: currentColor;
+    border-radius: 50%;
+    opacity: 0;
+    animation: spinner-grow .75s linear infinite; }
+
+.spinner-grow-sm {
+    width: 1rem;
+    height: 1rem; }

File diff suppressed because it is too large
+ 0 - 0
css/organizr.min.css


+ 5 - 4
index.php

@@ -6,10 +6,11 @@
     <meta charset="utf-8">
     <meta content="IE=edge" http-equiv="X-UA-Compatible">
     <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
-    <meta content="Organizr - Accept no others" name="description">
+    <meta content="<?php echo $GLOBALS['organizrIndexDescription']; ?>"
+          name="description">
     <meta content="CauseFX" name="author">
 	<?php echo favIcons(); ?>
-    <title>Organizr v2</title>
+    <title><?php echo $GLOBALS['organizrIndexTitle']; ?></title>
     <link href="bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
     <link href="plugins/bower_components/sidebar-nav/dist/sidebar-nav.min.css" rel="stylesheet">
     <link href="plugins/bower_components/jquery-wizard-master/css/wizard.css" rel="stylesheet">
@@ -17,7 +18,7 @@
     <link href="plugins/bower_components/jquery-wizard-master/libs/formvalidation/formValidation.min.css"
           rel="stylesheet">
     <link href="plugins/bower_components/Magnific-Popup-master/dist/magnific-popup.css" rel="stylesheet">
-    <link href="plugins/bower_components/sweetalert/sweetalert.css" rel="stylesheet" type="text/css">
+    <!--<link href="plugins/bower_components/sweetalert/sweetalert.css" rel="stylesheet" type="text/css">-->
     <link href="plugins/bower_components/switchery/dist/switchery.min.css" rel="stylesheet"/>
     <link href="plugins/bower_components/dropzone-master/dist/dropzone.css" rel="stylesheet" type="text/css"/>
     <link href="plugins/bower_components/css-chart/css-chart.css" rel="stylesheet">
@@ -272,7 +273,7 @@
 <script src="plugins/bower_components/datatables/jquery.dataTables.min.js"></script>
 <script src="plugins/bower_components/datatables-plugins/sorting/datetime-moment.js"></script>
 <script src="plugins/bower_components/Magnific-Popup-master/dist/jquery.magnific-popup.min.js"></script>
-<script src="plugins/bower_components/sweetalert/sweetalert.min.js"></script>
+<script src="plugins/bower_components/sweetalert/sweetalert.min.js?v=<?php echo $GLOBALS['fileHash']; ?>"></script>
 <script src="plugins/bower_components/switchery/dist/switchery.min.js"></script>
 <script src="js/tinycolor.min.js"></script>
 <script src="plugins/bower_components/bootstrap-colorpicker-sliders/bootstrap.colorpickersliders.min.js"></script>

+ 96 - 67
js/custom.js

@@ -504,20 +504,33 @@ $(document).on("click", ".changeDefaultGroup", function () {
 });
 //DELETE GROUP
 $(document).on("click", ".deleteUserGroup", function () {
-    //Create POST Array
-    var post = {
-        action:'deleteUserGroup',
-        api:'api/?v1/settings/user/manage/groups',
-        id:$(this).parent().parent().attr("data-id"),
-        groupID:$(this).parent().parent().attr("data-group-id"),
-        groupName:$(this).parent().parent().attr("data-group"),
-        messageTitle:'',
-        messageBody:'Deleted User Group '+$(this).parent().parent().attr("data-group"),
-        error:'Organizr Function: User Group API Connection Failed'
-    };
-    var callbacks = $.Callbacks();
-    callbacks.add( buildGroupManagement );
-    settingsAPI(post,callbacks);
+    var group = $(this);
+    swal({
+        title: window.lang.translate('Delete ')+group.parent().parent().attr("data-group")+'?',
+        icon: "warning",
+        buttons: {
+            cancel: window.lang.translate('No'),
+            confirm: window.lang.translate('Yes'),
+        },
+        dangerMode: true,
+        confirmButtonColor: "#DD6B55"
+    }).then(function(willDelete) {
+        if (willDelete) {
+            var post = {
+                action:'deleteUserGroup',
+                api:'api/?v1/settings/user/manage/groups',
+                id:group.parent().parent().attr("data-id"),
+                groupID:group.parent().parent().attr("data-group-id"),
+                groupName:group.parent().parent().attr("data-group"),
+                messageTitle:'',
+                messageBody:'Deleted User Group '+group.parent().parent().attr("data-group"),
+                error:'Organizr Function: User Group API Connection Failed'
+            };
+            var callbacks = $.Callbacks();
+            callbacks.add( buildGroupManagement );
+            settingsAPI(post,callbacks);
+        }
+    });
 });
 //ADD GROUP
 $(document).on("click", ".addNewGroup", function () {
@@ -644,16 +657,15 @@ $(document).on("click", ".deleteUser", function () {
     var user = $(this);
     swal({
         title: window.lang.translate('Delete ')+user.parent().parent().attr("data-username")+'?',
-        type: "warning",
-        showCancelButton: true,
-        confirmButtonColor: "#DD6B55",
-        confirmButtonText: window.lang.translate('Yes'),
-        cancelButtonText: window.lang.translate('No'),
-        closeOnConfirm: true,
-        closeOnCancel: true
-    }, function(isConfirm){
-        if (isConfirm) {
-            //Create POST Array
+        icon: "warning",
+        buttons: {
+            cancel: window.lang.translate('No'),
+            confirm: window.lang.translate('Yes'),
+        },
+        dangerMode: true,
+        confirmButtonColor: "#DD6B55"
+    }).then(function(willDelete) {
+        if (willDelete) {
             var post = {
                 action:'deleteUser',
                 api:'api/?v1/settings/user/manage/users',
@@ -668,7 +680,6 @@ $(document).on("click", ".deleteUser", function () {
             settingsAPI(post,callbacks);
         }
     });
-
 });
 // CHANGE TAB GROUP
 $(document).on("change", ".tabGroupSelect", function () {
@@ -817,17 +828,16 @@ $(document).on("change", ".defaultSwitch", function () {
 $(document).on("click", ".deleteTab", function () {
     var user = $(this);
     swal({
-        title: window.lang.translate('Delete ')+user.parent().parent().attr("data-name")+'?',
-        type: "warning",
-        showCancelButton: true,
-        confirmButtonColor: "#DD6B55",
-        confirmButtonText: window.lang.translate('Yes'),
-        cancelButtonText: window.lang.translate('No'),
-        closeOnConfirm: true,
-        closeOnCancel: true
-    }, function(isConfirm){
-        if (isConfirm) {
-            //Create POST Array
+        title: window.lang.translate('Delete ') + user.parent().parent().attr("data-name") + '?',
+        icon: "warning",
+        buttons: {
+            cancel: window.lang.translate('No'),
+            confirm: window.lang.translate('Yes'),
+        },
+        dangerMode: true,
+        confirmButtonColor: "#DD6B55"
+    }).then(function(willDelete) {
+        if (willDelete) {
             var post = {
                 action:'deleteTab',
                 api:'api/?v1/settings/tab/editor/tabs',
@@ -1010,16 +1020,15 @@ $(document).on("click", ".deleteCategory", function () {
     var category = $(this);
     swal({
         title: window.lang.translate('Delete ')+category.parent().parent().attr("data-name")+'?',
-        type: "warning",
-        showCancelButton: true,
-        confirmButtonColor: "#DD6B55",
-        confirmButtonText: window.lang.translate('Yes'),
-        cancelButtonText: window.lang.translate('No'),
-        closeOnConfirm: true,
-        closeOnCancel: true
-    }, function(isConfirm){
-        if (isConfirm) {
-            //Create POST Array
+        icon: "warning",
+        buttons: {
+            cancel: window.lang.translate('No'),
+            confirm: window.lang.translate('Yes'),
+        },
+        dangerMode: true,
+        confirmButtonColor: "#DD6B55"
+    }).then(function(willDelete) {
+        if (willDelete) {
             var post = {
                 action:'deleteCategory',
                 api:'api/?v1/settings/tab/editor/categories',
@@ -1154,16 +1163,15 @@ $(document).on("click", ".deleteImage", function () {
     var image = $(this);
     swal({
         title: window.lang.translate('Delete ')+image.attr("data-image-name")+'?',
-        type: "warning",
-        showCancelButton: true,
-        confirmButtonColor: "#DD6B55",
-        confirmButtonText: window.lang.translate('Yes'),
-        cancelButtonText: window.lang.translate('No'),
-        closeOnConfirm: true,
-        closeOnCancel: true
-    }, function(isConfirm){
-        if (isConfirm) {
-            //Create POST Array
+        icon: "warning",
+        buttons: {
+            cancel: window.lang.translate('No'),
+            confirm: window.lang.translate('Yes'),
+        },
+        dangerMode: true,
+        confirmButtonColor: "#DD6B55"
+    }).then(function(willDelete) {
+        if (willDelete) {
             var post = {
                 action:'deleteImage',
                 api:'api/?v1/settings/image/manager/view',
@@ -1207,16 +1215,15 @@ $(document).on('click', '.disablePlugin', function() {
     var plugin = $(this);
     swal({
         title: window.lang.translate('Disable')+' '+plugin.attr("data-plugin-name")+'?',
-        type: "warning",
-        showCancelButton: true,
-        confirmButtonColor: "#DD6B55",
-        confirmButtonText: window.lang.translate('Yes'),
-        cancelButtonText: window.lang.translate('No'),
-        closeOnConfirm: true,
-        closeOnCancel: true
-    }, function(isConfirm){
-        if (isConfirm) {
-            //Create POST Array
+        icon: "warning",
+        buttons: {
+            cancel: window.lang.translate('No'),
+            confirm: window.lang.translate('Yes'),
+        },
+        dangerMode: true,
+        confirmButtonColor: "#DD6B55"
+    }).then(function(willDelete) {
+        if (willDelete) {
             var post = {
                 action:'disable',
                 api:'api/?v1/settings/plugins/list',
@@ -1234,7 +1241,6 @@ $(document).on('click', '.disablePlugin', function() {
             setTimeout(function(){ buildPlugins();ajaxloader(); }, 3000);
         }
     });
-
 });
 // AUTH BACKEND HIDE SHOW
 $(document).on('change', '#authSelect, #authBackendSelect', function(e) {
@@ -1791,6 +1797,19 @@ Mousetrap.bind("d d", function() { toggleDebug() });
 Mousetrap.bind("esc", function () {
     $('.splash-screen').removeClass('in').addClass('hidden')
 });
+Mousetrap.bind('ctrl+shift+up', function(e) {
+    var getCurrentTab = $('.allTabsList a.active').parent();
+    var previousTab = getCurrentTab.prev().children();
+    previousTab.trigger("click");
+    parent.focus();
+    return false;
+});
+Mousetrap.bind('ctrl+shift+down', function(e) {
+    var getCurrentTab = $('.allTabsList a.active').parent();
+    var nextTab = getCurrentTab.next().children();
+    nextTab.trigger("click");
+    return false;
+});
 $(document).on('change', "#new-tab-form-chooseImage", function (e) {
     var newIcon = $('#new-tab-form-chooseImage').val();
     if(newIcon !== 'Select or type Icon'){
@@ -1892,4 +1911,14 @@ $(document).on('click', ".copyDebug", function(){
 $(document).on("keyup", "#authBackendHostPrefix-input, #authBackendHostSuffix-input", function () {
     var newDN = $('#authBackendHostPrefix-input').val() + 'TestAcct' + $('#authBackendHostSuffix-input').val();
     $('#accountDN').html(newDN);
+});
+
+// homepage healthchecks
+$(document).on('click', ".good-health-checks", function(){
+    homepageHealthChecks();
+});
+$(document).on('click', ".showMoreHealth", function(){
+   var id = $(this).attr('data-id');
+    $('.showMoreHealthDiv-'+id).toggleClass('d-none');
+    $(this).find('.card-body').toggleClass('healthPosition');
 });

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


+ 314 - 24
js/functions.js

@@ -741,6 +741,10 @@ function loadNextTab(){
 	var next = $('#page-wrapper').find('.loaded').attr('data-name');
 	if (typeof next !== 'undefined') {
 		var type = $('#page-wrapper').find('.loaded').attr('data-type');
+        var parent = $('#menu-'+next).parent();
+        if(parent.hasClass('in') === false){
+            parent.parent().find('a').first().trigger('click')
+        }
 		switchTab(next,type);
 	}else{
 		console.log("Tab Function: No Available Tab to open");
@@ -954,7 +958,8 @@ function buildFormItem(item){
 			return smallLabel+'<select class="form-control'+extraClass+'"'+placeholder+value+id+name+disabled+type+attr+'>'+selectOptions(item.options, item.value)+'</select>';
 			break;
 		case 'select2':
-			return smallLabel+'<select class="m-b-10 '+extraClass+'"'+placeholder+value+id+name+disabled+type+attr+' multiple="multiple" data-placeholder="Choose">'+selectOptions(item.options, item.value)+'</select>';
+            var select2ID = (item.id) ? '#'+item.id : '.'+item.name;
+            return smallLabel+'<select class="m-b-10 '+extraClass+'"'+placeholder+value+id+name+disabled+type+attr+' multiple="multiple" data-placeholder="Choose">'+selectOptions(item.options, item.value)+'</select><script>$("'+select2ID+'").select2();</script>';
 			break;
 		case 'switch':
 		case 'checkbox':
@@ -1687,8 +1692,8 @@ function buildImageManagerViewItem(array){
 						<div class="el-card-avatar el-overlay-1"> <img class="lazyload tabImages" data-src="`+v+`" width="22" height="22">
 							<div class="el-overlay">
 								<ul class="el-info">
-									<li><a class="btn default btn-outline clipboard p-5" data-clipboard-text="`+clipboardText+`" href="javascript:void(0);"><i class="ti-clipboard"></i></a></li>
-									<li><a class="btn default btn-outline deleteImage p-5" href="javascript:void(0);" data-image-path="`+v+`" data-image-name="`+name[0]+`"><i class="icon-trash"></i></a></li>
+									<li><a class="btn default btn-outline clipboard p-a-5" data-clipboard-text="`+clipboardText+`" href="javascript:void(0);"><i class="ti-clipboard"></i></a></li>
+									<li><a class="btn default btn-outline deleteImage p-a-5" href="javascript:void(0);" data-image-path="`+v+`" data-image-name="`+name[0]+`"><i class="icon-trash"></i></a></li>
 								</ul>
 							</div>
 						</div>
@@ -1840,10 +1845,89 @@ function buildTabEditor(){
             return false;
         }
 		$('#tabEditorTable').html(buildTabEditorItem(response.data));
+        loadSettingsPage('api/?v1/settings/tab/editor/homepage','#settings-tab-editor-homepage','Homepage Items');
+        setTimeout(function(){ sortHomepageItemHrefs() }, 1000);
+        setTimeout(function(){ checkTabHomepageItems(); }, 1500);
+
+
 	}).fail(function(xhr) {
 		console.error("Organizr Function: API Connection Failed");
 	});
 }
+function checkTabHomepageItems(){
+    var tabList = $('.checkTabHomepageItem');
+    $.each(tabList, function(i,v) {
+        var el = $(v);
+        var id = el.attr('id');
+        var name = el.attr('data-name');
+        var url = el.attr('data-url');
+        var urlLocal = el.attr('data-url-local');
+        checkTabHomepageItem(id, name, url, urlLocal);
+    });
+}
+function sortHomepageItemHrefs(){
+    var hrefList = $('.popup-with-form');
+    window.hrefList = new Array();
+    $.each(hrefList, function(i,v) {
+        var el = $(v);
+        var href = el.attr('href');
+        if(href.includes('#homepage-')){
+            var splitHref = href.split("-");
+            window.hrefList[splitHref[1]] = i;
+        }
+    });
+}
+function checkTabHomepageItem(id, name, url, urlLocal){
+    name = name.toLowerCase();
+    url = url.toLowerCase();
+    urlLocal = urlLocal.toLowerCase();
+    if(name.includes('sonarr') || url.includes('sonarr') || urlLocal.includes('sonarr')){
+        addEditHomepageItem(id,'Sonarr');
+    }else if(name.includes('radarr') || url.includes('radarr') || urlLocal.includes('radarr')){
+        addEditHomepageItem(id,'Radarr');
+    }else if(name.includes('lidarr') || url.includes('lidarr') || urlLocal.includes('lidarr')){
+        addEditHomepageItem(id,'Lidarr');
+    }else if(name.includes('couchpotato') || url.includes('couchpotato') || urlLocal.includes('couchpotato')){
+        addEditHomepageItem(id,'CouchPotato');
+    }else if(name.includes('sick') || url.includes('sick') || urlLocal.includes('sick')){
+        addEditHomepageItem(id,'SickRage');
+    }else if((name.includes('plex') || url.includes('plex') || urlLocal.includes('plex')) && !name.includes('plexpy')){
+        addEditHomepageItem(id,'Plex');
+    }else if(name.includes('emby') || url.includes('emby') || urlLocal.includes('emby')){
+        addEditHomepageItem(id,'Emby');
+    }else if(name.includes('sab') || url.includes('sab') || urlLocal.includes('sab')){
+        addEditHomepageItem(id,'SabNZBD');
+    }else if(name.includes('nzbget') || url.includes('nzbget') || urlLocal.includes('nzbget')){
+        addEditHomepageItem(id,'NZBGet');
+    }else if(name.includes('transmission') || url.includes('transmission') || urlLocal.includes('transmission')){
+        addEditHomepageItem(id,'Transmission');
+    }else if(name.includes('qbit') || url.includes('qbit') || urlLocal.includes('qbit')){
+        addEditHomepageItem(id,'qBittorrent');
+    }else if(name.includes('rtorrent') || url.includes('rtorrent') || urlLocal.includes('rtorrent')){
+        addEditHomepageItem(id,'rTorrent');
+    }else if(name.includes('deluge') || url.includes('deluge') || urlLocal.includes('deluge')){
+        addEditHomepageItem(id,'Deluge');
+    }else if(name.includes('ombi') || url.includes('ombi') || urlLocal.includes('ombi')){
+        addEditHomepageItem(id,'Ombi');
+    }else if(name.includes('healthcheck') || url.includes('healthcheck') || urlLocal.includes('healthcheck')){
+        addEditHomepageItem(id,'HealthChecks');
+    }
+}
+function addEditHomepageItem(id, type){
+    var html = '';
+    var process = false;
+    if(type in window.hrefList){
+        html = '<i class="ti-home"></i>';
+        process = true;
+    }
+    if(html !== ''){
+        $('#'+id).html(html);
+    }
+    if(process){
+        $('#'+id).attr('onclick', "$('.popup-with-form').magnificPopup('open',"+window.hrefList[type]+")");
+    }
+    return false;
+}
 function buildCategoryEditor(){
 	organizrAPI('GET','api/?v1/tab/list').success(function(data) {
         try {
@@ -2440,8 +2524,11 @@ function categoryProcess(arrayItems){
 	}
 }
 function buildFrame(name,url){
+    var sandbox = activeInfo.settings.misc.sandbox;
+    sandbox = sandbox.replace(/,/gi, ' ');
+    sandbox = (sandbox) ? ' sandbox="' + sandbox + '"' : '';
 	return `
-		<iframe allowfullscreen="true" frameborder="0" id="frame-`+cleanClass(name)+`" data-name="`+cleanClass(name)+`" sandbox="allow-presentation allow-forms allow-same-origin allow-pointer-lock allow-scripts allow-popups allow-modals allow-top-navigation" scrolling="auto" src="`+url+`" class="iframe"></iframe>
+		<iframe allowfullscreen="true" frameborder="0" id="frame-`+cleanClass(name)+`" data-name="`+cleanClass(name)+`" `+sandbox+` scrolling="auto" src="`+url+`" class="iframe"></iframe>
 	`;
 }
 function buildFrameContainer(name,url,type){
@@ -2453,7 +2540,7 @@ function buildInternalContainer(name,url,type){
 function buildMenuList(name,url,type,icon,ping=null){
     var ping = (ping !== null) ? `<small class="menu-`+cleanClass(ping)+`-ping-ms hidden-xs label label-rouded label-inverse pull-right pingTime hidden">
 </small><div class="menu-`+cleanClass(ping)+`-ping" data-tab-name="`+name+`" data-previous-state=""></div>` : '';
-	return `<li class="allTabsList" id="menu-`+cleanClass(name)+`" data-tab-name="`+cleanClass(name)+`" type="`+type+`" data-url="`+url+`"><a class="waves-effect" onclick="tabActions(event,'`+cleanClass(name)+`',`+type+`);">`+iconPrefix(icon)+`<span class="hide-menu elip sidebar-tabName">`+name+`</span>`+ping+`</a></li>`;
+	return `<li class="allTabsList" id="menu-`+cleanClass(name)+`" data-tab-name="`+cleanClass(name)+`" type="`+type+`" data-url="`+url+`"><a class="waves-effect"  onclick="tabActions(event,'`+cleanClass(name)+`',`+type+`);">`+iconPrefix(icon)+`<span class="hide-menu elip sidebar-tabName">`+name+`</span>`+ping+`</a></li>`;
 }
 function tabProcess(arrayItems) {
 	var iFrameList = '';
@@ -2829,7 +2916,7 @@ function buildTabEditorItem(array){
 					</div>
 				</div>
 			</td>
-			<td><span class="tooltip-info" data-toggle="tooltip" data-placement="right" title="" data-original-title="`+v.url+`">`+v.name+`</span></td>
+			<td><span class="tooltip-info" data-toggle="tooltip" data-placement="right" title="" data-original-title="`+v.url+`">`+v.name+`</span><span id="checkTabHomepageItem-`+v.id+`" data-url="`+v.url+`" data-url-local="`+v.url_local+`" data-name="`+v.name+`" class="checkTabHomepageItem mouse label label-rouded label-inverse pull-right"></span></td>
 			`+buildTabCategorySelect(array.categories,v.id, v.category_id)+`
 			`+buildTabGroupSelect(array.groups,v.id, v.group_id)+`
 			`+buildTabTypeSelect(v.id, v.type, typeDisabled)+`
@@ -2861,7 +2948,7 @@ function submitSettingsForm(form){
                     var value = input.prop("checked") ? true : false;
                     break;
 				case 'select2':
-                    var value = input.val().toString();
+                    var value = (input.val() !== null) ? input.val().toString() : '';
                     break;
                 default:
                     var value = input.val();
@@ -3058,7 +3145,7 @@ function updateCheck(){
 		if(latest !== currentVersion) {
             console.log('Update Function: Update to ' + latest + ' is available');
             if (activeInfo.settings.misc.docker === false) {
-                message(window.lang.translate('Update Available'), latest + ' ' + window.lang.translate('is available, goto') + ' <a href="javascript:void(0)" onclick="tabActions(event,\'Settings\',0);clickPath(\'update\')"><span lang="en">Update Tab</span></a>', activeInfo.settings.notifications.position, '#FFF', 'update', '60000');
+                messageSingle(window.lang.translate('Update Available'), latest + ' ' + window.lang.translate('is available, goto') + ' <a href="javascript:void(0)" onclick="tabActions(event,\'Settings\',0);clickPath(\'update\')"><span lang="en">Update Tab</span></a>', activeInfo.settings.notifications.position, '#FFF', 'update', '60000');
             }
         }
 		$('#githubVersions').html(buildVersion(reverseObject(response)));
@@ -3103,7 +3190,7 @@ function checkCommitLoad(){
                 var current = activeInfo.settings.misc.githubCommit.toString().trim();
                 var link = 'https://github.com/causefx/Organizr/compare/'+current+'...'+latest;
                 if(latest !== current) {
-                    message(window.lang.translate('Update Available'),' <a href="'+link+'" target="_blank"><span lang="en">Compare Difference</span></a> <span lang="en">or</span> <a href="javascript:void(0)" onclick="updateNow()"><span lang="en">Update Now</span></a>', activeInfo.settings.notifications.position, '#FFF', 'update', '600000');
+                    messageSingle(window.lang.translate('Update Available'),' <a href="'+link+'" target="_blank"><span lang="en">Compare Difference</span></a> <span lang="en">or</span> <a href="javascript:void(0)" onclick="updateNow()"><span lang="en">Update Now</span></a>', activeInfo.settings.notifications.position, '#FFF', 'update', '600000');
                 }else{
                     console.log('Organizr Docker - Up to date');
                 }
@@ -3294,6 +3381,10 @@ function updateNow(){
     if(activeInfo.settings.misc.docker){
         dockerUpdate();
         return false;
+    }
+    if(activeInfo.serverOS === 'win'){
+        windowsUpdate();
+        return false;
     }
 	console.log('Organizr Function: Starting Update Process');
 	$(updateBar()).appendTo('.organizr-area');
@@ -3851,6 +3942,15 @@ function errorPage(error=null,uri=null){
         local('set','uri',$.urlParam('return'));
     }
 	if ( window.location !== window.parent.location ) {
+        var count = 0;
+        for (var k in window.parent.location) {
+            if (window.parent.location.hasOwnProperty(k)) {
+                ++count;
+            }
+        }
+        if(count == 0 || count == 'undefined'){
+            return false;
+        }
 		var iframeError = local('get', 'error');
 		parent.errorPage(iframeError);
         local('remove', 'uri');
@@ -4038,7 +4138,9 @@ function buildRecentItem(array, type, extra=null){
 			<div class="item lazyload `+className+` metadata-get mouse imageSource" data-source="`+type+`" data-key="`+v.metadataKey+`" data-uid="`+v.uid+`" data-src="`+v.imageURL+`">
 				`+extraImg+`
 				<div class="hover-homepage-item">
-					<a class="btn default refreshImage" data-type="recent-item" data-image="`+v.originalImage+`" href="javascript:void(0);"><i class="mdi mdi-refresh mdi-24px"></i></a>
+				    <span class="elip request-title-movie">
+					    <a class="text-white refreshImage" data-type="recent-item" data-image="`+v.originalImage+`" href="javascript:void(0);"><i class="mdi mdi-refresh mdi-24px"></i></a>
+					</span>
 				</div>
 				<span class="elip recent-title">`+v.title+`<br/>`+v.secondaryTitle+`</span>
 				<div id="`+v.uid+`-metadata-div" class="white-popup mfp-with-anim mfp-hide">
@@ -4063,7 +4165,9 @@ function buildPlaylistItem(array, type, extra=null){
 				items += `
 				<div class="item lazyload recent-poster metadata-get mouse imageSource" data-source="`+type+`" data-key="`+v.metadataKey+`" data-uid="`+v.uid+`" data-src="`+v.imageURL+`">
 					<div class="hover-homepage-item">
-						<a class="btn default refreshImage" data-type="recent-item" data-image="`+v.originalImage+`" href="javascript:void(0);"><i class="mdi mdi-refresh mdi-24px"></i></a>
+					    <span class="elip request-title-movie">
+						    <a class="text-white refreshImage" data-type="recent-item" data-image="`+v.originalImage+`" href="javascript:void(0);"><i class="mdi mdi-refresh mdi-24px"></i></a>
+						</span>
 					</div>
 					<span class="elip recent-title">`+v.title+`</span>
 					<div id="`+v.uid+`-metadata-div" class="white-popup mfp-with-anim mfp-hide">
@@ -4198,7 +4302,7 @@ function buildStream(array, type){
 	<div id="`+type+`Streams">
 		<div class="el-element-overlay row">
 		    <div class="col-md-12">
-		        <h4 class="pull-left"><span lang="en">Active</span> `+toUpper(type)+` <span lang="en">Streams</span>: </h4><h4 class="pull-left">&nbsp;<span class="label label-info m-l-20 checkbox-circle">`+streams+`</span></h4>
+		        <h4 class="pull-left"><span lang="en">Active</span> `+toUpper(type)+` <span lang="en">Streams</span>: </h4><h4 class="pull-left">&nbsp;<span class="label label-info m-l-20 checkbox-circle mouse" onclick="homepageStream('`+type+`')">`+streams+`</span></h4>
 		        <hr class="hidden-xs">
 		    </div>
 			<div class="clearfix"></div>
@@ -4240,7 +4344,7 @@ function buildRecent(array, type){
 	if(activeInfo.settings.homepage.options.alternateHomepageHeaders){
 		var headerAlt = `
 		<div class="col-md-12">
-			<h4 class="pull-left"><span lang="en">Recently Added</span></h4>
+			<h4 class="pull-left"><span class="mouse" onclick="homepageRecent('`+type+`')" lang="en">Recently Added</span></h4>
 			`+dropdownMenu+`
 			<hr class="hidden-xs"><div class="clearfix"></div>
 		</div>
@@ -4248,7 +4352,7 @@ function buildRecent(array, type){
 	}else{
 		var header = `
 		<div class="panel-heading bg-info p-t-10 p-b-10">
-			<span class="pull-left m-t-5"><img class="lazyload homepageImageTitle" data-src="plugins/images/tabs/`+type+`.png"> &nbsp; <span lang="en">Recently Added</span></span>
+			<span onclick="homepageRecent('`+type+`')" class="pull-left m-t-5 mouse"><img class="lazyload homepageImageTitle" data-src="plugins/images/tabs/`+type+`.png"> &nbsp; <span lang="en">Recently Added</span></span>
 			`+dropdownMenu+`
 			<div class="clearfix"></div>
 		</div>
@@ -4334,7 +4438,7 @@ function buildPlaylist(array, type){
 	if(activeInfo.settings.homepage.options.alternateHomepageHeaders){
 		var headerAlt = `
 		<div class="col-md-12">
-			<h4 class="pull-left"><span class="`+type+`-playlistTitle">`+first+`</span></h4>
+			<h4 class="pull-left"><span onclick="homepagePlaylist('`+type+`')" class="`+type+`-playlistTitle mouse">`+first+`</span></h4>
 			<div class="btn-group pull-right">
 				`+builtDropdown+`
 			</div>
@@ -4344,7 +4448,7 @@ function buildPlaylist(array, type){
 	}else{
 		var header = `
 		<div class="panel-heading bg-info p-t-10 p-b-10">
-			<span class="pull-left m-t-5"><img class="lazyload homepageImageTitle" data-src="plugins/images/tabs/`+type+`.png"> &nbsp; <span class="`+type+`-playlistTitle">`+first+`</span></span>
+			<span class="pull-left m-t-5 mouse" onclick="homepagePlaylist('`+type+`')"><img class="lazyload homepageImageTitle" data-src="plugins/images/tabs/`+type+`.png"> &nbsp; <span class="`+type+`-playlistTitle">`+first+`</span></span>
 			<div class="btn-group pull-right">
 					`+builtDropdown+`
 			</div>
@@ -4416,7 +4520,7 @@ function buildRequest(array){
 	if(activeInfo.settings.homepage.options.alternateHomepageHeaders){
 		var headerAlt = `
 		<div class="col-md-12">
-			<h4 class="pull-left"><span lang="en">Requests</span></h4>
+			<h4 class="pull-left"><span class="mouse" onclick="homepageRequests()" lang="en">Requests</span></h4>
 			<div class="btn-group pull-right">
 				`+builtDropdown+`
 			</div>
@@ -4426,7 +4530,7 @@ function buildRequest(array){
 	}else{
 		var header = `
 		<div class="panel-heading bg-info p-t-10 p-b-10">
-			<span class="pull-left m-t-5"><img class="lazyload homepageImageTitle" data-src="plugins/images/tabs/ombi.png"> &nbsp; Requests</span>
+			<span class="pull-left m-t-5 mouse" onclick="homepageRequests()"><img class="lazyload homepageImageTitle" data-src="plugins/images/tabs/ombi.png"> &nbsp; Requests</span>
 			<div class="btn-group pull-right">
 					`+builtDropdown+`
 			</div>
@@ -4549,7 +4653,7 @@ function buildRequestResult(array,media_type=null,list=null,page=null,search=fal
 	                <div class="el-card-item p-b-0">
 	                    <div class="el-card-avatar el-overlay-1 m-b-5 preloader-`+v.id+`"> <img class="lazyload resultImages" data-src="`+bg+`">
 	                        <div class="el-overlay">
-								<span class="text-info p-5 font-normal">`+comment+`</span>
+								<span class="text-info p-a-5 font-normal">`+comment+`</span>
 	                            <ul class="el-info">
 	                                <li><a class="btn default btn-outline" href="javascript:void(0);" onclick="processRequest('`+v.id+`','`+media_type+`');"><i class="icon-link"></i>&nbsp; <span lang="en">Request</span></a></li>
 	                                <li><a class="btn default btn-outline" href="https://www.themoviedb.org/`+media_type+`/`+v.id+`" target="_blank"><i class="icon-info"></i></a></li>
@@ -4738,6 +4842,10 @@ function buildDownloaderItem(array, source, type='none'){
     var count = 0;
 	switch (source) {
 		case 'sabnzbd':
+            if(array.content === false){
+                queue = '<tr><td class="max-texts" lang="en">Connection Error to ' + source + '</td></tr>';
+                break;
+            }
             if(array.content.queueItems.queue.paused){
                 var state = `<a href="#"><span class="downloader mouse" data-source="sabnzbd" data-action="resume" data-target="main"><i class="fa fa-play"></i></span></a>`;
                 var active = 'grayscale';
@@ -4790,6 +4898,10 @@ function buildDownloaderItem(array, source, type='none'){
             });
 			break;
 		case 'nzbget':
+            if(array.content === false){
+                queue = '<tr><td class="max-texts" lang="en">Connection Error to ' + source + '</td></tr>';
+                break;
+            }
             if(array.content.queueItems.result.length == 0){
                 queue = '<tr><td class="max-texts" lang="en">Nothing in queue</td></tr>';
             }
@@ -4837,6 +4949,10 @@ function buildDownloaderItem(array, source, type='none'){
             });
 			break;
 		case 'transmission':
+            if(array.content === false){
+                queue = '<tr><td class="max-texts" lang="en">Connection Error to ' + source + '</td></tr>';
+                break;
+            }
             if(array.content.queueItems.arguments.torrents == 0){
                 queue = '<tr><td class="max-texts" lang="en">Nothing in queue</td></tr>';
             }
@@ -4896,6 +5012,10 @@ function buildDownloaderItem(array, source, type='none'){
             });
 			break;
         case 'rTorrent':
+            if(array.content === false){
+                queue = '<tr><td class="max-texts" lang="en">Connection Error to ' + source + '</td></tr>';
+                break;
+            }
             if(array.content.queueItems == 0){
                 queue = '<tr><td class="max-texts" lang="en">Nothing in queue</td></tr>';
             }
@@ -4929,6 +5049,10 @@ function buildDownloaderItem(array, source, type='none'){
             });
             break;
 		case 'qBittorrent':
+		    if(array.content === false){
+                queue = '<tr><td class="max-texts" lang="en">Connection Error to ' + source + '</td></tr>';
+                break;
+            }
             if(array.content.queueItems.arguments.torrents == 0){
                 queue = '<tr><td class="max-texts" lang="en">Nothing in queue</td></tr>';
             }
@@ -4984,6 +5108,10 @@ function buildDownloaderItem(array, source, type='none'){
             });
 			break;
 		case 'deluge':
+            if(array.content === false){
+                queue = '<tr><td class="max-texts" lang="en">Connection Error to ' + source + '</td></tr>';
+                break;
+            }
             if(array.content.queueItems.length == 0){
                 queue = '<tr><td class="max-texts" lang="en">Nothing in queue</td></tr>';
             }
@@ -5221,7 +5349,7 @@ function buildMetadata(array, source){
 		var hasGenre = (typeof v.metadata.genres !== 'string') ? true : false;
 		if(hasActor){
 			$.each(v.metadata.actors, function(i,v) {
-				actors += '<div class="item lazyload recent-poster" data-src="'+(v.thumb.replace("http://", "https://"))+'" alt="'+v.name+'" ><span class="elip recent-title p-5">'+v.name+'<br><small class="font-light">'+v.role+'</small></span></div>';
+				actors += '<div class="item lazyload recent-poster" data-src="'+(v.thumb.replace("http://", "https://"))+'" alt="'+v.name+'" ><span class="elip recent-title p-a-5">'+v.name+'<br><small class="font-light">'+v.role+'</small></span></div>';
 			});
 		}
 		if(hasGenre){
@@ -5320,6 +5448,128 @@ function buildCalendarMetadata(array){
 		`;
 	return metadata;
 }
+function buildHealthChecks(array){
+    if(array === false){ return ''; }
+    var checks = (typeof array.content.checks !== 'undefined') ? array.content.checks.length : false;
+    return (checks) ? `
+	<div id="allHealthChecks">
+		<div class="el-element-overlay row">
+		    <div class="col-md-12">
+		        <h4 class="pull-left"><span lang="en">Health Checks</span> : </h4><h4 class="pull-left">&nbsp;<span class="label label-info m-l-20 checkbox-circle good-health-checks mouse">`+checks+`</span></h4>
+		        <hr class="hidden-xs">
+		    </div>
+			<div class="clearfix"></div>
+		    <!-- .cards -->
+			`+buildHealthChecksItem(array.content.checks)+`
+		    <!-- /.cards-->
+		</div>
+	</div>
+	<div class="clearfix"></div>
+	` : '';
+}
+function healthCheckIcon(tags){
+    var allTags = tags.split(' ');
+    var useIcon = '';
+    $.each(allTags, function(i,v) {
+        //check for image
+        var file =  v.substring(v.lastIndexOf('.')+1, v.length).toLowerCase() || v.toLowerCase();
+        switch (file) {
+            case 'png':
+            case 'jpg':
+            case 'jpeg':
+            case 'gif':
+                useIcon = '<img class="lazyload loginTitle" data-src="'+v+'">&nbsp;';
+                break;
+            default:
+        }
+    });
+    return useIcon;
+}
+function buildHealthChecksItem(array){
+    var checks = '';
+    $.each(array, function(i,v) {
+        var hasIcon = healthCheckIcon(v.tags);
+        v.name = (v.name) ? v.name : 'New Item';
+        switch(v.status){
+            case 'up':
+                var statusColor = 'success';
+                var statusIcon = 'ti-link text-success';
+                var nextPing = moment.utc(v.next_ping, "YYYY-MM-DD hh:mm[Z]").local().fromNow();
+                var lastPing = moment.utc(v.last_ping, "YYYY-MM-DD hh:mm[Z]").local().fromNow();
+                break;
+            case 'down':
+                var statusColor = 'danger animated-3 loop-animation flash';
+                var statusIcon = 'ti-unlink text-danger';
+                var nextPing = 'Service Down';
+                var lastPing = moment.utc(v.last_ping, "YYYY-MM-DD hh:mm[Z]").local().fromNow();
+                break;
+            case 'new':
+                var statusColor = 'info';
+                var statusIcon = 'ti-time text-info';
+                var nextPing = 'Waiting...';
+                var lastPing = 'n/a';
+                break;
+            case 'grace':
+                var statusColor = 'warning';
+                var statusIcon = 'ti-alert text-warning';
+                var nextPing = moment.utc(v.next_ping, "YYYY-MM-DD hh:mm[Z]").local().fromNow();
+                var lastPing = 'Missed';
+                break;
+            case 'paused':
+                var statusColor = 'primary';
+                var statusIcon = 'ti-control-pause text-primary';
+                var nextPing = 'Paused';
+                var lastPing = moment.utc(v.last_ping, "YYYY-MM-DD hh:mm[Z]").local().fromNow();
+                break;
+            default:
+                var statusColor = 'warning';
+                var statusIcon = 'ti-timer text-warning';
+                var nextPing = 'Waiting...';
+                var lastPing = 'n/a';
+        }
+        checks += `
+            <div class="col-xl-2 col-lg-3 col-md-4 col-sm-6 col-xs-12">
+                <div class="card bg-inverse text-white mb-3 showMoreHealth mouse" data-id="`+i+`">
+                    <div class="card-body bg-org-alt pt-1 pb-1">
+                        <div class="d-flex no-block align-items-center">
+                            <div class="left-health bg-`+statusColor+`"></div>
+                            <div class="ml-1 w-100">
+                                <i class="`+statusIcon+` font-20 pull-right mt-3 mb-2"></i>
+                                <h3 class="d-flex no-block align-items-center mt-2 mb-2">`+hasIcon+v.name+`</h3>
+                                <div class="clearfix"></div>
+                                <div class="d-none showMoreHealthDiv-`+i+`"><h5>Last: `+lastPing+`</h5><h5>Next: `+nextPing+`</h5></div>
+                                <div class="clearfix"></div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        `
+    });
+    return checks;
+}
+function homepageHealthChecks(tags, timeout){
+    var tags = (typeof tags !== 'undefined') ? tags : activeInfo.settings.homepage.options.healthChecksTags;
+    var timeout = (typeof timeout !== 'undefined') ? timeout : activeInfo.settings.homepage.refresh.homepageHealthChecksRefresh;
+    organizrAPI('POST','api/?v1/homepage/connect',{action:'getHealthChecks',tags:tags}).success(function(data) {
+        try {
+            var response = JSON.parse(data);
+        }catch(e) {
+            console.log(e + ' error: ' + data);
+            orgErrorAlert('<h4>' + e + '</h4>' + formatDebug(data));
+            return false;
+        }
+        document.getElementById('homepageOrderhealthchecks').innerHTML = '';
+        if(response.data !== null){
+            $('#homepageOrderhealthchecks').html(buildHealthChecks(response.data));
+        }
+    }).fail(function(xhr) {
+        console.error("Organizr Function: API Connection Failed");
+    });
+    var timeoutTitle = 'HealthChecks-Homepage';
+    if(typeof timeouts[timeoutTitle] !== 'undefined'){ clearTimeout(timeouts[timeoutTitle]); }
+    timeouts[timeoutTitle] = setTimeout(function(){ homepageHealthChecks(tags,timeout); }, timeout);
+}
 function homepageDownloader(type, timeout){
 	var timeout = (typeof timeout !== 'undefined') ? timeout : activeInfo.settings.homepage.refresh.homepageDownloadRefresh;
 	//if(isHidden()){ return; }
@@ -5490,9 +5740,9 @@ function homepageRequests(timeout){
 	if(typeof timeouts['ombi-Homepage'] !== 'undefined'){ clearTimeout(timeouts['ombi-Homepage']); }
 	timeouts['ombi-Homepage'] = setTimeout(function(){ homepageRequests(timeout); }, timeout);
 }
-function testAPIConnection(service){
+function testAPIConnection(service, data = ''){
     messageSingle('',' Testing now...',activeInfo.settings.notifications.position,'#FFF','info','10000');
-    organizrAPI('POST','api/?v1/test/api/connection',{action:service}).success(function(data) {
+    organizrAPI('POST','api/?v1/test/api/connection',{action:service, data:data}).success(function(data) {
         try {
             var response = JSON.parse(data);
         }catch(e) {
@@ -6560,8 +6810,10 @@ function checkIfTabNameExists(tabName){
     }
 }
 function orgErrorAlert(error){
-    $('#main-org-error-container').addClass('show');
-    $('#main-org-error').html(error);
+    if(activeInfo.settings.misc.debugErrors) {
+        $('#main-org-error-container').addClass('show');
+        $('#main-org-error').html(error);
+    }
 }
 function closeOrgError(){
     $('#main-org-error-container').removeClass('show');
@@ -6578,6 +6830,44 @@ function isJSON(data) {
         return false;
     }
 }
+function createElementFromHTML(htmlString) {
+    var div = document.createElement('div');
+    div.innerHTML = htmlString.trim();
+    return div.firstChild;
+}
+function showLDAPLoginTest(){
+    var div = `
+        <div class="row">
+            <div class="col-12">
+                <div class="card m-b-0">
+                    <div class="form-horizontal">
+                        <div class="card-body">
+                            <h4 class="card-title" lang="en">LDAP User Info</h4>
+                            <div class="form-group row">
+                                <div class="col-sm-12">
+                                    <input type="text" class="form-control" id="ldapUsernameTest" placeholder="Username">
+                                </div>
+                            </div>
+                            <div class="form-group row">
+                                <div class="col-sm-12">
+                                    <input type="password" class="form-control" id="ldapPasswordTest" placeholder="Password">
+                                </div>
+                            </div>
+                            <div class="form-group mb-0 p-r-10 text-right">
+                                <button type="submit" onclick="testAPIConnection('ldap_login', {'username':$('#ldapUsernameTest').val(),'password':$('#ldapPasswordTest').val()})" class="btn btn-info waves-effect waves-light">Test Login</button>
+                            </div>
+                        </div>				
+                    </div>
+                </div>
+            </div>
+        </div>
+    `;
+    swal({
+        content: createElementFromHTML(div),
+        buttons: false,
+        className: 'bg-org'
+    })
+}
 function launch(){
 	organizrConnect('api/?v1/launch_organizr').success(function (data) {
         try {

+ 520 - 0
js/langpack/ca[Catalan].json

@@ -0,0 +1,520 @@
+{
+    "token": {
+        "Navigation": "Navegació",
+        "Date": "Data",
+        "Type": "Escriviu",
+        "IP Address": "Adreça IP",
+        "Username": "Nom d'usuari",
+        "Message": "Missatge",
+        "My Profile": "El meu perfil",
+        "Account Settings": "Configuració del compte",
+        "Login/Register": "Entreu / Registreu-vos",
+        "Logout": "Tancar sessió",
+        "Inbox": "Safata d'entrada",
+        "Go Back": "Torna",
+        "Reset": "Restableix",
+        "Email": "Correu electrònic",
+        "Enter your Email and instructions will be sent to you!": "Introduïu el vostre correu electrònic i us enviaran les instruccions.",
+        "Register": "Registrar-se",
+        "Registration Password": "Contrasenya de registre",
+        "Recover Password": "Recuperar contrasenya",
+        "Password": "Contrasenya",
+        "Sign Up": "Registra't",
+        "Don't have an account?": "No teniu un compte?",
+        "Forgot pwd?": "Heu oblidat la contrasenya?",
+        "Remember Me": "Recorda'm",
+        "Login": "Inicieu Sessió",
+        "Installed": "Instal·lat",
+        "Install Update": "Instal·la l'actualització",
+        "Organizr Versions": "Versions Organizr",
+        "About": "Quant a",
+        "Organizr Logs": "Registres Organizr",
+        "Main Settings": "Configuració principal",
+        "Updates": "Actualitzacions",
+        "Logs": "Registres",
+        "Main": "Principal",
+        "Plugins": "Connectors",
+        "Manage Users": "Gestioneu els usuaris",
+        "Customize Organizr": "Personalitza Organizr",
+        "Edit Categories": "Editeu categories",
+        "Edit Tabs": "Editeu pestanyes",
+        "Tabs": "Pestanyes",
+        "System Settings": "Ajustos del sistema",
+        "User Management": "Gestió d'usuaris",
+        "Customize": "Personalitza",
+        "Tab Editor": "Editor de pestanyes",
+        "Settings": "Configuració",
+        "Organizr Settings": "Configuració Organizr",
+        "Categories": "Categories",
+        "Login Logs": "Registres d’inici de sessió",
+        "Login Log": "Registre d'inici de sessió",
+        "Organizr Log": "Registre Organizr",
+        "FIXED": "FIXAT",
+        "NEW": "NOU",
+        "NOTE": "NOTA",
+        "Update Available": "Actualització disponible",
+        "is available, goto": "està disponible, aneu",
+        "Update Tab": "Actualitza la pestanya",
+        "Database Name:": "Nom de la base de dades:",
+        "Database Name": "Nom de la base de dades",
+        "Database Location:": "Ubicació de la base de dades:",
+        "Database Location": "Ubicació de la base de dades",
+        "Hover to show": "Passa per mostrar",
+        "API Key:": "Clau de l'API:",
+        "API Key": "Clau de l'API",
+        "Registration Password:": "Contrasenya de registre:",
+        "Hash Key:": "Clau de Hash:",
+        "Hash Key": "Clau de Hash",
+        "Password:": "Contrasenya:",
+        "Attention": "Atenció",
+        "The Hash Key will be used to decrypt all passwords etc... on the server.": "La clau de Hash s'utilitzarà per desxifrar totes les contrasenyes, etc ... al servidor.",
+        "The API Key will be used for all calls to organizr for the UI. [Auto-Generated]": "La clau de l’API s’utilitzarà per a totes les peticions a Organizr per a la IU. [Generada automàticament]",
+        "Notice": "Avís",
+        "Business": "Negocis",
+        "Personal": "Personal",
+        "Choose License": "Trieu la llicència",
+        "Install Type": "Tipus d'instal·lació",
+        "Verify": "Verifiqueu",
+        "Database": "Base de dades",
+        "Security": "Seguretat",
+        "Admin Info": "Informació administrador/a",
+        "Parent Directory:": "Directori principal:",
+        "Admin Creation": "Creació d’administradors/es",
+        "The Database will contain sensitive information.  Please place in directory outside of root Web Directory.": "La base de dades contindrà informació confidencial. Col·loqueu el directori fora del directori web de l’arrel.",
+        "MANAGE": "GESTIONEU",
+        "CATEGORY": "CATEGORIA",
+        "ADDED": "AFEGIT",
+        "NAME & EMAIL": "NOM I CORREU",
+        "MANAGE USERS": "GESTIONEU USUARIS",
+        "Add User": "Afegeix un usuari",
+        "Manage Groups": "Gestiona els grups",
+        "Choose Language": "Trieu l’idioma",
+        "Welcome": "Benvinguda",
+        "License": "Llicència",
+        "Webserver Version": "Versió del servidor web",
+        "PHP Version": "Versió de PHP",
+        "Organizr Branch": "branca Organizr",
+        "Organizr Version": "Versió Organizr",
+        "Information": "Informació",
+        "Below you will find all the links for everything that has to do with Organizr": "A continuació trobareu tots els enllaços per a tot allò que tingui a veure amb Organizr",
+        "Loading...": "Carregant ...",
+        "Donate": "Doneu",
+        "Edit Group": "Edita el grup",
+        "For icons, use the following format:": "Per a les icones, utilitzeu el format següent:",
+        "For images, use the following format:": "Per a imatges, utilitzeu el format següent:",
+        "You may use an image or icon in this field": "Podeu utilitzar una imatge o una icona en aquest camp",
+        "Image Legend": "Llegenda de la imatge",
+        "Category Image": "Imatge de categoria",
+        "Category Name": "Nom de la categoria",
+        "Edit Category": "Edita la categoria",
+        "Add Category": "Afegeix una categoria",
+        "Add New Category": "Afegeix una nova categoria",
+        "DELETE": "ELIMINEU",
+        "EDIT": "EDITEU",
+        "DEFAULT": "PER DEFECTE",
+        "TABS": "PESTANYES",
+        "NAME": "NOM",
+        "Category Editor": "Editor de categories",
+        "Tab Image": "Imatge de pestanya",
+        "Tab URL": "URL de la pestanya",
+        "Tab Name": "Nom de la pestanya",
+        "Edit Tab": "Edita la pestanya",
+        "Add Tab": "Afegeix una pestanya",
+        "Add New Tab": "Afegeix una pestanya nova",
+        "SPLASH": "SPLASH",
+        "ACTIVE": "ACTIU",
+        "TYPE": "TIPUS",
+        "GROUP": "GRUP",
+        "Delete": "Suprimeix",
+        "No": "No",
+        "Yes": "Sí",
+        "Deleted Category": "Categoria suprimida",
+        "Add New User": "Afegeix un usuari nou",
+        "EMAIL": "CORREU",
+        "Deleted User": "Usuari eliminat",
+        "Category Order Saved": "Categoria Ordre desada",
+        "Group Image": "Imatge de grup",
+        "Group Name": "Nom del grup",
+        "Add Group": "Afegeix un grup",
+        "Add New Group": "Afegeix un grup nou",
+        "USERS": "USUARIS",
+        "GROUP NAME": "NOM DEL GRUP",
+        "MANAGE GROUPS": "GESTIONAR GRUPS",
+        "Changed Language To": "Canvi de llengua a",
+        "Groups": "Grups",
+        "Users": "Usuaris",
+        "Appearance": "Aparició",
+        "Customize Appearance": "Personalitza l'aparença",
+        "Request Me!": "Demaneu-me!",
+        "Would you like to Request it?": "Voleu sol·licitar-ho?",
+        "No Results for:": "No hi ha resultats per a:",
+        "Nothing in queue": "Res a la cua",
+        "Nothing in history": "Res a la història",
+        "Nothing in hitsory": "Res en trossos",
+        "Load More": "Carrega més",
+        "Request": "Sol·licitud",
+        "No Results": "Sense resultats",
+        "Airs Today TV": "Emssions diàries TV",
+        "Popular TV": "TV popular",
+        "Top TV": "Top TV",
+        "Upcoming Movies": "Properes pel·lícules",
+        "Popular Movies": "Pel·lícules populars",
+        "Top Movies": "Top Movies",
+        "In Theatres": "En cinemes",
+        "Suggestions": "Suggeriments",
+        "TV": "TV",
+        "Movie": "Pel·lícula",
+        "Denied": "Denegat",
+        "Unapproved": "No aprovat",
+        "Approved": "Aprovat",
+        "Unavailable": "No disponible",
+        "Available": "Disponible",
+        "Recently Added": "Recentment afegit",
+        "Active": "Actiu",
+        "Requested By:": "Sol · licitada per:",
+        "Request Options": "Opcions de sol·licitud",
+        "Deny": "Denegat",
+        "OK": "D'acord",
+        "Seconds": "Segons",
+        "Verify Password": "Verifica la contrasenya",
+        "Account Information": "Informació del compte",
+        "Inactive Plugins": "Connectors inactius",
+        "Active Plugins": "Connectors actius",
+        "Inactive": "Inactiu",
+        "Everything Active": "Tot actiu",
+        "Nothing Active": "Res activ",
+        "Choose Plex Machine": "Trieu la màquina Plex",
+        "Test Speed to Server": "Prova la velocitat al servidor",
+        "Test Server Speed": "Prova de velocitat del servidor",
+        "Subject": "Assignatura",
+        "To:": "A:",
+        "Email Users": "Email usuaris",
+        "E-Mail Center": "Centre de correu electrònic",
+        "You have been invited. Please goto ": "Has estat convidat. Si us plau, aneu ",
+        "Use Invite Code": "Utilitzeu el codi d'invitació",
+        "VALID": "VÀLID",
+        "IP ADDRESS": "ADREÇA IP",
+        "USED BY": "UTILITZAT PER",
+        "DATE USED": "DATA USADA",
+        "DATE SENT": "DATA de la presentació",
+        "INVITE CODE": "CODI D'INVITACIÓ",
+        "USERNAME": "NOM D'USUARI",
+        "Manage Invites": "Gestiona les invitacions",
+        "No Invites": "Sense invitacions",
+        "Create/Send Invite": "Creeu / envieu invitació",
+        "Name or Username": "Nom o nom d’usuari",
+        "New Invite": "Invitació nova",
+        "Hover to show ": "Passa per mostrar ",
+        "Parent Directory: ": "Directori principal: ",
+        "The Database will contain sensitive information. Please place in directory outside of root Web Directory.": "La base de dades contindrà informació confidencial. Col·loqueu el directori fora del directori web de l’arrel.",
+        "I Want to Help": "Vull ajudar",
+        "Head on over to POEditor and help us translate Organizr into your language": "Passa a POEditor i ajuda a traduir Organizr al teu idioma",
+        "Want to help translate?": "Voleu ajudar a traduir?",
+        "Single Sign-On": "Inici de sessió únic",
+        "Coming Soon...": "Pròximament...",
+        "Homepage Order": "Ordre de pàgina d'inici",
+        "Homepage Items": "Articles de la pàgina d'inici",
+        "Image Manager": "Gestor d'imatges",
+        "Edit User": "Edita l'usuari",
+        "Password Again": "Contrasenya una altra vegada",
+        "Template": "Plantilla",
+        "Plex Machine": "Màquina Plex",
+        "Get Plex Machine": "Obteniu la màquina Plex",
+        "Grab It": "Agafar",
+        "Plex Password": "Contrasenya Plex",
+        "Plex Username": "Nom d’usuari de Plex",
+        "Enter Plex Details": "Introduïu els detalls de Plex",
+        "Get Plex Token": "Obteniu el símbol de Plex",
+        "Upload Image": "Carrega una imatge",
+        "View Images": "Veure imatges",
+        "Reload": "Recarregar",
+        "Unlock": "Descobrir",
+        "Browser Information": "Informació del navegador",
+        "Web Folder": "Carpeta web",
+        "Dependencies Missing": "Falta les dependències",
+        "Organizr Dependency Check": "Verificació de la dependència d'Organizr",
+        "Please make sure both Token and Machine are filled in": "Assegureu-vos que tots dos Token i Machine estiguin emplenats",
+        "Loading Requests...": "S'estan carregant sol·licituds ...",
+        "Loading Recent...": "S'està carregant els últims ...",
+        "Loading Now Playing...": "Carregant ara Jugant ...",
+        "Loading Playlists...": "S'està carregant llistes de reproducció ...",
+        "Loading Download Queue...": "S'està carregant la cua de descàrrega ...",
+        "Organizr Mod Picks": "Organització Modificacions",
+        "Requests": "Sol·licituds",
+        "Become Sponsor": "Fes-te patrocinador",
+        "Splash Page": "Splash Page",
+        "Lock Screen": "Pantalla de bloqueig",
+        "If you signed in with a Emby Acct... Please use the following link to change your password there:": "Si heu iniciat la sessió amb Emby Acct ... Utilitzeu el següent enllaç per canviar la vostra contrasenya allà:",
+        "Password Notice": "Avís de contrasenya",
+        "If you signed in with a Plex Acct... Please use the following link to change your password there:": "Si heu iniciat la sessió amb un Plex Acct ... Utilitzeu el següent enllaç per canviar la vostra contrasenya allà:",
+        "Active Tokens": "Fitxes actives",
+        "Deactivate": "Desactiva",
+        "Activate": "Activa",
+        "Current": "Actual",
+        "Save": "Desa",
+        "STATUS": "ESTAT",
+        "PLUGIN": "CONNECTOR",
+        "Plugin Marketplace": "Plugin Marketplace",
+        "Marketplace": "Mercat",
+        "Chat": "Xat",
+        "Current Directory: ": "Directori actual: ",
+        "Suggested Directory: ": "Directori suggerit: ",
+        "The Registration Password will lockout the registration field with this password. {User-Generated]": "La contrasenya de registre bloquejarà el camp de registre amb aquesta contrasenya. {Generat per l'usuari]",
+        "The Hash Key will be used to decrypt all passwords etc... on the server. {User-Generated]": "La clau Hash s'utilitzarà per desxifrar totes les contrasenyes, etc ... al servidor. {Generat per l'usuari]",
+        "If using Plex or Emby - It is suggested that you use the username and email of the Admin account.": "Si utilitzeu Plex o Emby - Us recomanem que utilitzeu el nom d’usuari i el correu electrònic del compte d’administrador.",
+        "Business has Media items hidden [Plex, Emby etc...]": "El negoci té elements multimèdia amagats [Plex, Emby, etc ...]",
+        "Personal has everything unlocked - no restrictions": "El personal té tot alliberat: no hi ha restriccions",
+        "Continue To Website": "Continua al lloc web",
+        "Patreon": "Patreon",
+        "Cryptos": "Cryptos",
+        "Square Cash": "Square Cash",
+        "PayPal": "PayPal",
+        "Beerpay.io": "Beerpay.io",
+        "Sponsors": "Patrocinadors",
+        "THEME": "TEMA",
+        "Theme Marketplace": "Mercat de temes",
+        "Test Tab": "Pestanya de prova",
+        "Select or type Icon": "Seleccioneu o escriviu Icona",
+        "Choose Icon": "Trieu la icona",
+        "Choose Image": "Trieu Imatge",
+        "Ping URL": "URL de ping",
+        "Please set tab as [New Window] on next screen": "Configureu la pestanya com a [Finestra nova] a la pantalla següent",
+        "Tab can be set as iFrame": "La pestanya es pot configurar com a iFrame",
+        "Premier": "Premier",
+        "Missing": "Falta",
+        "Unaired": "Unaired",
+        "Downloaded": "Descarregat",
+        "All": "Tots",
+        "Choose Media Status": "Trieu Estat del suport",
+        "Music": "Música",
+        "Choose Media Type": "Trieu el tipus de suport",
+        "PHP Version Check": "Verificació de la versió PHP",
+        "Don\\'t have an account?": "No tens un compte?",
+        "The value of #987654 is just a placeholder, you can change to any value you like.": "El valor de # 987654 és només un espai reservat, podeu canviar a qualsevol valor que vulgueu.",
+        "This is not the same as database authentication - i.e. Plex Authentication | Emby Authentication | FTP Authentication": "Això no és el mateix que l’autenticació de la base de dades: és a dir, l’autenticació de Plex | Autenticació Emby | Autenticació FTP",
+        "Status: [ ": "Estat: [ ",
+        "This module requires XMLRPC": "Aquest mòdul requereix XMLRPC",
+        "Misc Options": "Opcions diverses",
+        "Search My Media": "Cerca a My Media",
+        "Import Plex Users": "Importa els usuaris de Plex",
+        "Import": "Importa",
+        "INSTALL": "INSTAL·LAR",
+        "INFO": "INFO",
+        "Day": "Dia",
+        "Week": "Setmana",
+        "Month": "Mes",
+        "List": "Llista",
+        "Streams": "Transmissions",
+        "javascript:void(0)": "javascript: void (0)",
+        "Request Show or Movie": "Demana un programa o una pel·lícula",
+        "Mark as Unavailable": "Marqueu com a no disponible",
+        "Mark as Available": "Marca com a disponible",
+        "Approve": "Aprova",
+        "Start": "Començar",
+        "Everyone Refresh Seconds": "Refresqueu-ne tots els segons",
+        "Admin Refresh Seconds": "Actualització dels segons de l’administrador",
+        "Minimum Authentication for Time Display": "Autenticació mínima per a la visualització del temps",
+        "Show Ping Time": "Mostra el temps de ping",
+        "Offline Sound": "So fora de línia",
+        "Online Sound": "So en línia",
+        "Minimum Authentication for Message and Sound": "Autenticació mínima per a missatge i so",
+        "Minimum Authentication": "Autenticació mínima",
+        "Nginx Auth Debug": "Nginx Auth Debug",
+        "Hide Registration": "Oculta el registre",
+        "Lockout Groups To": "Grups de bloqueig a",
+        "Lockout Groups From": "Grups de bloqueig des de",
+        "Inactivity Lock": "Bloqueig d'inactivitat",
+        "Inactivity Timer [Minutes]": "Temporitzador d'inactivitat [minuts]",
+        "Emby Token": "Emby Token",
+        "http(s)://hostname:port": "http (s): // hostname: port",
+        "Emby URL": "URL Emby",
+        "cn=%s,dc=sub,dc=domain,dc=com": "cn =% s, dc = sub, dc = domini, dc = com",
+        "Host Base DN": "DN de base de host",
+        "http{s) | ftp(s) | ldap(s)://hostname:port": "http {s) | ftp (s) | ldap (s): // hostname: port",
+        "Host Address": "Adreça de l’amfitrió",
+        "Enable Plex oAuth": "Habiliteu Plex oAuth",
+        "Retrieve": "Recuperar",
+        "Use Get Plex Machine Button": "Utilitzeu el botó de la màquina Get Plex",
+        "Use Get Token Button": "Utilitzeu el botó Get Token",
+        "Plex Token": "Token de Plex",
+        "Authentication Backend": "Backend d’autenticació",
+        "Authentication Type": "Tipus d'autenticació",
+        "Generate": "Genera",
+        "Generate New API Key": "Genera una nova clau d’API",
+        "Organizr API": "Organizr API",
+        "Force Install Branch": "Força la branca d'instal·lació",
+        "Branch": "Branca",
+        "SSO": "SSO",
+        "Enable": "Habilita",
+        "Tautulli URL": "URL de Tautulli",
+        "Ombi URL": "URL Ombi",
+        "Plex Note": "Nota Plex",
+        "Admin username for Plex": "Nom d'usuari d'administrador per a Plex",
+        "Admin Username": "Nom d’usuari d’administrador",
+        "Click Main on the sub-menu above.": "Feu clic a Main (Principal) al submenú anterior.",
+        "Important Information": "Informació important",
+        "PING": "PING",
+        "Custom HTML/JavaScript": "HTML personalitzat / JavaScript",
+        "CustomHTML-2": "Personalitza HTML-2",
+        "CustomHTML-1": "Personalitza HTML-1",
+        "Refresh Seconds": "Actualitzar segons",
+        "Limit to User": "Limitar a l’usuari",
+        "Minimum Group to Request": "Grup mínim a sol·licitar",
+        "Token": "Token",
+        "URL": "URL",
+        "Ombi": "Ombi",
+        "Items Per Day": "Articles per dia",
+        "Time Format": "Format del temps",
+        "Default View": "Visualització predeterminada",
+        "Start Day": "Dia d'inici",
+        "SickRage": "SickRage",
+        "CouchPotato": "CouchPotato",
+        "Test Connection": "Prova la connexió",
+        "Please Save before Testing": "Deseu abans de provar",
+        "# of Days After": "Nombre de dies després",
+        "# of Days Before": "Nombre de dies abans",
+        "Radarr": "Radarr",
+        "Lidarr": "Lidarr",
+        "Show Unmonitored": "Mostra sense supervisar",
+        "Sonarr": "Sonarr",
+        "Hide Completed": "Oculta Completat",
+        "Hide Seeding": "Oculta la sembra",
+        "Deluge": "Deluge",
+        "Order": "Ordre",
+        "Status: [": "Estat: [",
+        "]": "]",
+        "rTorrent": "rTorrent",
+        "Reverse Sorting": "Ordenació inversa",
+        "qBittorrent": "qBittorrent",
+        "Transmission": "Transmissió",
+        "NZBGet": "NZBGet",
+        "SabNZBD": "SabNZBD",
+        "Image Cache Size": "Mida de la memòria cau de la imatge",
+        "Emby Tab WAN URL": "Tab emby URL WAN",
+        "Only use if you have Emby in a reverse proxy": "Utilitzeu-lo només si teniu Emby en un servidor intermediari invers",
+        "Emby Tab Name": "Nom de la fitxa Emby",
+        "Item Limit": "Límit d’element",
+        "Minimum Authorization": "Autorització mínima",
+        "User Information": "Informació de l'usuari",
+        "Emby": "Emby",
+        "Plex Tab WAN URL": "Plex URL WAN de la pestanya Plex",
+        "Only use if you have Plex in a reverse proxy": "Utilitzeu-lo només si teniu Plex en un servidor intermediari invers",
+        "Plex Tab Name": "Nom de la pestanya Plex",
+        "Media Server": "Servidor de suports",
+        "Plex": "Plex",
+        "separate by comma's": "separats per comes",
+        "iCal URL's": "URL de iCal",
+        "Enable iCal": "Habiliteu iCal",
+        "Calendar": "Calendari",
+        "Theme Javascript": "Tema Javascript",
+        "Custom Javascript": "Javascript personalitzat",
+        "Theme CSS [Can replace colors from above]": "Tema CSS [Pot substituir colors des de dalt]",
+        "Custom CSS [Can replace colors from above]": "CSS personalitzat [Pot substituir colors des de dalt]",
+        "Copy code and paste inside left box": "Copieu el codi i enganxeu-lo al quadre esquerre",
+        "Download and unzip file and place in": "Baixeu i descomprimiu el fitxer i introduïu-lo",
+        "Click [Generate your Favicons and HTML code]": "Feu clic a [Genera el vostre codi favicons i HTML]",
+        "Enter this path": "Introduïu aquest camí",
+        "At bottom of page on [Favicon Generator Options] under [Path] choose [I cannot or I do not want to place favicon files at the root of my web site.]": "A la part inferior de la pàgina [Opcions del generador de Favicon] a la secció [Camí] trieu [no puc o no vull posar fitxers favicon a l’arrel del meu lloc web.]",
+        "Edit settings to your liking": "Editeu la configuració al vostre gust",
+        "Choose your image to use": "Trieu la vostra imatge per utilitzar-la",
+        "Click [Select your Favicon picture]": "Feu clic a [Selecciona la imatge de Favicon]",
+        "Instructions": "Instruccions",
+        "Fav Icon Code": "Codi d'icones Fav",
+        "Test Message": "Missatge de prova",
+        "Position": "Posició",
+        "Style": "Estil",
+        "Theme": "Tema",
+        "Button Text Color": "Color del text del botó",
+        "Button Color": "Color del botó",
+        "Accent Text Color": "Color del text de l'accent",
+        "Accent Color": "Color de l'accent",
+        "Side Bar Text Color": "Color del text de la barra lateral",
+        "Side Bar Color": "Color de la barra lateral",
+        "Nav Bar Text Color": "Color del text de la barra de navegació",
+        "Nav Bar Color": "Color de la barra de navegació",
+        "Unsorted Tab Placement": "Col·locació de pestanyes sense classificar",
+        "Alternate Homepage Titles": "Títols alternatius de pàgina d'inici",
+        "Minimal Login Screen": "Pantalla d’inici de sessió mínima",
+        "Login Wallpaper": "Fons de pantalla d'inici de sessió",
+        "Use Logo instead of Title": "Utilitzeu el logotip en lloc del títol",
+        "Title": "Títol",
+        "Logo": "Logotip",
+        "Import Users": "Importa usuaris",
+        "Drop files here to upload": "Deixa anar els fitxers aquí per pujar-los",
+        "SpeedTest Settings": "Configuració de SpeedTest",
+        "PHP Mailer Settings": "Configuració del programari PHP",
+        "Invites Settings": "Configuració de les invitacions",
+        "Chat Settings": "Configuració del xat",
+        "Delete ": "Suprimeix ",
+        "App Cluster": "Clúster d’aplicacions",
+        "App ID": "ID de l'aplicació",
+        "API Secret": "API secreta",
+        "Auth Key": "Clau d’autenticació",
+        "Use Pusher SSL": "Utilitzeu el polsador SSL",
+        "Message Sound": "Missatge de so",
+        "# of Previous Messages": "Nombre de missatges anteriors",
+        "Note": "Nota",
+        "Libraries": "Biblioteques",
+        "Body": "Cos",
+        "Name": "Nom",
+        "Template #4": "Plantilla # 4",
+        "Template #3": "Plantilla # 3",
+        "Template #2": "Plantilla # 2",
+        "Reminder Template": "Plantilla de recordatori",
+        "Invite User": "Convida l'usuari",
+        "Reset Password": "Restablir la contrasenya",
+        "New Registration": "Nou registre",
+        "Edit Template": "Edita la plantilla",
+        "Send Welcome E-Mail": "Envia un correu electrònic de benvinguda",
+        "Full URL": "URL complet",
+        "WAN Logo URL": "URL del logotip WAN",
+        "https://domain.com/": "https://domini.com/",
+        "Domain Link Override": "Substitució d'enllaços de domini",
+        "Send": "Envia",
+        "Send Test": "Enviar prova",
+        "i.e. same as username": "és a dir, igual que el nom d’usuari",
+        "Sender Email": "Correu electrònic del remitent",
+        "Sender Name": "Nom del remitent",
+        "Verify Certificate": "Verifiqueu el certificat",
+        "Authentication": "Autenticació",
+        "SMTP Port": "Port SMTP",
+        "SMTP Host": "Host SMTP",
+        "Results For cmd:": "Resultats per cmd:",
+        "Organizr Information:": "Informació de l’Organizr:",
+        "DB Schema": "Esquema del DB",
+        "Misc SSO": "Diversos SSO",
+        "Tautulli SSO": "Tautulli SSO",
+        "Plex SSO": "Plex SSO",
+        "Ombi SSO": "Ombi SSO",
+        "Commands": "Ordres",
+        "Input Command": "Ordre d’entrada",
+        "Organizr Debug Area": "Àrea de depuració d’Organizr",
+        "Google Ads": "Google Ads",
+        "DB Folder": "Carpeta de base de dades",
+        "API Folder": "Carpeta de l'API",
+        "Root Folder": "Carpeta arrel",
+        "Organizr Paths": "Camins d’Organizr",
+        "Organizr News": "Notícies d'Organizr",
+        "Close Error": "Tanca l’error",
+        "An Error Occured": "Va ocórrer un error",
+        "Debug Area": "Àrea de depuració",
+        "Tab Local URL": "Pestanya URL local",
+        "PRELOAD": "PRELOAD",
+        "Use Ombi Alias Names": "Utilitzeu els noms d’àlies d’Ombi",
+        "TV Show Default Request": "Sol·licitud per defecte del programa de televisió",
+        "Add to Combined Downloader": "Afegiu-ho a Combinació de descàrregues",
+        "http(s)://hostname:port/xmlrpc": "http(s)://hostname:port/xmlrpc\n",
+        "rTorrent API URL Override": "Anular l’URL de l’APT rTorrent",
+        "Enable Notify Sounds": "Activa els sons de notificacions",
+        "Remember Me Length": "Recorda la meva longitud",
+        "Minimum Authentication for Debug Area": "Autenticació mínima per a l'àrea de depuració",
+        "Account DN": "Compte DN",
+        "Bind Username": "Enllaçar nom d’usuari",
+        "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld": "El sufix del compte - comença amb coma -, ou = persones, dc = domini, dc = tld",
+        "Account Suffix": "Sufix de compte",
+        "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP": "Prefix del compte: és a dir, controlador del controlador Nom d’usuari per a AD - uid = per a OpenLDAP",
+        "Account Prefix": "Prefix del compte",
+        "LDAP Backend Type": "Tipus de dorsal LDAP",
+        "Strict Plex Friends": "Amics de Plex estrictes"
+    }
+}

+ 37 - 37
js/langpack/da[Danish].json

@@ -479,42 +479,42 @@
         "Authentication": "Authentication",
         "SMTP Port": "SMTP Port",
         "SMTP Host": "SMTP Host",
-        "Results For cmd:": "",
-        "Organizr Information:": "",
-        "DB Schema": "",
-        "Misc SSO": "",
-        "Tautulli SSO": "",
-        "Plex SSO": "",
-        "Ombi SSO": "",
-        "Commands": "",
-        "Input Command": "",
-        "Organizr Debug Area": "",
-        "Google Ads": "",
-        "DB Folder": "",
-        "API Folder": "",
-        "Root Folder": "",
-        "Organizr Paths": "",
-        "Organizr News": "",
-        "Close Error": "",
-        "An Error Occured": "",
-        "Debug Area": "",
-        "Tab Local URL": "",
-        "PRELOAD": "",
-        "Use Ombi Alias Names": "",
-        "TV Show Default Request": "",
-        "Add to Combined Downloader": "",
-        "http(s)://hostname:port/xmlrpc": "",
-        "rTorrent API URL Override": "",
-        "Enable Notify Sounds": "",
-        "Remember Me Length": "",
-        "Minimum Authentication for Debug Area": "",
-        "Account DN": "",
-        "Bind Username": "",
-        "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld": "",
-        "Account Suffix": "",
-        "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP": "",
-        "Account Prefix": "",
-        "LDAP Backend Type": "",
-        "Strict Plex Friends": ""
+        "Results For cmd:": "Result For cmd:",
+        "Organizr Information:": "Organizr Information:",
+        "DB Schema": "DB Schema",
+        "Misc SSO": "Misc SSO",
+        "Tautulli SSO": "Tautulli SSO",
+        "Plex SSO": "Plex SSO",
+        "Ombi SSO": "Ombi SSO",
+        "Commands": "Commands",
+        "Input Command": "Input Command",
+        "Organizr Debug Area": "Organizr Debug Area",
+        "Google Ads": "Google Ads",
+        "DB Folder": "DB Folder",
+        "API Folder": "API Folder",
+        "Root Folder": "Root Folder",
+        "Organizr Paths": "Organizr Paths",
+        "Organizr News": "Organizr News",
+        "Close Error": "Close Error",
+        "An Error Occured": "An Error Occurred",
+        "Debug Area": "Debug Area",
+        "Tab Local URL": "Tab Local URL",
+        "PRELOAD": "PRELOAD",
+        "Use Ombi Alias Names": "Use Ombi Alias Names",
+        "TV Show Default Request": "TV Show Default Request",
+        "Add to Combined Downloader": "Add to Combined Downloader",
+        "http(s)://hostname:port/xmlrpc": "http(s)://hostname:port/xmlrpc",
+        "rTorrent API URL Override": "rTorrent API URL Override",
+        "Enable Notify Sounds": "Enable Notify Sounds",
+        "Remember Me Length": "Remember Me Length",
+        "Minimum Authentication for Debug Area": "Minimum Authentication for Debug Area",
+        "Account DN": "Account DN",
+        "Bind Username": "Bind Username",
+        "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld": "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld",
+        "Account Suffix": "Account Suffix",
+        "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP": "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP",
+        "Account Prefix": "Account Prefix",
+        "LDAP Backend Type": "LDAP Backend Type",
+        "Strict Plex Friends": "Strict Plex Friends"
     }
 }

+ 37 - 37
js/langpack/de-ch[German (Switzerland)].json

@@ -479,42 +479,42 @@
         "Authentication": "Authentifizierig",
         "SMTP Port": "SMTP Port",
         "SMTP Host": "SMTP Port",
-        "Results For cmd:": "",
-        "Organizr Information:": "",
-        "DB Schema": "",
-        "Misc SSO": "",
-        "Tautulli SSO": "",
-        "Plex SSO": "",
-        "Ombi SSO": "",
-        "Commands": "",
-        "Input Command": "",
-        "Organizr Debug Area": "",
-        "Google Ads": "",
-        "DB Folder": "",
-        "API Folder": "",
-        "Root Folder": "",
-        "Organizr Paths": "",
-        "Organizr News": "",
-        "Close Error": "",
-        "An Error Occured": "",
-        "Debug Area": "",
-        "Tab Local URL": "",
-        "PRELOAD": "",
-        "Use Ombi Alias Names": "",
-        "TV Show Default Request": "",
-        "Add to Combined Downloader": "",
-        "http(s)://hostname:port/xmlrpc": "",
-        "rTorrent API URL Override": "",
-        "Enable Notify Sounds": "",
-        "Remember Me Length": "",
-        "Minimum Authentication for Debug Area": "",
-        "Account DN": "",
-        "Bind Username": "",
-        "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld": "",
-        "Account Suffix": "",
-        "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP": "",
-        "Account Prefix": "",
-        "LDAP Backend Type": "",
-        "Strict Plex Friends": ""
+        "Results For cmd:": "Result For cmd:",
+        "Organizr Information:": "Organizr Information:",
+        "DB Schema": "DB Schema",
+        "Misc SSO": "Misc SSO",
+        "Tautulli SSO": "Tautulli SSO",
+        "Plex SSO": "Plex SSO",
+        "Ombi SSO": "Ombi SSO",
+        "Commands": "Commands",
+        "Input Command": "Input Command",
+        "Organizr Debug Area": "Organizr Debug Area",
+        "Google Ads": "Google Ads",
+        "DB Folder": "DB Folder",
+        "API Folder": "API Folder",
+        "Root Folder": "Root Folder",
+        "Organizr Paths": "Organizr Paths",
+        "Organizr News": "Organizr News",
+        "Close Error": "Close Error",
+        "An Error Occured": "An Error Occurred",
+        "Debug Area": "Debug Area",
+        "Tab Local URL": "Tab Local URL",
+        "PRELOAD": "PRELOAD",
+        "Use Ombi Alias Names": "Use Ombi Alias Names",
+        "TV Show Default Request": "TV Show Default Request",
+        "Add to Combined Downloader": "Add to Combined Downloader",
+        "http(s)://hostname:port/xmlrpc": "http(s)://hostname:port/xmlrpc",
+        "rTorrent API URL Override": "rTorrent API URL Override",
+        "Enable Notify Sounds": "Enable Notify Sounds",
+        "Remember Me Length": "Remember Me Length",
+        "Minimum Authentication for Debug Area": "Minimum Authentication for Debug Area",
+        "Account DN": "Account DN",
+        "Bind Username": "Bind Username",
+        "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld": "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld",
+        "Account Suffix": "Account Suffix",
+        "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP": "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP",
+        "Account Prefix": "Account Prefix",
+        "LDAP Backend Type": "LDAP Backend Type",
+        "Strict Plex Friends": "Strict Plex Friends"
     }
 }

+ 37 - 37
js/langpack/el[Greek].json

@@ -479,42 +479,42 @@
         "Authentication": "Authentication",
         "SMTP Port": "SMTP Port",
         "SMTP Host": "SMTP Host",
-        "Results For cmd:": "",
-        "Organizr Information:": "",
-        "DB Schema": "",
-        "Misc SSO": "",
-        "Tautulli SSO": "",
-        "Plex SSO": "",
-        "Ombi SSO": "",
-        "Commands": "",
-        "Input Command": "",
-        "Organizr Debug Area": "",
-        "Google Ads": "",
-        "DB Folder": "",
-        "API Folder": "",
-        "Root Folder": "",
-        "Organizr Paths": "",
-        "Organizr News": "",
-        "Close Error": "",
-        "An Error Occured": "",
-        "Debug Area": "",
-        "Tab Local URL": "",
-        "PRELOAD": "",
-        "Use Ombi Alias Names": "",
-        "TV Show Default Request": "",
-        "Add to Combined Downloader": "",
-        "http(s)://hostname:port/xmlrpc": "",
-        "rTorrent API URL Override": "",
-        "Enable Notify Sounds": "",
-        "Remember Me Length": "",
-        "Minimum Authentication for Debug Area": "",
-        "Account DN": "",
-        "Bind Username": "",
-        "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld": "",
-        "Account Suffix": "",
-        "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP": "",
-        "Account Prefix": "",
-        "LDAP Backend Type": "",
-        "Strict Plex Friends": ""
+        "Results For cmd:": "Result For cmd:",
+        "Organizr Information:": "Organizr Information:",
+        "DB Schema": "DB Schema",
+        "Misc SSO": "Misc SSO",
+        "Tautulli SSO": "Tautulli SSO",
+        "Plex SSO": "Plex SSO",
+        "Ombi SSO": "Ombi SSO",
+        "Commands": "Commands",
+        "Input Command": "Input Command",
+        "Organizr Debug Area": "Organizr Debug Area",
+        "Google Ads": "Google Ads",
+        "DB Folder": "DB Folder",
+        "API Folder": "API Folder",
+        "Root Folder": "Root Folder",
+        "Organizr Paths": "Organizr Paths",
+        "Organizr News": "Organizr News",
+        "Close Error": "Close Error",
+        "An Error Occured": "An Error Occurred",
+        "Debug Area": "Debug Area",
+        "Tab Local URL": "Tab Local URL",
+        "PRELOAD": "PRELOAD",
+        "Use Ombi Alias Names": "Use Ombi Alias Names",
+        "TV Show Default Request": "TV Show Default Request",
+        "Add to Combined Downloader": "Add to Combined Downloader",
+        "http(s)://hostname:port/xmlrpc": "http(s)://hostname:port/xmlrpc",
+        "rTorrent API URL Override": "rTorrent API URL Override",
+        "Enable Notify Sounds": "Enable Notify Sounds",
+        "Remember Me Length": "Remember Me Length",
+        "Minimum Authentication for Debug Area": "Minimum Authentication for Debug Area",
+        "Account DN": "Account DN",
+        "Bind Username": "Bind Username",
+        "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld": "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld",
+        "Account Suffix": "Account Suffix",
+        "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP": "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP",
+        "Account Prefix": "Account Prefix",
+        "LDAP Backend Type": "LDAP Backend Type",
+        "Strict Plex Friends": "Strict Plex Friends"
     }
 }

+ 37 - 37
js/langpack/en[English].json

@@ -479,42 +479,42 @@
         "Authentication": "Authentication",
         "SMTP Port": "SMTP Port",
         "SMTP Host": "SMTP Host",
-        "Results For cmd:": "",
-        "Organizr Information:": "",
-        "DB Schema": "",
-        "Misc SSO": "",
-        "Tautulli SSO": "",
-        "Plex SSO": "",
-        "Ombi SSO": "",
-        "Commands": "",
-        "Input Command": "",
-        "Organizr Debug Area": "",
-        "Google Ads": "",
-        "DB Folder": "",
-        "API Folder": "",
-        "Root Folder": "",
-        "Organizr Paths": "",
-        "Organizr News": "",
-        "Close Error": "",
-        "An Error Occured": "",
-        "Debug Area": "",
-        "Tab Local URL": "",
-        "PRELOAD": "",
-        "Use Ombi Alias Names": "",
-        "TV Show Default Request": "",
-        "Add to Combined Downloader": "",
-        "http(s)://hostname:port/xmlrpc": "",
-        "rTorrent API URL Override": "",
-        "Enable Notify Sounds": "",
-        "Remember Me Length": "",
-        "Minimum Authentication for Debug Area": "",
-        "Account DN": "",
-        "Bind Username": "",
-        "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld": "",
-        "Account Suffix": "",
-        "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP": "",
-        "Account Prefix": "",
-        "LDAP Backend Type": "",
-        "Strict Plex Friends": ""
+        "Results For cmd:": "Result For cmd:",
+        "Organizr Information:": "Organizr Information:",
+        "DB Schema": "DB Schema",
+        "Misc SSO": "Misc SSO",
+        "Tautulli SSO": "Tautulli SSO",
+        "Plex SSO": "Plex SSO",
+        "Ombi SSO": "Ombi SSO",
+        "Commands": "Commands",
+        "Input Command": "Input Command",
+        "Organizr Debug Area": "Organizr Debug Area",
+        "Google Ads": "Google Ads",
+        "DB Folder": "DB Folder",
+        "API Folder": "API Folder",
+        "Root Folder": "Root Folder",
+        "Organizr Paths": "Organizr Paths",
+        "Organizr News": "Organizr News",
+        "Close Error": "Close Error",
+        "An Error Occured": "An Error Occurred",
+        "Debug Area": "Debug Area",
+        "Tab Local URL": "Tab Local URL",
+        "PRELOAD": "PRELOAD",
+        "Use Ombi Alias Names": "Use Ombi Alias Names",
+        "TV Show Default Request": "TV Show Default Request",
+        "Add to Combined Downloader": "Add to Combined Downloader",
+        "http(s)://hostname:port/xmlrpc": "http(s)://hostname:port/xmlrpc",
+        "rTorrent API URL Override": "rTorrent API URL Override",
+        "Enable Notify Sounds": "Enable Notify Sounds",
+        "Remember Me Length": "Remember Me Length",
+        "Minimum Authentication for Debug Area": "Minimum Authentication for Debug Area",
+        "Account DN": "Account DN",
+        "Bind Username": "Bind Username",
+        "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld": "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld",
+        "Account Suffix": "Account Suffix",
+        "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP": "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP",
+        "Account Prefix": "Account Prefix",
+        "LDAP Backend Type": "LDAP Backend Type",
+        "Strict Plex Friends": "Strict Plex Friends"
     }
 }

+ 37 - 37
js/langpack/es[Spanish].json

@@ -479,42 +479,42 @@
         "Authentication": "Autenticación",
         "SMTP Port": "Puerto SMTP",
         "SMTP Host": "Servidor de correo electrónico",
-        "Results For cmd:": "",
-        "Organizr Information:": "",
-        "DB Schema": "",
-        "Misc SSO": "",
-        "Tautulli SSO": "",
-        "Plex SSO": "",
-        "Ombi SSO": "",
-        "Commands": "",
-        "Input Command": "",
-        "Organizr Debug Area": "",
-        "Google Ads": "",
-        "DB Folder": "",
-        "API Folder": "",
-        "Root Folder": "",
-        "Organizr Paths": "",
-        "Organizr News": "",
-        "Close Error": "",
-        "An Error Occured": "",
-        "Debug Area": "",
-        "Tab Local URL": "",
-        "PRELOAD": "",
-        "Use Ombi Alias Names": "",
-        "TV Show Default Request": "",
-        "Add to Combined Downloader": "",
-        "http(s)://hostname:port/xmlrpc": "",
-        "rTorrent API URL Override": "",
-        "Enable Notify Sounds": "",
-        "Remember Me Length": "",
-        "Minimum Authentication for Debug Area": "",
-        "Account DN": "",
-        "Bind Username": "",
-        "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld": "",
-        "Account Suffix": "",
-        "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP": "",
-        "Account Prefix": "",
-        "LDAP Backend Type": "",
-        "Strict Plex Friends": ""
+        "Results For cmd:": "Resultado Para cmd:",
+        "Organizr Information:": "Información sobre Organizr",
+        "DB Schema": "DB Schema",
+        "Misc SSO": "Misc SSO",
+        "Tautulli SSO": "Tautulli SSO",
+        "Plex SSO": "Plex SSO",
+        "Ombi SSO": "Ombi SSO",
+        "Commands": "Commands",
+        "Input Command": "Input Command",
+        "Organizr Debug Area": "Organizr Debug Area",
+        "Google Ads": "\n Google Ads",
+        "DB Folder": "Carpeta Bases de Datos",
+        "API Folder": "Carpeta API",
+        "Root Folder": "Carpeta Root",
+        "Organizr Paths": "Organizr Rutas",
+        "Organizr News": "Noticias Organizr",
+        "Close Error": "Error de cierre",
+        "An Error Occured": "Ha ocurrido un error",
+        "Debug Area": "Zona Debug",
+        "Tab Local URL": "Pestaña Local URL",
+        "PRELOAD": "PRELOAD",
+        "Use Ombi Alias Names": "Use Ombi Alias Names",
+        "TV Show Default Request": "TV Show Default Request",
+        "Add to Combined Downloader": "Add to Combined Downloader",
+        "http(s)://hostname:port/xmlrpc": "http(s)://hostname:port/xmlrpc",
+        "rTorrent API URL Override": "rTorrent API URL Override",
+        "Enable Notify Sounds": "Enable Notify Sounds",
+        "Remember Me Length": "Remember Me Length",
+        "Minimum Authentication for Debug Area": "Minimum Authentication for Debug Area",
+        "Account DN": "Account DN",
+        "Bind Username": "Bind Username",
+        "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld": "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld",
+        "Account Suffix": "Account Suffix",
+        "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP": "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP",
+        "Account Prefix": "Account Prefix",
+        "LDAP Backend Type": "LDAP Backend Type",
+        "Strict Plex Friends": "Strict Plex Friends"
     }
 }

+ 39 - 39
js/langpack/fr[French].json

@@ -121,7 +121,7 @@
         "Edit Tab": "Éditer l'onglet",
         "Add Tab": "Ajouter un onglet",
         "Add New Tab": "Ajouter un nouvel onglet",
-        "SPLASH": "ANIMATION DE CHARGEMENT",
+        "SPLASH": "DÉMARRAGE",
         "ACTIVE": "ACTIF",
         "TYPE": "TYPE",
         "GROUP": "GROUPE",
@@ -242,7 +242,7 @@
         "Organizr Mod Picks": "Sélection de modules Organizr",
         "Requests": "Requêtes",
         "Become Sponsor": "Devenir partenaire",
-        "Splash Page": "Page d'initialisation",
+        "Splash Page": "Page de démarage",
         "Lock Screen": "Écran de verrouillage",
         "If you signed in with a Emby Acct... Please use the following link to change your password there:": "Si vous vous êtes connecté avec un compte Emby... Veuillez utiliser le lien suivant pour y changer votre mot de passe :",
         "Password Notice": "Remarque à propos du mot de passe",
@@ -479,42 +479,42 @@
         "Authentication": "Authentification",
         "SMTP Port": "Port SMTP",
         "SMTP Host": "Hôte SMTP",
-        "Results For cmd:": "",
-        "Organizr Information:": "",
-        "DB Schema": "",
-        "Misc SSO": "",
-        "Tautulli SSO": "",
-        "Plex SSO": "",
-        "Ombi SSO": "",
-        "Commands": "",
-        "Input Command": "",
-        "Organizr Debug Area": "",
-        "Google Ads": "",
-        "DB Folder": "",
-        "API Folder": "",
-        "Root Folder": "",
-        "Organizr Paths": "",
-        "Organizr News": "",
-        "Close Error": "",
-        "An Error Occured": "",
-        "Debug Area": "",
-        "Tab Local URL": "",
-        "PRELOAD": "",
-        "Use Ombi Alias Names": "",
-        "TV Show Default Request": "",
-        "Add to Combined Downloader": "",
-        "http(s)://hostname:port/xmlrpc": "",
-        "rTorrent API URL Override": "",
-        "Enable Notify Sounds": "",
-        "Remember Me Length": "",
-        "Minimum Authentication for Debug Area": "",
-        "Account DN": "",
-        "Bind Username": "",
-        "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld": "",
-        "Account Suffix": "",
-        "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP": "",
-        "Account Prefix": "",
-        "LDAP Backend Type": "",
-        "Strict Plex Friends": ""
+        "Results For cmd:": "Résultats pour la commande :",
+        "Organizr Information:": "Informations sur Organizr",
+        "DB Schema": "Schema BDD",
+        "Misc SSO": "Autre SSO",
+        "Tautulli SSO": "Tautulli SSO",
+        "Plex SSO": "Plex SSO",
+        "Ombi SSO": "Ombi SSO",
+        "Commands": "Commandes",
+        "Input Command": "Saisie de commande",
+        "Organizr Debug Area": "Zone de debug Organizr",
+        "Google Ads": "Google Ads",
+        "DB Folder": "Chemin de la BDD",
+        "API Folder": "Chemin de l'API",
+        "Root Folder": "Chemin racine",
+        "Organizr Paths": "Chemins Organizr",
+        "Organizr News": "Nouveautés Organizr",
+        "Close Error": "Fermer l'erreur",
+        "An Error Occured": "Une erreur s'est produite",
+        "Debug Area": "Zone de debug",
+        "Tab Local URL": "URL Local de l'onglet",
+        "PRELOAD": "PRÉCHARGER",
+        "Use Ombi Alias Names": "Utiliser les alias Ombi",
+        "TV Show Default Request": "TV Show Default Request",
+        "Add to Combined Downloader": "Ajouter aux Téléchargements Combinés",
+        "http(s)://hostname:port/xmlrpc": " http(s)://hostname:port/xmlrpc",
+        "rTorrent API URL Override": "rTorrent API URL Override",
+        "Enable Notify Sounds": "Activer les sons de notifications",
+        "Remember Me Length": "Durée du \"Se souvenir de moi\"",
+        "Minimum Authentication for Debug Area": "Niveau d'authentification minimum pour la zone de debug",
+        "Account DN": "Account DN",
+        "Bind Username": "Bind Username",
+        "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld": "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld",
+        "Account Suffix": "Account Suffix",
+        "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP": "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP",
+        "Account Prefix": "Account Prefix",
+        "LDAP Backend Type": "LDAP Backend Type",
+        "Strict Plex Friends": "Amis Plex Stricts"
     }
 }

+ 37 - 37
js/langpack/hu[Hungarian].json

@@ -479,42 +479,42 @@
         "Authentication": "Authentication",
         "SMTP Port": "SMTP Port",
         "SMTP Host": "SMTP Host",
-        "Results For cmd:": "",
-        "Organizr Information:": "",
-        "DB Schema": "",
-        "Misc SSO": "",
-        "Tautulli SSO": "",
-        "Plex SSO": "",
-        "Ombi SSO": "",
-        "Commands": "",
-        "Input Command": "",
-        "Organizr Debug Area": "",
-        "Google Ads": "",
-        "DB Folder": "",
-        "API Folder": "",
-        "Root Folder": "",
-        "Organizr Paths": "",
-        "Organizr News": "",
-        "Close Error": "",
-        "An Error Occured": "",
-        "Debug Area": "",
-        "Tab Local URL": "",
-        "PRELOAD": "",
-        "Use Ombi Alias Names": "",
-        "TV Show Default Request": "",
-        "Add to Combined Downloader": "",
-        "http(s)://hostname:port/xmlrpc": "",
-        "rTorrent API URL Override": "",
-        "Enable Notify Sounds": "",
-        "Remember Me Length": "",
-        "Minimum Authentication for Debug Area": "",
-        "Account DN": "",
-        "Bind Username": "",
-        "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld": "",
-        "Account Suffix": "",
-        "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP": "",
-        "Account Prefix": "",
-        "LDAP Backend Type": "",
-        "Strict Plex Friends": ""
+        "Results For cmd:": "Result For cmd:",
+        "Organizr Information:": "Organizr Information:",
+        "DB Schema": "DB Schema",
+        "Misc SSO": "Misc SSO",
+        "Tautulli SSO": "Tautulli SSO",
+        "Plex SSO": "Plex SSO",
+        "Ombi SSO": "Ombi SSO",
+        "Commands": "Commands",
+        "Input Command": "Input Command",
+        "Organizr Debug Area": "Organizr Debug Area",
+        "Google Ads": "Google Ads",
+        "DB Folder": "DB Folder",
+        "API Folder": "API Folder",
+        "Root Folder": "Root Folder",
+        "Organizr Paths": "Organizr Paths",
+        "Organizr News": "Organizr News",
+        "Close Error": "Close Error",
+        "An Error Occured": "An Error Occurred",
+        "Debug Area": "Debug Area",
+        "Tab Local URL": "Tab Local URL",
+        "PRELOAD": "PRELOAD",
+        "Use Ombi Alias Names": "Use Ombi Alias Names",
+        "TV Show Default Request": "TV Show Default Request",
+        "Add to Combined Downloader": "Add to Combined Downloader",
+        "http(s)://hostname:port/xmlrpc": "http(s)://hostname:port/xmlrpc",
+        "rTorrent API URL Override": "rTorrent API URL Override",
+        "Enable Notify Sounds": "Enable Notify Sounds",
+        "Remember Me Length": "Remember Me Length",
+        "Minimum Authentication for Debug Area": "Minimum Authentication for Debug Area",
+        "Account DN": "Account DN",
+        "Bind Username": "Bind Username",
+        "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld": "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld",
+        "Account Suffix": "Account Suffix",
+        "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP": "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP",
+        "Account Prefix": "Account Prefix",
+        "LDAP Backend Type": "LDAP Backend Type",
+        "Strict Plex Friends": "Strict Plex Friends"
     }
 }

+ 4 - 4
js/langpack/it[Italian].json

@@ -297,12 +297,12 @@
         "Misc Options": "Misc Options",
         "Search My Media": "Search My Media",
         "Import Plex Users": "Import Plex Users",
-        "Import": "Import",
+        "Import": "Importa",
         "INSTALL": "INSTALL",
         "INFO": "INFO",
-        "Day": "Day",
-        "Week": "Week",
-        "Month": "Month",
+        "Day": "Giorno",
+        "Week": "Settimana",
+        "Month": "Mese",
         "List": "List",
         "Streams": "Streams",
         "javascript:void(0)": "javascript:void(0)",

+ 37 - 37
js/langpack/lt[Lithuanian].json

@@ -479,42 +479,42 @@
         "Authentication": "Autentifikacija",
         "SMTP Port": "SMTP portas",
         "SMTP Host": "SMTP serveris",
-        "Results For cmd:": "",
-        "Organizr Information:": "",
-        "DB Schema": "",
-        "Misc SSO": "",
-        "Tautulli SSO": "",
-        "Plex SSO": "",
-        "Ombi SSO": "",
-        "Commands": "",
-        "Input Command": "",
-        "Organizr Debug Area": "",
-        "Google Ads": "",
-        "DB Folder": "",
-        "API Folder": "",
-        "Root Folder": "",
-        "Organizr Paths": "",
-        "Organizr News": "",
-        "Close Error": "",
-        "An Error Occured": "",
-        "Debug Area": "",
-        "Tab Local URL": "",
-        "PRELOAD": "",
-        "Use Ombi Alias Names": "",
-        "TV Show Default Request": "",
-        "Add to Combined Downloader": "",
-        "http(s)://hostname:port/xmlrpc": "",
-        "rTorrent API URL Override": "",
-        "Enable Notify Sounds": "",
-        "Remember Me Length": "",
-        "Minimum Authentication for Debug Area": "",
-        "Account DN": "",
-        "Bind Username": "",
-        "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld": "",
-        "Account Suffix": "",
-        "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP": "",
-        "Account Prefix": "",
-        "LDAP Backend Type": "",
-        "Strict Plex Friends": ""
+        "Results For cmd:": "Result For cmd:",
+        "Organizr Information:": "Organizr Information:",
+        "DB Schema": "DB Schema",
+        "Misc SSO": "Misc SSO",
+        "Tautulli SSO": "Tautulli SSO",
+        "Plex SSO": "Plex SSO",
+        "Ombi SSO": "Ombi SSO",
+        "Commands": "Commands",
+        "Input Command": "Input Command",
+        "Organizr Debug Area": "Organizr Debug Area",
+        "Google Ads": "Google Ads",
+        "DB Folder": "DB Folder",
+        "API Folder": "API Folder",
+        "Root Folder": "Root Folder",
+        "Organizr Paths": "Organizr Paths",
+        "Organizr News": "Organizr News",
+        "Close Error": "Close Error",
+        "An Error Occured": "An Error Occurred",
+        "Debug Area": "Debug Area",
+        "Tab Local URL": "Tab Local URL",
+        "PRELOAD": "PRELOAD",
+        "Use Ombi Alias Names": "Use Ombi Alias Names",
+        "TV Show Default Request": "TV Show Default Request",
+        "Add to Combined Downloader": "Add to Combined Downloader",
+        "http(s)://hostname:port/xmlrpc": "http(s)://hostname:port/xmlrpc",
+        "rTorrent API URL Override": "rTorrent API URL Override",
+        "Enable Notify Sounds": "Enable Notify Sounds",
+        "Remember Me Length": "Remember Me Length",
+        "Minimum Authentication for Debug Area": "Minimum Authentication for Debug Area",
+        "Account DN": "Account DN",
+        "Bind Username": "Bind Username",
+        "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld": "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld",
+        "Account Suffix": "Account Suffix",
+        "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP": "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP",
+        "Account Prefix": "Account Prefix",
+        "LDAP Backend Type": "LDAP Backend Type",
+        "Strict Plex Friends": "Strict Plex Friends"
     }
 }

+ 37 - 37
js/langpack/nb[Bokm&aring;l].json

@@ -479,42 +479,42 @@
         "Authentication": "Authentication",
         "SMTP Port": "SMTP Port",
         "SMTP Host": "SMTP Host",
-        "Results For cmd:": "",
-        "Organizr Information:": "",
-        "DB Schema": "",
-        "Misc SSO": "",
-        "Tautulli SSO": "",
-        "Plex SSO": "",
-        "Ombi SSO": "",
-        "Commands": "",
-        "Input Command": "",
-        "Organizr Debug Area": "",
-        "Google Ads": "",
-        "DB Folder": "",
-        "API Folder": "",
-        "Root Folder": "",
-        "Organizr Paths": "",
-        "Organizr News": "",
-        "Close Error": "",
-        "An Error Occured": "",
-        "Debug Area": "",
-        "Tab Local URL": "",
-        "PRELOAD": "",
-        "Use Ombi Alias Names": "",
-        "TV Show Default Request": "",
-        "Add to Combined Downloader": "",
-        "http(s)://hostname:port/xmlrpc": "",
-        "rTorrent API URL Override": "",
-        "Enable Notify Sounds": "",
-        "Remember Me Length": "",
-        "Minimum Authentication for Debug Area": "",
-        "Account DN": "",
-        "Bind Username": "",
-        "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld": "",
-        "Account Suffix": "",
-        "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP": "",
-        "Account Prefix": "",
-        "LDAP Backend Type": "",
-        "Strict Plex Friends": ""
+        "Results For cmd:": "Result For cmd:",
+        "Organizr Information:": "Organizr Information:",
+        "DB Schema": "DB Schema",
+        "Misc SSO": "Misc SSO",
+        "Tautulli SSO": "Tautulli SSO",
+        "Plex SSO": "Plex SSO",
+        "Ombi SSO": "Ombi SSO",
+        "Commands": "Commands",
+        "Input Command": "Input Command",
+        "Organizr Debug Area": "Organizr Debug Area",
+        "Google Ads": "Google Ads",
+        "DB Folder": "DB Folder",
+        "API Folder": "API Folder",
+        "Root Folder": "Root Folder",
+        "Organizr Paths": "Organizr Paths",
+        "Organizr News": "Organizr News",
+        "Close Error": "Close Error",
+        "An Error Occured": "An Error Occurred",
+        "Debug Area": "Debug Area",
+        "Tab Local URL": "Tab Local URL",
+        "PRELOAD": "PRELOAD",
+        "Use Ombi Alias Names": "Use Ombi Alias Names",
+        "TV Show Default Request": "TV Show Default Request",
+        "Add to Combined Downloader": "Add to Combined Downloader",
+        "http(s)://hostname:port/xmlrpc": "http(s)://hostname:port/xmlrpc",
+        "rTorrent API URL Override": "rTorrent API URL Override",
+        "Enable Notify Sounds": "Enable Notify Sounds",
+        "Remember Me Length": "Remember Me Length",
+        "Minimum Authentication for Debug Area": "Minimum Authentication for Debug Area",
+        "Account DN": "Account DN",
+        "Bind Username": "Bind Username",
+        "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld": "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld",
+        "Account Suffix": "Account Suffix",
+        "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP": "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP",
+        "Account Prefix": "Account Prefix",
+        "LDAP Backend Type": "LDAP Backend Type",
+        "Strict Plex Friends": "Strict Plex Friends"
     }
 }

+ 9 - 9
js/langpack/nl[Dutch].json

@@ -479,25 +479,25 @@
         "Authentication": "Authenticatie",
         "SMTP Port": "SMTP Pport",
         "SMTP Host": "SMTP Host",
-        "Results For cmd:": "Result For cmd:",
+        "Results For cmd:": "Resultaat Voor cmd:",
         "Organizr Information:": "Organizr Informatie:",
         "DB Schema": "DB Schema",
         "Misc SSO": "Overige SSO",
         "Tautulli SSO": "Tautulli SSO",
         "Plex SSO": "Plex SSO",
         "Ombi SSO": "Ombi SSO",
-        "Commands": "Commands",
-        "Input Command": "Input Command",
-        "Organizr Debug Area": "Organizr Debug Area",
+        "Commands": "Commando's",
+        "Input Command": "Invoer Commando",
+        "Organizr Debug Area": "Organizr Debug Omgeving",
         "Google Ads": "Google Ads",
         "DB Folder": "DB Map",
-        "API Folder": "API Folder",
+        "API Folder": "API Map",
         "Root Folder": "Root-map",
         "Organizr Paths": "Organizr Mappen",
         "Organizr News": "Organizr Nieuws",
-        "Close Error": "Close Error",
+        "Close Error": "Sluit Error",
         "An Error Occured": "Er is een fout opgetreden",
-        "Debug Area": "Debug Area",
+        "Debug Area": "Debug Omgeving",
         "Tab Local URL": "Lokale Tabblad URL",
         "PRELOAD": "PRELOAD",
         "Use Ombi Alias Names": "Gebruik Ombi Alias Namen",
@@ -507,9 +507,9 @@
         "rTorrent API URL Override": "rTorrent API URL Override",
         "Enable Notify Sounds": "Zet Notify geluiden aan",
         "Remember Me Length": "Onthoudt Mij Lengte",
-        "Minimum Authentication for Debug Area": "Minimum Authentication for Debug Area",
+        "Minimum Authentication for Debug Area": "Minimum Authenticatie voor Debug Omgeving",
         "Account DN": "Account DN",
-        "Bind Username": "Bind Username",
+        "Bind Username": "Koppel Gebruikersnaam",
         "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld": "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld",
         "Account Suffix": "Account Achtervoegsel",
         "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP": "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP",

+ 10 - 10
js/langpack/pl[Polish].json

@@ -96,7 +96,7 @@
         "Organizr Branch": "Gałąź Organizr",
         "Organizr Version": "Wersja Organizr",
         "Information": "Informacje",
-        "Below you will find all the links for everything that has to do with Organizr": "Poniżej, znajdują się linki do wszystkiego, co jest związane z Organizr",
+        "Below you will find all the links for everything that has to do with Organizr": "Poniżej znajdują się linki do wszystkiego, co jest związane z Organizr",
         "Loading...": "Wczytywanie...",
         "Donate": "Wesprzyj",
         "Edit Group": "Edytuj grupę",
@@ -148,9 +148,9 @@
         "Request Me!": "Zgłoś mnie!",
         "Would you like to Request it?": "Czy chciałbyś to zgłosić?",
         "No Results for:": "Brak wyników dla:",
-        "Nothing in queue": "Nic nie ma w kolejce",
-        "Nothing in history": "Nic nie ma w historii",
-        "Nothing in hitsory": "Nic nie ma w historii",
+        "Nothing in queue": "Nic w kolejce",
+        "Nothing in history": "Nic w historii",
+        "Nothing in hitsory": "Nic w historii",
         "Load More": "Wczytaj więcej",
         "Request": "Zgłoszenie",
         "No Results": "Brak wyników",
@@ -164,7 +164,7 @@
         "Suggestions": "Sugestie",
         "TV": "TV",
         "Movie": "Film",
-        "Denied": "Odmówiono",
+        "Denied": "Odmówione",
         "Unapproved": "Niezatwierdzone",
         "Approved": "Zatwierdzone",
         "Unavailable": "Niedostępne",
@@ -175,7 +175,7 @@
         "Request Options": "Opcje zgłaszania",
         "Deny": "Odmów",
         "OK": "OK",
-        "Seconds": "Sekundy",
+        "Seconds": "Sekund(y)",
         "Verify Password": "Zweryfikuj hasło",
         "Account Information": "Informacje konta",
         "Inactive Plugins": "Nieaktywne wtyczki",
@@ -306,7 +306,7 @@
         "List": "Lista",
         "Streams": "Strumienie",
         "javascript:void(0)": "javascript:void(0)",
-        "Request Show or Movie": "Zgłoś program lub film",
+        "Request Show or Movie": "Zgłoś serial lub film",
         "Mark as Unavailable": "Oznacz jako niedostępne",
         "Mark as Available": "Oznacz jako dostępne",
         "Approve": "Zatwierdź",
@@ -355,8 +355,8 @@
         "Important Information": "Ważna informacja",
         "PING": "PING",
         "Custom HTML/JavaScript": "Niestandardowy HTML/JavaScript",
-        "CustomHTML-2": "NiestandardowyHTML-2",
-        "CustomHTML-1": "NiestandardowyHTML-1",
+        "CustomHTML-2": "Niestandardowy HTML-2",
+        "CustomHTML-1": "Niestandardowy HTML-1",
         "Refresh Seconds": "Odświeżanie w sekundach",
         "Limit to User": "Ogranicz do użytkownika",
         "Minimum Group to Request": "Minimalna grupa dla zgłoszeń",
@@ -501,7 +501,7 @@
         "Tab Local URL": "Lokalny URL karty",
         "PRELOAD": "PRELOAD",
         "Use Ombi Alias Names": "Użyj nazw aliasów Ombi",
-        "TV Show Default Request": "Domyślne zgłoszenie Programu TV",
+        "TV Show Default Request": "Domyślne zgłoszenie serialu",
         "Add to Combined Downloader": "Dodaj do zbiorczego pobierania",
         "http(s)://hostname:port/xmlrpc": "http(s)://nazwa_hosta:port/xmlrpc",
         "rTorrent API URL Override": "Nadpisanie URL API rTorrent",

+ 37 - 37
js/langpack/ro[Romanian].json

@@ -479,42 +479,42 @@
         "Authentication": "Authentication",
         "SMTP Port": "SMTP Port",
         "SMTP Host": "SMTP Host",
-        "Results For cmd:": "",
-        "Organizr Information:": "",
-        "DB Schema": "",
-        "Misc SSO": "",
-        "Tautulli SSO": "",
-        "Plex SSO": "",
-        "Ombi SSO": "",
-        "Commands": "",
-        "Input Command": "",
-        "Organizr Debug Area": "",
-        "Google Ads": "",
-        "DB Folder": "",
-        "API Folder": "",
-        "Root Folder": "",
-        "Organizr Paths": "",
-        "Organizr News": "",
-        "Close Error": "",
-        "An Error Occured": "",
-        "Debug Area": "",
-        "Tab Local URL": "",
-        "PRELOAD": "",
-        "Use Ombi Alias Names": "",
-        "TV Show Default Request": "",
-        "Add to Combined Downloader": "",
-        "http(s)://hostname:port/xmlrpc": "",
-        "rTorrent API URL Override": "",
-        "Enable Notify Sounds": "",
-        "Remember Me Length": "",
-        "Minimum Authentication for Debug Area": "",
-        "Account DN": "",
-        "Bind Username": "",
-        "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld": "",
-        "Account Suffix": "",
-        "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP": "",
-        "Account Prefix": "",
-        "LDAP Backend Type": "",
-        "Strict Plex Friends": ""
+        "Results For cmd:": "Result For cmd:",
+        "Organizr Information:": "Organizr Information:",
+        "DB Schema": "DB Schema",
+        "Misc SSO": "Misc SSO",
+        "Tautulli SSO": "Tautulli SSO",
+        "Plex SSO": "Plex SSO",
+        "Ombi SSO": "Ombi SSO",
+        "Commands": "Commands",
+        "Input Command": "Input Command",
+        "Organizr Debug Area": "Organizr Debug Area",
+        "Google Ads": "Google Ads",
+        "DB Folder": "DB Folder",
+        "API Folder": "API Folder",
+        "Root Folder": "Root Folder",
+        "Organizr Paths": "Organizr Paths",
+        "Organizr News": "Organizr News",
+        "Close Error": "Close Error",
+        "An Error Occured": "An Error Occurred",
+        "Debug Area": "Debug Area",
+        "Tab Local URL": "Tab Local URL",
+        "PRELOAD": "PRELOAD",
+        "Use Ombi Alias Names": "Use Ombi Alias Names",
+        "TV Show Default Request": "TV Show Default Request",
+        "Add to Combined Downloader": "Add to Combined Downloader",
+        "http(s)://hostname:port/xmlrpc": "http(s)://hostname:port/xmlrpc",
+        "rTorrent API URL Override": "rTorrent API URL Override",
+        "Enable Notify Sounds": "Enable Notify Sounds",
+        "Remember Me Length": "Remember Me Length",
+        "Minimum Authentication for Debug Area": "Minimum Authentication for Debug Area",
+        "Account DN": "Account DN",
+        "Bind Username": "Bind Username",
+        "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld": "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld",
+        "Account Suffix": "Account Suffix",
+        "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP": "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP",
+        "Account Prefix": "Account Prefix",
+        "LDAP Backend Type": "LDAP Backend Type",
+        "Strict Plex Friends": "Strict Plex Friends"
     }
 }

+ 101 - 101
js/langpack/ru[Russian].json

@@ -31,22 +31,22 @@
         "Organizr Logs": "Журнал Organizr",
         "Main Settings": "Основные параметры",
         "Updates": "Обновления",
-        "Logs": "Журнал",
+        "Logs": "Журналы",
         "Main": "Основное меню",
         "Plugins": "Дополнения",
-        "Manage Users": "Управление пользователями",
+        "Manage Users": "Настройка пользователей",
         "Customize Organizr": "Персонализация Organizr",
         "Edit Categories": "Редактировать категории",
         "Edit Tabs": "Редактировать вкладки",
         "Tabs": "Вкладки",
-        "System Settings": "Системные параметры",
-        "User Management": "Управление пользователями",
+        "System Settings": "Настройка системы",
+        "User Management": "Настройка пользователей",
         "Customize": "Персонализация",
-        "Tab Editor": "Редактор вкладки",
+        "Tab Editor": "Редактор вкладок",
         "Settings": "Настройки",
         "Organizr Settings": "Настройки Organizr",
         "Categories": "Категории",
-        "Login Logs": "Журнал авторизации",
+        "Login Logs": "Журналы авторизации",
         "Login Log": "Журнал авторизации",
         "Organizr Log": "Журнал Organizr",
         "FIXED": "ИСПРАВЛЕНО",
@@ -59,12 +59,12 @@
         "Database Name": "Название базы данных:",
         "Database Location:": "Расположение базы данных:",
         "Database Location": "Расположение базы данных:",
-        "Hover to show": "Наведите указатель мыши, чтобы показать",
+        "Hover to show": "Наведите, чтобы показать",
         "API Key:": "Ключ API:",
         "API Key": "Ключ API:",
         "Registration Password:": "Пароль регистрации:",
         "Hash Key:": "Хэш-ключ:",
-        "Hash Key": "Хэш-ключ:",
+        "Hash Key": "Хэш-ключ",
         "Password:": "Пароль:",
         "Attention": "Внимание",
         "The Hash Key will be used to decrypt all passwords etc... on the server.": "Хэш-ключ используется для расшифровки всех паролей и т.д на сервере",
@@ -86,7 +86,7 @@
         "ADDED": "ДОБАВЛЕННОЕ",
         "NAME & EMAIL": "ИМЯ и Электронная почта",
         "MANAGE USERS": "УПРАВЛЕНИЕ ПОЛЬЗОВАТЕЛЯМИ",
-        "Add User": "Добавить пользвоателя",
+        "Add User": "Добавить пользов-ля",
         "Manage Groups": "Управление группами",
         "Choose Language": "Выбрать язык",
         "Welcome": "Добро пожаловать",
@@ -98,8 +98,8 @@
         "Information": "Информация",
         "Below you will find all the links for everything that has to do with Organizr": "Ниже вы найдете все необходимые ссылки что можно делать с Organizr",
         "Loading...": "Загрузка",
-        "Donate": "Подертвовать",
-        "Edit Group": "Редактировать группы",
+        "Donate": "Пожертвовать",
+        "Edit Group": "Изменить группу",
         "For icons, use the following format:": "Используйте следующий формат для иконок:",
         "For images, use the following format:": "Используйте следующий формат для изображений:",
         "You may use an image or icon in this field": "Вы можете использовать изображение или иконку в этом поле",
@@ -118,7 +118,7 @@
         "Tab Image": "Изображение вкладки",
         "Tab URL": "URL вкладки",
         "Tab Name": "Имя вкладки",
-        "Edit Tab": "Редактировать вкладку",
+        "Edit Tab": "Изменить вкладку",
         "Add Tab": "Добавить вкладку",
         "Add New Tab": "Добавить новую вкладку",
         "SPLASH": "ЗАСТАВКА",
@@ -131,7 +131,7 @@
         "Deleted Category": "Удалить категорию",
         "Add New User": "Добавить нового пользователя",
         "EMAIL": "Электронная почта",
-        "Deleted User": "Удалить пользователя",
+        "Deleted User": "Удаленный польз.",
         "Category Order Saved": "Порядок сортировки категорий сохранен",
         "Group Image": "Изображение группы",
         "Group Name": "Имя группы",
@@ -145,36 +145,36 @@
         "Users": "Пользователи",
         "Appearance": "Внешний вид",
         "Customize Appearance": "Редактирование внешнего вида",
-        "Request Me!": "Спросите меня!",
-        "Would you like to Request it?": "Вы хотите спросить это?",
+        "Request Me!": "Запросить!",
+        "Would you like to Request it?": "Запросить это?",
         "No Results for:": "Нету результатов для:",
         "Nothing in queue": "Очередь пуста",
-        "Nothing in history": "Нет истории",
-        "Nothing in hitsory": "Нет истории",
+        "Nothing in history": "История пуста",
+        "Nothing in hitsory": "История пуста",
         "Load More": "Загрузить ещё",
         "Request": "Запрос",
         "No Results": "Нет результатов",
         "Airs Today TV": "Сегодня в эфире",
-        "Popular TV": "Популярное телевидение",
-        "Top TV": "Лучшее",
-        "Upcoming Movies": "Предстоящие фильмы",
+        "Popular TV": "Популярные сериалы",
+        "Top TV": "Топ сериалов",
+        "Upcoming Movies": "Выходящее кино",
         "Popular Movies": "Популярные фильмы",
-        "Top Movies": "Лучшие фильмы",
+        "Top Movies": "Топ фильмов",
         "In Theatres": "В кинотеатрах",
         "Suggestions": "Предложения",
-        "TV": "Телевидение",
+        "TV": "Сериалы",
         "Movie": "Кино",
         "Denied": "Отклонено",
-        "Unapproved": "Не подтверждено",
-        "Approved": "Подтверждено",
+        "Unapproved": "Не принято",
+        "Approved": "Принято",
         "Unavailable": "Не доступно",
         "Available": "Доступно",
         "Recently Added": "Уже добавлено",
         "Active": "Активно",
-        "Requested By:": "Запрошено:",
+        "Requested By:": "Запросил(а):",
         "Request Options": "Опции запроса",
         "Deny": "Отклонить",
-        "OK": "OK",
+        "OK": "ОК",
         "Seconds": "Секунды",
         "Verify Password": "Подтверждение пароля",
         "Account Information": "Информация об аккаунте",
@@ -184,10 +184,10 @@
         "Everything Active": "Включить все",
         "Nothing Active": "Выключить все",
         "Choose Plex Machine": "Выбрать Plex сервер",
-        "Test Speed to Server": "Тестировать скорость до сервера",
-        "Test Server Speed": "Тестировать скорость сервера",
-        "Subject": "Объект",
-        "To:": "Для:",
+        "Test Speed to Server": "Проверить скорость до сервера",
+        "Test Server Speed": "Проверить скорость сервера",
+        "Subject": "Отправитель",
+        "To:": "Кому:",
         "Email Users": "Email пользователей",
         "E-Mail Center": "Центр электронной почты",
         "You have been invited. Please goto ": "Вы были приглашены. Перейдите ",
@@ -199,12 +199,12 @@
         "DATE SENT": "ДАТА ОТПРАВКИ",
         "INVITE CODE": "КОД ПРИГЛАШЕНИЯ",
         "USERNAME": "ПОЛЬЗОВАТЕЛЬ",
-        "Manage Invites": "Администрирование приглашений",
+        "Manage Invites": "Управление приглашениями",
         "No Invites": "Нет приглашений",
         "Create/Send Invite": "Создать/Выслать приглашение",
         "Name or Username": "Имя или имя пользователя",
         "New Invite": "Новое приглашение",
-        "Hover to show ": "Наведите указатель мыши, чтобы показать ",
+        "Hover to show ": "Наведите, чтобы показать ",
         "Parent Directory: ": "Родительская папка: ",
         "The Database will contain sensitive information. Please place in directory outside of root Web Directory.": "База данных содержит важную информацию. Пожалуйста поместите ее за пределами корневого каталога веб сервера.",
         "I Want to Help": "Я хочу помочь",
@@ -212,18 +212,18 @@
         "Want to help translate?": "Хотите помочь с переводом?",
         "Single Sign-On": "Единая точка входа",
         "Coming Soon...": "Скоро...",
-        "Homepage Order": "Список домашней страницы",
-        "Homepage Items": "Сервисы домашней страницы",
+        "Homepage Order": "Порядок виджетов на главной",
+        "Homepage Items": "Виджеты главной страницы",
         "Image Manager": "Менеджер изображений",
         "Edit User": "Редактировать пользователя",
         "Password Again": "Повторите пароль",
         "Template": "Шаблон",
         "Plex Machine": "Plex сервер",
         "Get Plex Machine": "Получить список Plex серверов",
-        "Grab It": "Схватить удачу",
+        "Grab It": "Собрать",
         "Plex Password": "Пароль Plex",
         "Plex Username": "Пользователь Plex",
-        "Enter Plex Details": "Введите информацию о Plex",
+        "Enter Plex Details": "Введите описание Plex",
         "Get Plex Token": "Получить Plex токен",
         "Upload Image": "Загрузить изображение",
         "View Images": "Просмотреть изображения",
@@ -233,18 +233,18 @@
         "Web Folder": "Веб каталог",
         "Dependencies Missing": "Пропущены зависимости",
         "Organizr Dependency Check": "Проверить зависимости Organizr",
-        "Please make sure both Token and Machine are filled in": "Проверьте что оба поля токена и сервера заполнены",
-        "Loading Requests...": "Загрузка запросов...",
-        "Loading Recent...": "Загрузка новинок...",
+        "Please make sure both Token and Machine are filled in": "Проверьте что поля токена и сервера заполнены",
+        "Loading Requests...": "Загрузка Запросов...",
+        "Loading Recent...": "Загрузка Новинок...",
         "Loading Now Playing...": "Загрузка текущих воспроизведений...",
-        "Loading Playlists...": "загрузка плейлистов...",
+        "Loading Playlists...": "Загрузка Плейлистов...",
         "Loading Download Queue...": "Загрузка очереди скачивания...",
         "Organizr Mod Picks": "Выбор модов Organizr",
         "Requests": "Запросы",
         "Become Sponsor": "Стать спонсором",
         "Splash Page": "Страница заставки",
         "Lock Screen": "Заблокировать экран",
-        "If you signed in with a Emby Acct... Please use the following link to change your password there:": "Если вы зашли через аккаунт Emby. Пожалуйста используйте следующую ссылку для смены пароля:",
+        "If you signed in with a Emby Acct... Please use the following link to change your password there:": "Если вы зашли через аккаунт Emby. Пожалуйста, используйте следующую ссылку для смены пароля:",
         "Password Notice": "Подсказка пароля",
         "If you signed in with a Plex Acct... Please use the following link to change your password there:": "Если вы зашли через аккаунт Plex. Пожалуйста используйте следующую ссылку для смены пароля:",
         "Active Tokens": "Активные токены",
@@ -273,13 +273,13 @@
         "Sponsors": "Спонсоры",
         "THEME": "ТЕМА",
         "Theme Marketplace": "Магазин тем",
-        "Test Tab": "Тестировать вкладку",
+        "Test Tab": "Проверить вкладку",
         "Select or type Icon": "Выберите или введите иконку",
         "Choose Icon": "Выбрать иконку",
         "Choose Image": "Выбрать изображение",
-        "Ping URL": "Проверять URL на доступность",
+        "Ping URL": "URL для проверки",
         "Please set tab as [New Window] on next screen": "Пожалуйста, отметьте вкладку как новое окно на следующем экране",
-        "Tab can be set as iFrame": "Вкладка может ",
+        "Tab can be set as iFrame": "Вкладка может быть встроена",
         "Premier": "Премьеры",
         "Missing": "Пропущено",
         "Unaired": "Вышло из эфира",
@@ -304,22 +304,22 @@
         "Week": "Неделя",
         "Month": "Месяц",
         "List": "Список",
-        "Streams": "Потоковое вещание",
+        "Streams": "Потоки",
         "javascript:void(0)": "javascript:void(0)",
-        "Request Show or Movie": "Требуется сериал или кино",
+        "Request Show or Movie": "Запросить сериал или фильм",
         "Mark as Unavailable": "Пометить как недоступный",
         "Mark as Available": "Пометить как доступный",
-        "Approve": "Утвердить",
+        "Approve": "Принять",
         "Start": "Запустить",
-        "Everyone Refresh Seconds": "Интервал обновления для всех в секундах",
-        "Admin Refresh Seconds": "Интервал обновления для Администратора в секундах",
-        "Minimum Authentication for Time Display": "Минимальная аутентификация для отображения времени",
+        "Everyone Refresh Seconds": "Общий интервал обновления, сек.",
+        "Admin Refresh Seconds": "Админский интервал обновления, сек",
+        "Minimum Authentication for Time Display": "Минимальные права для отображения времени",
         "Show Ping Time": "Показывать время пинга",
         "Offline Sound": "Оффлайн звук",
         "Online Sound": "Онлайн звук",
-        "Minimum Authentication for Message and Sound": "Минимальная аутентификация для сообщений и звуков",
-        "Minimum Authentication": "Минимальная аутентификация",
-        "Nginx Auth Debug": "Nginx аутентификация отладки",
+        "Minimum Authentication for Message and Sound": "Минимальные права для сообщений и звуков",
+        "Minimum Authentication": "Минимальные права",
+        "Nginx Auth Debug": "Отладка Nginx аутентификации",
         "Hide Registration": "Скрыть регистрацию",
         "Lockout Groups To": "Блокировка групп для",
         "Lockout Groups From": "Группы блокировки от",
@@ -334,7 +334,7 @@
         "Host Address": "Адрес хоста",
         "Enable Plex oAuth": "Включить Plex аутентификацию",
         "Retrieve": "Исправить",
-        "Use Get Plex Machine Button": "Используете кнопку \"Получить список Plex серверов\"",
+        "Use Get Plex Machine Button": "Используйте кнопку \"Получить список серверов\"",
         "Use Get Token Button": "Используйте кнопку \"Получить токен\"",
         "Plex Token": "Токен Plex",
         "Authentication Backend": "Бэкэнд аутентификация",
@@ -348,12 +348,12 @@
         "Enable": "Включить",
         "Tautulli URL": "Tautulli URL",
         "Ombi URL": "Ombi URL",
-        "Plex Note": "Plex примечание",
+        "Plex Note": "Описание Plex",
         "Admin username for Plex": "Учетная запись администратора Plex",
-        "Admin Username": "Имя пользователя администратора",
+        "Admin Username": "Имя администратора",
         "Click Main on the sub-menu above.": "Click Main on the sub-menu above.",
         "Important Information": "Важная информация",
-        "PING": "Проверить доступность",
+        "PING": "PING",
         "Custom HTML/JavaScript": "Пользовательский HTML/JavaScript",
         "CustomHTML-2": "Пользовательский HTML-2",
         "CustomHTML-1": "Пользовательский HTML-1",
@@ -366,7 +366,7 @@
         "Items Per Day": "Предметов в день",
         "Time Format": "Формат времени",
         "Default View": "Вид по умолчанию",
-        "Start Day": "Начало дня",
+        "Start Day": "Первый день",
         "SickRage": "SickRage",
         "CouchPotato": "CouchPotato",
         "Test Connection": "Проверить соединение",
@@ -378,7 +378,7 @@
         "Show Unmonitored": "Показывать непроверяемые",
         "Sonarr": "Sonarr",
         "Hide Completed": "Скрыть завершенные",
-        "Hide Seeding": "Скрыть раздающие",
+        "Hide Seeding": "Скрыть раздаваемые",
         "Deluge": "Deluge",
         "Order": "Порядок",
         "Status: [": "Статус: [",
@@ -395,7 +395,7 @@
         "Emby Tab Name": "Имя вкладки Emby",
         "Item Limit": "Максимальное количество постеров",
         "Minimum Authorization": "Минимальная аутентификация",
-        "User Information": "информация о пользователе",
+        "User Information": "Информация о пользователе",
         "Emby": "Emby",
         "Plex Tab WAN URL": "Внешний URL вкладки Plex",
         "Only use if you have Plex in a reverse proxy": "Только если вы используете Plex через обратный прокси",
@@ -416,7 +416,7 @@
         "Enter this path": "Введите этот путь",
         "At bottom of page on [Favicon Generator Options] under [Path] choose [I cannot or I do not want to place favicon files at the root of my web site.]": "В нижней части страницы [Опции генератора Favicon] в разделе [Путь] выберите [Я не могу или не хочу размещать файлы favicon в корне моего веб-сайта.]",
         "Edit settings to your liking": "Редактировать настройки по своему вкусу",
-        "Choose your image to use": "Выберите ваше изображение для использования",
+        "Choose your image to use": "Выберите ваше изображение",
         "Click [Select your Favicon picture]": "Нажмите [Выберите свою фотографию Favicon]",
         "Instructions": "Инструкции",
         "Fav Icon Code": "Fav Icon Code",
@@ -436,11 +436,11 @@
         "Alternate Homepage Titles": "Альтернативные заголовки домашней страницы",
         "Minimal Login Screen": "Минималистичное окно авторизации",
         "Login Wallpaper": "Обои окна авторизации",
-        "Use Logo instead of Title": "Использовать логотип взамен ",
+        "Use Logo instead of Title": "Использовать логотип взамен заголовка",
         "Title": "Заголовок",
         "Logo": "Логотип",
         "Import Users": "Импортировать пользователей",
-        "Drop files here to upload": "Перетащите сюда файлы для загрузки",
+        "Drop files here to upload": "Перетащите файлы для загрузки",
         "SpeedTest Settings": "Настройки SpeedTest",
         "PHP Mailer Settings": "Настройки PHP Mailer",
         "Invites Settings": "Настройки приглашений",
@@ -453,7 +453,7 @@
         "Use Pusher SSL": "Использовать Pusher SSL",
         "Message Sound": "Звук сообщения",
         "# of Previous Messages": "# предыдущих сообщений",
-        "Note": "Примечание",
+        "Note": "Заметка",
         "Libraries": "Библиотеки",
         "Body": "Основная часть",
         "Name": "Имя",
@@ -479,42 +479,42 @@
         "Authentication": "Аутентификация",
         "SMTP Port": "Порт SMTP сервера",
         "SMTP Host": "Адрес SMTP сервера\n",
-        "Results For cmd:": "",
-        "Organizr Information:": "",
-        "DB Schema": "",
-        "Misc SSO": "",
-        "Tautulli SSO": "",
-        "Plex SSO": "",
-        "Ombi SSO": "",
-        "Commands": "",
-        "Input Command": "",
-        "Organizr Debug Area": "",
-        "Google Ads": "",
-        "DB Folder": "",
-        "API Folder": "",
-        "Root Folder": "",
-        "Organizr Paths": "",
-        "Organizr News": "",
-        "Close Error": "",
-        "An Error Occured": "",
-        "Debug Area": "",
-        "Tab Local URL": "",
-        "PRELOAD": "",
-        "Use Ombi Alias Names": "",
-        "TV Show Default Request": "",
-        "Add to Combined Downloader": "",
-        "http(s)://hostname:port/xmlrpc": "",
-        "rTorrent API URL Override": "",
-        "Enable Notify Sounds": "",
-        "Remember Me Length": "",
-        "Minimum Authentication for Debug Area": "",
-        "Account DN": "",
-        "Bind Username": "",
-        "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld": "",
-        "Account Suffix": "",
-        "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP": "",
-        "Account Prefix": "",
-        "LDAP Backend Type": "",
-        "Strict Plex Friends": ""
+        "Results For cmd:": "Result For cmd:",
+        "Organizr Information:": "Информация о Organizr",
+        "DB Schema": "Схема базы",
+        "Misc SSO": "Misc SSO",
+        "Tautulli SSO": "Tautulli SSO",
+        "Plex SSO": "Plex SSO",
+        "Ombi SSO": "Ombi SSO",
+        "Commands": "Команды",
+        "Input Command": "Введите команду",
+        "Organizr Debug Area": "Зона отладки Organizr",
+        "Google Ads": "Google Ads",
+        "DB Folder": "Директория базы",
+        "API Folder": "Директория API",
+        "Root Folder": "Основная директория",
+        "Organizr Paths": "Пути Organizr",
+        "Organizr News": "Новости Organizr",
+        "Close Error": "Закрыть ошибки",
+        "An Error Occured": "Произошедшие ошибки",
+        "Debug Area": "Зона Отладки",
+        "Tab Local URL": "Локальная URL вкладки",
+        "PRELOAD": "Предзагрузка",
+        "Use Ombi Alias Names": "Использовать имена Ombi",
+        "TV Show Default Request": "Шаблон запроса сериала",
+        "Add to Combined Downloader": "Добавить совмещенный загрузчик",
+        "http(s)://hostname:port/xmlrpc": "http(s)://hostname:port/xmlrpc",
+        "rTorrent API URL Override": "Перезаписать URL API rTorrent",
+        "Enable Notify Sounds": "Включить звук уведомлений",
+        "Remember Me Length": "Запомнить меня",
+        "Minimum Authentication for Debug Area": "Минимальные права для Зоны Отладки",
+        "Account DN": "DN учетной записи",
+        "Bind Username": "Сопоставить учетную запись",
+        "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld": "Суффикс аккаунта - начинается с запятой -  ,ou=people,dc=domain,dc=tld",
+        "Account Suffix": "Суффикс Аккаунта",
+        "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP": "Префикс Аккаунта - т.е. Controller\\ from Controller\\Username для AD - uid= для OpenLDAP",
+        "Account Prefix": "Префикс Аккаунта",
+        "LDAP Backend Type": "Тип LDAP",
+        "Strict Plex Friends": "Исключить друзей в Plex"
     }
 }

+ 37 - 37
js/langpack/sk[Slovak].json

@@ -479,42 +479,42 @@
         "Authentication": "overenie pravosti",
         "SMTP Port": "Port SMTP",
         "SMTP Host": "Hostiteľ SMTP",
-        "Results For cmd:": "",
-        "Organizr Information:": "",
-        "DB Schema": "",
-        "Misc SSO": "",
-        "Tautulli SSO": "",
-        "Plex SSO": "",
-        "Ombi SSO": "",
-        "Commands": "",
-        "Input Command": "",
-        "Organizr Debug Area": "",
-        "Google Ads": "",
-        "DB Folder": "",
-        "API Folder": "",
-        "Root Folder": "",
-        "Organizr Paths": "",
-        "Organizr News": "",
-        "Close Error": "",
-        "An Error Occured": "",
-        "Debug Area": "",
-        "Tab Local URL": "",
-        "PRELOAD": "",
-        "Use Ombi Alias Names": "",
-        "TV Show Default Request": "",
-        "Add to Combined Downloader": "",
-        "http(s)://hostname:port/xmlrpc": "",
-        "rTorrent API URL Override": "",
-        "Enable Notify Sounds": "",
-        "Remember Me Length": "",
-        "Minimum Authentication for Debug Area": "",
-        "Account DN": "",
-        "Bind Username": "",
-        "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld": "",
-        "Account Suffix": "",
-        "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP": "",
-        "Account Prefix": "",
-        "LDAP Backend Type": "",
-        "Strict Plex Friends": ""
+        "Results For cmd:": "Result For cmd:",
+        "Organizr Information:": "Organizr Information:",
+        "DB Schema": "DB Schema",
+        "Misc SSO": "Misc SSO",
+        "Tautulli SSO": "Tautulli SSO",
+        "Plex SSO": "Plex SSO",
+        "Ombi SSO": "Ombi SSO",
+        "Commands": "Commands",
+        "Input Command": "Input Command",
+        "Organizr Debug Area": "Organizr Debug Area",
+        "Google Ads": "Google Ads",
+        "DB Folder": "DB Folder",
+        "API Folder": "API Folder",
+        "Root Folder": "Root Folder",
+        "Organizr Paths": "Organizr Paths",
+        "Organizr News": "Organizr News",
+        "Close Error": "Close Error",
+        "An Error Occured": "An Error Occurred",
+        "Debug Area": "Debug Area",
+        "Tab Local URL": "Tab Local URL",
+        "PRELOAD": "PRELOAD",
+        "Use Ombi Alias Names": "Use Ombi Alias Names",
+        "TV Show Default Request": "TV Show Default Request",
+        "Add to Combined Downloader": "Add to Combined Downloader",
+        "http(s)://hostname:port/xmlrpc": "http(s)://hostname:port/xmlrpc",
+        "rTorrent API URL Override": "rTorrent API URL Override",
+        "Enable Notify Sounds": "Enable Notify Sounds",
+        "Remember Me Length": "Remember Me Length",
+        "Minimum Authentication for Debug Area": "Minimum Authentication for Debug Area",
+        "Account DN": "Account DN",
+        "Bind Username": "Bind Username",
+        "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld": "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld",
+        "Account Suffix": "Account Suffix",
+        "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP": "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP",
+        "Account Prefix": "Account Prefix",
+        "LDAP Backend Type": "LDAP Backend Type",
+        "Strict Plex Friends": "Strict Plex Friends"
     }
 }

+ 37 - 37
js/langpack/sv[Swedish].json

@@ -479,42 +479,42 @@
         "Authentication": "Autentisering",
         "SMTP Port": "SMTP Port\n",
         "SMTP Host": "SMTP Värd",
-        "Results For cmd:": "",
-        "Organizr Information:": "",
-        "DB Schema": "",
-        "Misc SSO": "",
-        "Tautulli SSO": "",
-        "Plex SSO": "",
-        "Ombi SSO": "",
-        "Commands": "",
-        "Input Command": "",
-        "Organizr Debug Area": "",
-        "Google Ads": "",
-        "DB Folder": "",
-        "API Folder": "",
-        "Root Folder": "",
-        "Organizr Paths": "",
-        "Organizr News": "",
-        "Close Error": "",
-        "An Error Occured": "",
-        "Debug Area": "",
-        "Tab Local URL": "",
-        "PRELOAD": "",
-        "Use Ombi Alias Names": "",
-        "TV Show Default Request": "",
-        "Add to Combined Downloader": "",
-        "http(s)://hostname:port/xmlrpc": "",
-        "rTorrent API URL Override": "",
-        "Enable Notify Sounds": "",
-        "Remember Me Length": "",
-        "Minimum Authentication for Debug Area": "",
-        "Account DN": "",
-        "Bind Username": "",
-        "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld": "",
-        "Account Suffix": "",
-        "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP": "",
-        "Account Prefix": "",
-        "LDAP Backend Type": "",
-        "Strict Plex Friends": ""
+        "Results For cmd:": "Result For cmd:",
+        "Organizr Information:": "Organizr Information:",
+        "DB Schema": "DB Schema",
+        "Misc SSO": "Misc SSO",
+        "Tautulli SSO": "Tautulli SSO",
+        "Plex SSO": "Plex SSO",
+        "Ombi SSO": "Ombi SSO",
+        "Commands": "Commands",
+        "Input Command": "Input Command",
+        "Organizr Debug Area": "Organizr Debug Area",
+        "Google Ads": "Google Ads",
+        "DB Folder": "DB Folder",
+        "API Folder": "API Folder",
+        "Root Folder": "Root Folder",
+        "Organizr Paths": "Organizr Paths",
+        "Organizr News": "Organizr News",
+        "Close Error": "Close Error",
+        "An Error Occured": "An Error Occurred",
+        "Debug Area": "Debug Area",
+        "Tab Local URL": "Tab Local URL",
+        "PRELOAD": "PRELOAD",
+        "Use Ombi Alias Names": "Use Ombi Alias Names",
+        "TV Show Default Request": "TV Show Default Request",
+        "Add to Combined Downloader": "Add to Combined Downloader",
+        "http(s)://hostname:port/xmlrpc": "http(s)://hostname:port/xmlrpc",
+        "rTorrent API URL Override": "rTorrent API URL Override",
+        "Enable Notify Sounds": "Enable Notify Sounds",
+        "Remember Me Length": "Remember Me Length",
+        "Minimum Authentication for Debug Area": "Minimum Authentication for Debug Area",
+        "Account DN": "Account DN",
+        "Bind Username": "Bind Username",
+        "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld": "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld",
+        "Account Suffix": "Account Suffix",
+        "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP": "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP",
+        "Account Prefix": "Account Prefix",
+        "LDAP Backend Type": "LDAP Backend Type",
+        "Strict Plex Friends": "Strict Plex Friends"
     }
 }

+ 37 - 37
js/langpack/zh[Chinese].json

@@ -479,42 +479,42 @@
         "Authentication": "认证",
         "SMTP Port": "SMTP端口",
         "SMTP Host": "SMTP主机",
-        "Results For cmd:": "",
-        "Organizr Information:": "",
-        "DB Schema": "",
-        "Misc SSO": "",
-        "Tautulli SSO": "",
-        "Plex SSO": "",
-        "Ombi SSO": "",
-        "Commands": "",
-        "Input Command": "",
-        "Organizr Debug Area": "",
-        "Google Ads": "",
-        "DB Folder": "",
-        "API Folder": "",
-        "Root Folder": "",
-        "Organizr Paths": "",
-        "Organizr News": "",
-        "Close Error": "",
-        "An Error Occured": "",
-        "Debug Area": "",
-        "Tab Local URL": "",
-        "PRELOAD": "",
-        "Use Ombi Alias Names": "",
-        "TV Show Default Request": "",
-        "Add to Combined Downloader": "",
-        "http(s)://hostname:port/xmlrpc": "",
-        "rTorrent API URL Override": "",
-        "Enable Notify Sounds": "",
-        "Remember Me Length": "",
-        "Minimum Authentication for Debug Area": "",
-        "Account DN": "",
-        "Bind Username": "",
-        "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld": "",
-        "Account Suffix": "",
-        "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP": "",
-        "Account Prefix": "",
-        "LDAP Backend Type": "",
-        "Strict Plex Friends": ""
+        "Results For cmd:": "Result For cmd:",
+        "Organizr Information:": "Organizr Information:",
+        "DB Schema": "DB Schema",
+        "Misc SSO": "Misc SSO",
+        "Tautulli SSO": "Tautulli SSO",
+        "Plex SSO": "Plex SSO",
+        "Ombi SSO": "Ombi SSO",
+        "Commands": "Commands",
+        "Input Command": "Input Command",
+        "Organizr Debug Area": "Organizr Debug Area",
+        "Google Ads": "Google Ads",
+        "DB Folder": "DB Folder",
+        "API Folder": "API Folder",
+        "Root Folder": "Root Folder",
+        "Organizr Paths": "Organizr Paths",
+        "Organizr News": "Organizr News",
+        "Close Error": "Close Error",
+        "An Error Occured": "An Error Occurred",
+        "Debug Area": "Debug Area",
+        "Tab Local URL": "Tab Local URL",
+        "PRELOAD": "PRELOAD",
+        "Use Ombi Alias Names": "Use Ombi Alias Names",
+        "TV Show Default Request": "TV Show Default Request",
+        "Add to Combined Downloader": "Add to Combined Downloader",
+        "http(s)://hostname:port/xmlrpc": "http(s)://hostname:port/xmlrpc",
+        "rTorrent API URL Override": "rTorrent API URL Override",
+        "Enable Notify Sounds": "Enable Notify Sounds",
+        "Remember Me Length": "Remember Me Length",
+        "Minimum Authentication for Debug Area": "Minimum Authentication for Debug Area",
+        "Account DN": "Account DN",
+        "Bind Username": "Bind Username",
+        "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld": "Account suffix - start with comma - ,ou=people,dc=domain,dc=tld",
+        "Account Suffix": "Account Suffix",
+        "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP": "Account prefix - i.e. Controller\\ from Controller\\Username for AD - uid= for OpenLDAP",
+        "Account Prefix": "Account Prefix",
+        "LDAP Backend Type": "LDAP Backend Type",
+        "Strict Plex Friends": "Strict Plex Friends"
     }
 }

+ 7 - 0
js/version.json

@@ -166,5 +166,12 @@
     "new": "Radarr 4k Image|SRI Hash for External JS (#1055)|Limit for Ombi items (#1107)|CTRL + SHIFT on splash page to not close it after click",
     "fixed": "Fix for bootstrap screen width bug @leram84|Ombi on homepage causes site to hang (#1107)|Fix SSO if user is LDAP user|Auto reload on internal tabs|Request button border-radius",
     "notes": "Updated language translations|Please report bugs in GitHub issues page"
+  },
+  "2.0.180": {
+    "date": "2019-05-03 18:00",
+    "title": "Long Overdue Update",
+    "new": "Emby Invites|HealthChecks.io to homepage|Added ability to have more than one api token for healthchecks.io|Updated SweetAlert to new version|Option to define local IP override|LDAP login test|iFrame sandbox options|Refreshes to plex and emby recent|OPNsense to Default Icon Library (#1131)|Added refresh to calendar|Added refresh to Streams|Added keybinding for next and previous tabs|Toggle to turn of organizr debug errors|Added method to login with API|Add header to auth line for current user - used for Grafana SSO|Shortcut to edit homepage item on tab page|Allow custom URL for selfhosted version of healthchecks (#1161)",
+    "fixed": "Ombi SSO error (#1117)|Changed Windows update method|Multiple CSS fixes|Invite select2 box code|V2 emby unexpected token (#1121)|Fix Menu overlapping (#1122)|Calendar does not show recurring events (#1104)|Fix big log files (#990)|Ignore js error for downloader (#1133)|Make all urls without scheme http (#1133)|Mobile login form autocapitalises and auto-corrects (#1138)|Undefined index: description (#1154)|http and https check (#1154)|Tab Url choose image text/image is misaligned (#1146)|invite settings box|Misaligned trashcan icon in Tab Editor menu (#1147)",
+    "notes": "Updated language translations|Added more default tab images|Please report bugs in GitHub issues page"
   }
 }

File diff suppressed because it is too large
+ 0 - 0
plugins/bower_components/sweetalert/sweetalert.min.js


BIN
plugins/images/tabs/AdGuardHome.png


BIN
plugins/images/tabs/cockpit.png


BIN
plugins/images/tabs/healthchecks.png


+ 0 - 0
plugins/images/tabs/homeassitant.png → plugins/images/tabs/homeassistant.png


BIN
plugins/images/tabs/jupyter.png


BIN
plugins/images/tabs/nodered.png


BIN
plugins/images/tabs/opnsense.png


BIN
plugins/images/tabs/radarr4k.png


BIN
plugins/images/tabs/sonarr4k.png


BIN
plugins/images/tabs/vera.png


BIN
plugins/images/tabs/vscode.png


BIN
plugins/images/tabs/xenorchestra.png


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