Parcourir la source

Merge pull request #1676 from causefx/v2-develop

V2 develop
causefx il y a 4 ans
Parent
commit
d5935c7e82

+ 192 - 32
api/classes/organizr.class.php

@@ -60,7 +60,7 @@ class Organizr
 	
 	// ===================================
 	// Organizr Version
-	public $version = '2.1.400';
+	public $version = '2.1.426';
 	// ===================================
 	// Quick php Version check
 	public $minimumPHP = '7.3';
@@ -138,6 +138,8 @@ class Organizr
 		$this->upgradeCheck();
 		// Is Page load Organizr OAuth?
 		$this->checkForOrganizrOAuth();
+		// Is user Blacklisted?
+		$this->checkIfUserIsBlacklisted();
 	}
 	
 	protected function connectDB()
@@ -180,11 +182,42 @@ class Organizr
 		}
 	}
 	
+	public function checkIfUserIsBlacklisted()
+	{
+		if ($this->hasDB()) {
+			$currentIP = $this->userIP();
+			if (in_array($currentIP, $this->arrayIP($this->config['blacklisted']))) {
+				die($this->config['blacklistedMessage']);
+			}
+		}
+	}
+	
 	public function auth()
 	{
 		if ($this->hasDB()) {
-			$whitelist = isset($_GET['whitelist']) ? $_GET['whitelist'] : false;
-			$blacklist = isset($_GET['blacklist']) ? $_GET['blacklist'] : false;
+			if (isset($_GET['type'])) {
+				switch (strtolower($_GET['type'])) {
+					case 'whitelist':
+					case 'white':
+					case 'w':
+					case 'wl':
+					case 'allow':
+						$_GET['whitelist'] = $_GET['ips'] ?? false;
+						break;
+					case 'blacklist':
+					case 'black':
+					case 'b':
+					case 'bl':
+					case 'deny':
+						$_GET['blacklist'] = $_GET['ips'] ?? false;
+						break;
+					default:
+						$this->setAPIResponse('error', $_GET['type'] . ' is not a valid type', 401);
+						return true;
+				}
+			}
+			$whitelist = $_GET['whitelist'] ?? false;
+			$blacklist = $_GET['blacklist'] ?? false;
 			$group = 0;
 			$groupParam = ($_GET['group']) ?? 0;
 			$redirect = false;
@@ -196,7 +229,7 @@ class Organizr
 				}
 			}
 			$currentIP = $this->userIP();
-			$unlocked = ($this->user['locked'] == '1') ? false : true;
+			$unlocked = !($this->user['locked'] == '1');
 			if (isset($this->user)) {
 				$currentUser = $this->user['username'];
 				$currentGroup = $this->user['groupID'];
@@ -206,15 +239,26 @@ class Organizr
 				$currentGroup = $this->getUserLevel();
 				$currentEmail = 'guest@guest.com';
 			}
-			$userInfo = "User: $currentUser | Group: $currentGroup | IP: $currentIP | Requesting Access to Group $group | Result: ";
+			$userInfo = [
+				"user" => $currentUser,
+				"group" => $currentGroup,
+				"email" => $currentEmail,
+				"user_ip" => $currentIP,
+				"requested_group" => $group
+			];
+			$responseMessage = 'User is not Authorized or User is locked';
 			if ($whitelist) {
 				if (in_array($currentIP, $this->arrayIP($whitelist))) {
-					$this->setAPIResponse('success', 'User is whitelisted', 200);
+					$responseMessage = 'User is whitelisted';
+					$this->setAPIResponse('success', $responseMessage, 200, $userInfo);
+					return true;
 				}
 			}
 			if ($blacklist) {
 				if (in_array($currentIP, $this->arrayIP($blacklist))) {
-					$this->setAPIResponse('error', $userInfo . ' User is blacklisted', 401);
+					$responseMessage = 'User is blacklisted';
+					$this->setAPIResponse('error', $responseMessage, 401, $userInfo);
+					return true;
 				}
 			}
 			if ($group !== null) {
@@ -227,10 +271,11 @@ class Organizr
 					header("X-Organizr-User: $currentUser");
 					header("X-Organizr-Email: $currentEmail");
 					header("X-Organizr-Group: $currentGroup");
-					$this->setAPIResponse('success', $userInfo . ' User is Authorized', 200);
+					$responseMessage = 'User is authorized';
+					$this->setAPIResponse('success', $responseMessage, 200, $userInfo);
 				} else {
 					if (!$redirect) {
-						$this->setAPIResponse('error', $userInfo . ' User is not Authorized or User is locked', 401);
+						$this->setAPIResponse('error', $responseMessage, 401, $userInfo);
 					} else {
 						exit(http_response_code(401) . header($redirect));
 					}
@@ -1717,6 +1762,16 @@ class Organizr
 	public function getSettingsMain()
 	{
 		return array(
+			'Settings Page' => array(
+				array(
+					'type' => 'select',
+					'name' => 'defaultSettingsTab',
+					'label' => 'Default Settings Tab',
+					'value' => $this->config['defaultSettingsTab'],
+					'options' => $this->getSettingsTabs(),
+					'help' => 'Choose which Settings Tab to be default when opening settings page'
+				),
+			),
 			'Github' => array(
 				array(
 					'type' => 'select',
@@ -2029,7 +2084,8 @@ class Organizr
 					'name' => 'debugAreaAuth',
 					'label' => 'Minimum Authentication for Debug Area',
 					'value' => $this->config['debugAreaAuth'],
-					'options' => $this->groupSelect()
+					'options' => $this->groupSelect(),
+					'settings' => '{}'
 				),
 				array(
 					'type' => 'select2',
@@ -2089,22 +2145,25 @@ class Organizr
 							'value' => 'allow-downloads'
 						),
 					)
-				)
-			),
-			'Performance' => array(
+				),
 				array(
-					'type' => 'switch',
-					'name' => 'performanceDisableIconDropdown',
-					'label' => 'Disable Icon Dropdown',
-					'help' => 'Disable select dropdown boxes on new and edit tab forms',
-					'value' => $this->config['performanceDisableIconDropdown'],
+					'type' => 'select2',
+					'class' => 'select2-multiple',
+					'id' => 'blacklisted-select',
+					'name' => 'blacklisted',
+					'label' => 'Blacklisted IP\'s',
+					'value' => $this->config['blacklisted'],
+					'help' => 'WARNING! This will block anyone with these IP\'s',
+					'options' => $this->makeOptionsFromValues($this->config['blacklisted']),
+					'settings' => '{tags: true}',
 				),
 				array(
-					'type' => 'switch',
-					'name' => 'performanceDisableImageDropdown',
-					'label' => 'Disable Image Dropdown',
-					'help' => 'Disable select dropdown boxes on new and edit tab forms',
-					'value' => $this->config['performanceDisableImageDropdown'],
+					'type' => 'textbox',
+					'name' => 'blacklistedMessage',
+					'class' => '',
+					'label' => 'Blacklisted Error Message',
+					'value' => $this->config['blacklistedMessage'],
+					'attr' => 'rows="10"',
 				),
 			),
 			'Login' => array(
@@ -2201,6 +2260,14 @@ class Organizr
 					'help' => 'Enable option to set Auth Proxy Header Login',
 					'value' => $this->config['authProxyEnabled'],
 				),
+				array(
+					'type' => 'input',
+					'name' => 'authProxyWhitelist',
+					'label' => 'Auth Proxy Whitelist',
+					'value' => $this->config['authProxyWhitelist'],
+					'placeholder' => 'i.e. 10.0.0.0/24 or 10.0.0.20',
+					'help' => 'IPv4 only at the moment - This must be set to work, will accept subnet or IP address'
+				),
 				array(
 					'type' => 'input',
 					'name' => 'authProxyHeaderName',
@@ -2211,12 +2278,12 @@ class Organizr
 				),
 				array(
 					'type' => 'input',
-					'name' => 'authProxyWhitelist',
-					'label' => 'Auth Proxy Whitelist',
-					'value' => $this->config['authProxyWhitelist'],
-					'placeholder' => 'i.e. 10.0.0.0/24 or 10.0.0.20',
-					'help' => 'IPv4 only at the moment - This must be set to work, will accept subnet or IP address'
-				),
+					'name' => 'authProxyHeaderNameEmail',
+					'label' => 'Auth Proxy Header Name for Email',
+					'value' => $this->config['authProxyHeaderNameEmail'],
+					'placeholder' => 'i.e. X-Forwarded-Email',
+					'help' => 'Please choose a unique value for added security'
+				)
 			),
 			'Ping' => array(
 				array(
@@ -3057,6 +3124,7 @@ class Organizr
 		$function = 'plugin_auth_' . $this->config['authBackend'];
 		$authSuccess = false;
 		$authProxy = false;
+		$addEmailToAuthProxy = true;
 		// Check Login attempts and kill if over limit
 		if ($loginAttempts > $this->config['loginAttempts'] || isset($_COOKIE['lockout'])) {
 			$this->coookieSeconds('set', 'lockout', $this->config['loginLockout'], $this->config['loginLockout']);
@@ -3066,11 +3134,14 @@ class Organizr
 		// Check if Auth Proxy is enabled
 		if ($this->config['authProxyEnabled'] && $this->config['authProxyHeaderName'] !== '' && $this->config['authProxyWhitelist'] !== '') {
 			if (isset($this->getallheaders()[$this->config['authProxyHeaderName']])) {
-				$usernameHeader = isset($this->getallheaders()[$this->config['authProxyHeaderName']]) ? $this->getallheaders()[$this->config['authProxyHeaderName']] : $username;
+				$usernameHeader = $this->getallheaders()[$this->config['authProxyHeaderName']] ?? $username;
+				$emailHeader = $this->getallheaders()[$this->config['authProxyHeaderNameEmail']] ?? null;
 				$this->writeLog('success', 'Auth Proxy Function - Starting Verification for IP: ' . $this->userIP() . ' for request on: ' . $_SERVER['REMOTE_ADDR'] . ' against IP/Subnet: ' . $this->config['authProxyWhitelist'], $usernameHeader);
 				$whitelistRange = $this->analyzeIP($this->config['authProxyWhitelist']);
 				$authProxy = $this->authProxyRangeCheck($whitelistRange['from'], $whitelistRange['to']);
 				$username = ($authProxy) ? $usernameHeader : $username;
+				$password = ($password == null) ? $this->random_ascii_string(10) : $password;
+				$addEmailToAuthProxy = ($authProxy && $emailHeader) ? ['email' => $emailHeader] : true;
 				if ($authProxy) {
 					$this->writeLog('success', 'Auth Proxy Function - IP: ' . $this->userIP() . ' has been verified', $usernameHeader);
 				} else {
@@ -3103,7 +3174,7 @@ class Organizr
 						}
 					}
 			}
-			$authSuccess = ($authProxy) ? true : $authSuccess;
+			$authSuccess = ($authProxy) ? $addEmailToAuthProxy : $authSuccess;
 		} else {
 			// Has oAuth Token!
 			switch ($oAuthType) {
@@ -3334,7 +3405,7 @@ class Organizr
 				);
 				$PhpMailer->_phpMailerPluginSendEmail($sendEmail);
 			}
-			if ($this->createToken($username, $email, $this->gravatar($email), $this->config['rememberMeDays'])) {
+			if ($this->createToken($username, $email, $this->config['rememberMeDays'])) {
 				$this->writeLoginLog($username, 'success');
 				$this->writeLog('success', 'Login Function - A User has logged in', $username);
 				return true;
@@ -6106,6 +6177,95 @@ class Organizr
 		}
 	}
 	
+	public function CBPFWTabs()
+	{
+		return '
+		<script>
+		/**
+		* cbpFWTabs.js v1.0.0
+		* http://www.codrops.com
+		*
+		* Licensed under the MIT license.
+		* http://www.opensource.org/licenses/mit-license.php
+		*
+		* Copyright 2014, Codrops
+		* http://www.codrops.com
+		*/
+		;( function( window ) {
+			\'use strict\';
+		
+			function extend( a, b ) {
+				for( var key in b ) {
+					if( b.hasOwnProperty( key ) ) {
+						a[key] = b[key];
+					}
+				}
+				return a;
+			}
+		
+			function CBPFWTabs( el, options ) {
+				this.el = el;
+				this.options = extend( {}, this.options );
+		        extend( this.options, options );
+		        this._init();
+			}
+		
+			CBPFWTabs.prototype.options = {
+				start : 0
+			};
+		
+			CBPFWTabs.prototype._init = function() {
+				// tabs elems
+				this.tabs = [].slice.call( this.el.querySelectorAll( \'nav > ul > li\' ) );
+				// content items
+				this.items = [].slice.call( this.el.querySelectorAll( \'.content-wrap > section\' ) );
+				// current index
+				this.current = -1;
+				// show current content item
+				try{
+					if(this.tabs[0].innerHTML.indexOf(\'#settings\') >= 0){
+						this._show(' . $this->config['defaultSettingsTab'] . ');
+						let tabId = $(this.items[' . $this->config['defaultSettingsTab'] . ']).attr("id") + "-anchor";
+						$("#" + tabId).click();
+						$("#" + tabId + " a").click();
+					}else{
+						this._show();
+					}
+				}catch{
+					this._show();
+				}
+				// init events
+				this._initEvents();
+			};
+		
+			CBPFWTabs.prototype._initEvents = function() {
+				var self = this;
+				this.tabs.forEach( function( tab, idx ) {
+					tab.addEventListener( \'click\', function( ev ) {
+						ev.preventDefault();
+						self._show( idx );
+					} );
+				} );
+			};
+		
+			CBPFWTabs.prototype._show = function( idx ) {
+				if( this.current >= 0 ) {
+					this.tabs[ this.current ].className = this.items[ this.current ].className = \'\';
+				}
+				// change current
+				this.current = idx != undefined ? idx : this.options.start >= 0 && this.options.start < this.items.length ? this.options.start : 0;
+				this.tabs[ this.current ].className = \'tab-current\';
+				this.items[ this.current ].className = \'content-current\';
+			};
+		
+			// add to global namespace
+			window.CBPFWTabs = CBPFWTabs;
+		
+		})( window );
+		</script>
+		';
+	}
+	
 	public function socksHeadingHTML($app)
 	{
 		return '

+ 5 - 1
api/config/default.php

@@ -343,6 +343,7 @@ return array(
 	'homepagePlexRecentlyAddedMethod' => 'legacy',
 	'authProxyEnabled' => false,
 	'authProxyHeaderName' => '',
+	'authProxyHeaderNameEmail' => '',
 	'authProxyWhitelist' => '',
 	'ignoreTFALocal' => false,
 	'unifiURL' => '',
@@ -494,5 +495,8 @@ return array(
 	'traktAccessTokenExpires' => '',
 	'traktRefreshToken' => '',
 	'autoCollapseCategories' => false,
-	'autoExpandNavBar' => true
+	'autoExpandNavBar' => true,
+	'defaultSettingsTab' => '5',
+	'blacklisted' => '',
+	'blacklistedMessage' => 'You have been blacklisted from this site.'
 );

+ 20 - 0
api/functions/option-functions.php

@@ -2,6 +2,26 @@
 
 trait OptionsFunction
 {
+	public function makeOptionsFromValues($values = null)
+	{
+		$formattedValues = [];
+		if (strpos($values, ',') !== false) {
+			$explode = explode(',', $values);
+			foreach ($explode as $item) {
+				$formattedValues[] = [
+					'name' => $item,
+					'value' => $item
+				];
+			}
+		} else {
+			$formattedValues[] = [
+				'name' => $values,
+				'value' => $values
+			];
+		}
+		return $formattedValues;
+	}
+	
 	public function calendarLocaleOptions()
 	{
 		return [

+ 40 - 0
api/functions/organizr-functions.php

@@ -283,6 +283,36 @@ trait OrganizrFunctions
 		);
 	}
 	
+	public function getSettingsTabs()
+	{
+		return array(
+			array(
+				'name' => 'Tab Editor',
+				'value' => '0'
+			),
+			array(
+				'name' => 'Customize',
+				'value' => '1'
+			),
+			array(
+				'name' => 'User Management',
+				'value' => '2'
+			),
+			array(
+				'name' => 'Image Manager',
+				'value' => '3'
+			),
+			array(
+				'name' => 'Plugins',
+				'value' => '4'
+			),
+			array(
+				'name' => 'System Settings',
+				'value' => '5'
+			)
+		);
+	}
+	
 	public function getAuthTypes()
 	{
 		return array(
@@ -331,6 +361,16 @@ trait OrganizrFunctions
 			return strpos($v, 'plugin_auth_') === 0;
 		}) as $value) {
 			$name = str_replace('plugin_auth_', '', $value);
+			if ($name == 'ldap') {
+				if (!function_exists('ldap_connect')) {
+					continue;
+				}
+			}
+			if ($name == 'ldap_disabled') {
+				if (function_exists('ldap_connect')) {
+					continue;
+				}
+			}
 			if (strpos($name, 'disabled') === false) {
 				$backendOptions[] = array(
 					'name' => ucwords(str_replace('_', ' ', $name)),

+ 14 - 6
api/homepage/sonarr.php

@@ -50,18 +50,26 @@ trait SonarrHomepageItem
 				),
 				'Connection' => array(
 					array(
-						'type' => 'input',
+						'type' => 'select2',
+						'class' => 'select2-multiple',
+						'id' => 'sonarrURL-select',
 						'name' => 'sonarrURL',
-						'label' => 'URL',
+						'label' => 'Sonarr URL',
 						'value' => $this->config['sonarrURL'],
 						'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
-						'placeholder' => 'http(s)://hostname:port'
+						'placeholder' => 'http(s)://hostname:port',
+						'options' => $this->makeOptionsFromValues($this->config['sonarrURL']),
+						'settings' => '{tags: true}',
 					),
 					array(
-						'type' => 'password-alt',
+						'type' => 'select2',
+						'class' => 'select2-multiple',
+						'id' => 'sonarrToken-select',
 						'name' => 'sonarrToken',
-						'label' => 'Token',
-						'value' => $this->config['sonarrToken']
+						'label' => 'Sonarr Token',
+						'value' => $this->config['sonarrToken'],
+						'options' => $this->makeOptionsFromValues($this->config['sonarrToken']),
+						'settings' => '{tags: true, theme: "default password-alt"}',
 					)
 				),
 				'API SOCKS' => array(

+ 9 - 2
api/index.php

@@ -1,10 +1,17 @@
 <?php
 reset($_GET);
 $function = (key($_GET) ? str_replace("/", "_", key($_GET)) : false);
+function validateData($data)
+{
+	$data = trim($data);
+	$data = stripslashes($data);
+	return htmlspecialchars($data);
+}
+
 switch ($function) {
 	case 'v1_auth':
 		$group = ($_GET['group']) ?? 0;
-		header('Location: v2/auth?group=' . $group);
+		header('Location: v2/auth?group=' . validateData($group));
 		exit;
 	default:
 		// Forward everything to v2 api
@@ -12,4 +19,4 @@ switch ($function) {
 		$result['statusText'] = "Please Use api/v2";
 		break;
 }
-header('Location: v2/');
+header('Location: v2/');

+ 35 - 6
api/plugins/healthChecks/plugin.php

@@ -102,17 +102,18 @@ class HealthChecks extends Organizr
 					'html' => '
 						<div class="row">
 						    <div class="col-lg-12">
-						        <div class="panel panel-info">
+						        <div class="panel panel-danger">
 						            <div class="panel-heading">
 						                <span lang="en">ATTENTION</span>
 						            </div>
 						            <div class="panel-wrapper collapse in" aria-expanded="true">
 						                <div class="panel-body">
-						                	<h4 lang="en">This is only used for the import button...</h4>
+						                	<h4 lang="en">Please use a Full Access Token</h4>
 						                    <br/>
-						                    <span>
-						                    	<span lang="en">Make sure to save before using the import button on Services tab</span>
-						                    </span>
+						                    <div>
+						                    	<p lang="en">Do not use a Read-Only Token as that will not give a correct UUID for sending the results to HealthChecks.io</p>
+						                    	<p lang="en">Make sure to save before using the import button on Services tab</p>
+						                    </div>
 						                </div>
 						            </div>
 						        </div>
@@ -163,12 +164,37 @@ class HealthChecks extends Organizr
 		return $success;
 	}
 	
+	public function _healthCheckSelfHostedURLValidation($url, $checkOnly = false)
+	{
+		$selfHosted = true;
+		$url = $this->qualifyURL($url);
+		if (stripos($url, 'hc-ping.com') == false) {
+			if (stripos($url, '/ping') == false) {
+				$url = $url . '/ping';
+			}
+		} else {
+			$selfHosted = false;
+		}
+		return $checkOnly ? $selfHosted : $url;
+	}
+	
+	public function _healthCheckPluginStartUUID($uuid)
+	{
+		if (!$uuid || $this->config['HEALTHCHECKS-PingURL'] == '') {
+			return false;
+		}
+		$url = $this->_healthCheckSelfHostedURLValidation($this->config['HEALTHCHECKS-PingURL']);
+		$uuid = '/' . $uuid;
+		$options = ($this->localURL($url)) ? array('verify' => false) : array('verify' => $this->getCert());
+		return Requests::get($url . $uuid . '/start', [], $options);
+	}
+	
 	public function _healthCheckPluginUUID($uuid, $pass = false)
 	{
 		if (!$uuid || $this->config['HEALTHCHECKS-PingURL'] == '') {
 			return false;
 		}
-		$url = $this->qualifyURL($this->config['HEALTHCHECKS-PingURL']);
+		$url = $this->_healthCheckSelfHostedURLValidation($this->config['HEALTHCHECKS-PingURL']);
 		$uuid = '/' . $uuid;
 		$path = !$pass ? '/fail' : '';
 		$options = ($this->localURL($url)) ? array('verify' => false) : array('verify' => $this->getCert());
@@ -209,6 +235,9 @@ class HealthChecks extends Organizr
 				$testExternal = $v['External URL'] !== '' ?? false;
 				$testBoth = ($testLocal && $testExternal) ?? false;
 				$pass = false;
+				if ($testLocal || $testExternal || $testBoth) {
+					$this->_healthCheckPluginStartUUID($v['UUID']);
+				}
 				if ($testLocal) {
 					$allItems[$k]['results']['internal'] = ($this->_healthCheckPluginTest($v['Internal URL'])) ? 'Success' : 'Error';
 				}

+ 8 - 3
api/plugins/healthChecks/settings.js

@@ -44,9 +44,14 @@ $(document).on('click', '.importNewHCService', function() {
 					}
 					$.each(checks, function(i,v) {
 						let alreadySetup = false;
-						let uuid = v.ping_url;
-						uuid = uuid.match(/([0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12})/g);
-						uuid = uuid[0];
+						if(typeof v.unique_key !== 'undefined'){
+							data = 'Please use a Full Access Token';
+							throw new Error('Read-Only Token Used');
+						}else{
+							var uuid = v.ping_url;
+							uuid = uuid.match(/([0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12})/g);
+							uuid = uuid[0];
+						}
 						if(uuid){
 							if(checksAlreadySetup){
 								$.each(checksAlreadySetup, function(index,val) {

+ 11 - 0
api/v2/routes/root.php

@@ -100,6 +100,17 @@ $app->any('/auth-{group}', function ($request, $response, $args) {
 		->withHeader('Content-Type', 'application/json;charset=UTF-8')
 		->withStatus($GLOBALS['responseCode']);
 });
+$app->any('/auth/[{group}[/{type}[/{ips}]]]', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$_GET['group'] = $args['group'];
+	$_GET['type'] = $args['type'];
+	$_GET['ips'] = $args['ips'];
+	$Organizr->auth();
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+});
 $app->get('/launch', function ($request, $response, $args) {
 	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
 	$tabInfo = $Organizr->getUserTabsAndCategories();

+ 10 - 0
css/organizr.css

@@ -1427,6 +1427,7 @@ img.dark-logo-side {
     position: relative;
     vertical-align: middle;
     width: 100% !important;
+    z-index: 9999;
 }
 .select2-container--default .select2-selection--single .select2-selection__rendered {
     line-height: 38px;
@@ -4557,4 +4558,13 @@ html {
 }
 .language-box {
     position: absolute !important;
+}
+.password-alt li.select2-selection__choice {
+    -webkit-text-security: disc;
+}
+.password-alt li.select2-selection__choice:hover {
+    -webkit-text-security: unset;
+}
+span.select2-selection__choice__remove {
+    -webkit-text-security: initial;
 }

Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
css/organizr.min.css


+ 1 - 1
index.php

@@ -192,7 +192,6 @@ $Organizr = new Organizr();
 <script src="plugins/bower_components/jquery-wizard-master/libs/formvalidation/bootstrap.min.js"></script>
 <script src="js/bowser.min.js"></script>
 <script src="js/jasny-bootstrap.js"></script>
-<script src="js/cbpFWTabs.js?v=<?php echo $Organizr->fileHash; ?>"></script>
 <script src="js/js.cookie.js"></script>
 <script src="js/jquery-lang.min.js"></script>
 <script src="js/jquery-ui.min.js"></script>
@@ -247,6 +246,7 @@ echo $Organizr->googleTracking();
 echo $Organizr->pluginFiles('js');
 echo $Organizr->formKey();
 echo $Organizr->loadCalendarJS();
+echo $Organizr->CBPFWTabs();
 ?>
 </body>
 

+ 1 - 79
js/cbpFWTabs.js

@@ -1,79 +1 @@
-/**
- * cbpFWTabs.js v1.0.0
- * http://www.codrops.com
- *
- * Licensed under the MIT license.
- * http://www.opensource.org/licenses/mit-license.php
- *
- * Copyright 2014, Codrops
- * http://www.codrops.com
- */
-;( function( window ) {
-
-	'use strict';
-
-	function extend( a, b ) {
-		for( var key in b ) {
-			if( b.hasOwnProperty( key ) ) {
-				a[key] = b[key];
-			}
-		}
-		return a;
-	}
-
-	function CBPFWTabs( el, options ) {
-		this.el = el;
-		this.options = extend( {}, this.options );
-  		extend( this.options, options );
-  		this._init();
-	}
-
-	CBPFWTabs.prototype.options = {
-		start : 0
-	};
-
-	CBPFWTabs.prototype._init = function() {
-		// tabs elems
-		this.tabs = [].slice.call( this.el.querySelectorAll( 'nav > ul > li' ) );
-		// content items
-		this.items = [].slice.call( this.el.querySelectorAll( '.content-wrap > section' ) );
-		// current index
-		this.current = -1;
-		// show current content item
-		try{
-			if(this.tabs[0].innerHTML.indexOf('#settings') >= 0){
-				this._show(5);
-			}else{
-				this._show();
-			}
-		}catch{
-			this._show();
-		}
-		// init events
-		this._initEvents();
-	};
-
-	CBPFWTabs.prototype._initEvents = function() {
-		var self = this;
-		this.tabs.forEach( function( tab, idx ) {
-			tab.addEventListener( 'click', function( ev ) {
-				ev.preventDefault();
-				self._show( idx );
-			} );
-		} );
-	};
-
-	CBPFWTabs.prototype._show = function( idx ) {
-		if( this.current >= 0 ) {
-			this.tabs[ this.current ].className = this.items[ this.current ].className = '';
-		}
-		// change current
-		this.current = idx != undefined ? idx : this.options.start >= 0 && this.options.start < this.items.length ? this.options.start : 0;
-		this.tabs[ this.current ].className = 'tab-current';
-		this.items[ this.current ].className = 'content-current';
-	};
-
-	// add to global namespace
-	window.CBPFWTabs = CBPFWTabs;
-
-})( window );
+/* no more file */

+ 55 - 21
js/functions.js

@@ -16,6 +16,7 @@ lang.init({
 var OAuthLoginNeeded = false;
 var directToHash = false;
 var pingOrg = false;
+var checkCommitLoadStatus = false;
 var timeouts = {};
 var increment = 0;
 var tabInformation = {};
@@ -188,7 +189,7 @@ function formatDebug(result){
             formatted = result;
 
     }
-    return '<pre class="whitebox bg-org text-success">' + formatted + '</pre>';
+    return '<pre class="whitebox bg-org text-success default-scroller">' + formatted + '</pre>';
 }
 function getDebugPreInfo(){
     var formatted = 'Version: ' + activeInfo.version +
@@ -1112,7 +1113,8 @@ function buildFormItem(item){
 			break;
 		case 'select2':
             var select2ID = (item.id) ? '#'+item.id : '.'+item.name;
-            return smallLabel+'<select class="m-b-10 '+extraClass+'"'+placeholder+value+id+name+disabled+type+label+attr+' multiple="multiple" data-placeholder="Choose">'+selectOptions(item.options, item.value)+'</select><script>$("'+select2ID+'").select2();</script>';
+            let settings = (item.settings) ? item.settings : '{}';
+            return smallLabel+'<select class="m-b-10 '+extraClass+'"'+placeholder+value+id+name+disabled+type+label+attr+' multiple="multiple" data-placeholder="Choose">'+selectOptions(item.options, item.value)+'</select><script>$("'+select2ID+'").select2('+settings+');</script>';
 			break;
 		case 'switch':
 		case 'checkbox':
@@ -3480,6 +3482,7 @@ function updateCheck(){
 		if(latest !== currentVersion) {
 			organizrConsole('Update Function','Update to ' + latest + ' is available', 'warning');
             if (activeInfo.settings.misc.docker === false) {
+	            closeAllMessages();
                 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');
             }
         }else{
@@ -3553,22 +3556,28 @@ function newsLoad(){
 }
 function checkCommitLoad(){
     if(activeInfo.settings.misc.docker && activeInfo.settings.misc.githubCommit !== 'n/a' && activeInfo.settings.misc.githubCommit !== null) {
-        getLatestCommitJSON().success(function (data) {
-            try {
-                var latest = data.sha.toString().trim();
-                var current = activeInfo.settings.misc.githubCommit.toString().trim();
-                var link = 'https://github.com/causefx/Organizr/compare/'+current+'...'+latest;
-                if(latest !== current) {
-                    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{
-	                organizrConsole('Update Function','Organizr Docker - Up to date');
-                }
-            } catch (e) {
-	            organizrCatchError(e,data);
-            }
-        }).fail(function (xhr) {
-            console.error("Organizr Function: Github Connection Failed");
-        });
+	    if(checkCommitLoadStatus == false) {
+		    checkCommitLoadStatus = true;
+		    getLatestCommitJSON().success(function (data) {
+			    try {
+				    var latest = data.sha.toString().trim();
+				    var current = activeInfo.settings.misc.githubCommit.toString().trim();
+				    var link = 'https://github.com/causefx/Organizr/compare/' + current + '...' + latest;
+				    if (latest !== current) {
+					    closeAllMessages();
+					    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 {
+					    organizrConsole('Update Function', 'Organizr Docker - Up to date');
+				    }
+			    } catch (e) {
+				    organizrCatchError(e, data);
+			    }
+			    checkCommitLoadStatus = false;
+		    }).fail(function (xhr) {
+			    console.error("Organizr Function: Github Connection Failed");
+			    checkCommitLoadStatus = false;
+		    });
+	    }
     }
 }
 function sponsorLoad(){
@@ -3635,9 +3644,6 @@ function buildBackers(array){
         <!-- /.usercard-->
     `;
 	return backers;
-
-
-
 }
 function sponsorDetails(id){
 	sponsorsJSON().success(function(data) {
@@ -9845,6 +9851,29 @@ function messageSingle(heading,text,position,color,icon,timeout){
         setTimeout(function(){ messageSingle(heading,text,position,color,icon,timeout); }, 100);
     }
 }
+
+function closeAllMessages(){
+	let bb = activeInfo.settings.notifications.backbone;
+	if(notificationsReady){
+		switch (bb) {
+			case 'toastr':
+				$.toast().reset('all');
+				break;
+			case 'izi':
+				iziToast.destroy();
+				break;
+			case 'alertify':
+				alertify.dismissAll();
+				break;
+			case 'noty':
+				Noty.closeAll();
+				break;
+			default:
+				return false;
+		}
+	}
+}
+
 function blockDev(e) {
     var evtobj = window.event ? event : e;
     if (evtobj.keyCode == 73 && evtobj.shiftKey && evtobj.ctrlKey){
@@ -10685,6 +10714,11 @@ function loadJavascript(script = null, defer = false){
 		}
 	}
 }
+
+function tabShit(){
+
+}
+
 function launch(){
 	console.info('https://docs.organizr.app/books/setup-features/page/organizr-20--%3E-21-migration-guide');
 	organizrConsole('API V2 API','If you see a 404 Error for api/v2/launch below this line, you have not setup the new location block... See URL above this line', 'error');

+ 7 - 0
js/version.json

@@ -383,5 +383,12 @@
     "new": "Updated UI on update page|Refactored the way plugins are stored so it is cleaner|Updated healthChecks plugin|option to Auto-collapse categories (FR#150933)|option to disable auto expand of navbar FR#150876|language-box css class|add extras to unifi homepage call|warning to sso functions|healthchecks import to plugin|toggle for 401 and 403 bypass for healthchecks plugin",
     "fixed": "SSO failing due to timeout error white getting tokens (#1665)|force Radarr to use v3 endpoint now (#1663)|plex error not catching|update downloader refresh times to be independent|update requestOptions function|updated scrollbars again|change to edit homepage modal|set correct tab type after re-adding the homepage (#1671)",
     "notes": ""
+  },
+  "2.1.426": {
+    "date": "2021-07-09 19:20",
+    "title": "Some new features",
+    "new": "global blacklist user feature|option to define which settings page tab will open by default|sonarr to use new input method for token and url|icon for Prowlarr|email for Auth Proxy|start time and url validation for selfhosted url|new auth URL scheme",
+    "fixed": "selfhosted healtcheck import issue|validate data on unused API endpoint|authRegister bug requiring 2 logins|help message for blacklist|catch all for single item parse on options|LDAP showing in backend|weather homepage|checking of commit",
+    "notes": "update HC Plugin to only use Full Access Token|closeAllMessages function to update call"
   }
 }

BIN
plugins/images/tabs/prowlarr.png


Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff