Przeglądaj źródła

Merge pull request #1752 from causefx/v2-develop

V2 develop
causefx 4 lat temu
rodzic
commit
cf2b91b031

+ 2 - 2
README.md

@@ -38,14 +38,14 @@ Do you have quite a bit of services running on your computer or server? Do you h
 - Custom tabs for your services
 - Customise the top bar by adding your own site logo or site name
 - Enable or disable iFrame for your tabs
-- Fail2ban support ([see wiki](https://docs.organizr.app/books/setup-features/page/fail2ban-integration))
+- Fail2ban support ([see wiki](https://docs.organizr.app/features/fail2ban-integration))
 - Fullscreen Support
 - Gravatar Support
 - Keyboard shortcut support (Check help tab in settings)
 - Login with Plex/Emby/LDAP or sFTP credentials
 - Mobile support
 - Multiple login support
-- Nginx Auth_Request support ([see wiki](https://docs.organizr.app/books/setup-features/page/serverauth))
+- Nginx Auth_Request support ([see wiki](https://docs.organizr.app/features/server-authentication))
 - Organizr login log viewer
 - Personalise any theme: Customise the look and feel of Organizr with access to the colour palette
 - Pin/Unpin sidebar

+ 84 - 84
api/classes/deluge.class.php

@@ -6,14 +6,14 @@ class deluge
 	private $url;
 	private $request_id;
 	public $options;
-	
+
 	public function __construct($host, $password, $options)
 	{
-		$verify = (isset($options['verify'])) ? $options['verify'] : false;
+		$verify = $options['verify'] ?? false;
 		$this->url = $host . (substr($host, -1) == "/" ? "" : "/") . "json";
 		$this->request_id = 0;
 		$this->ch = curl_init($this->url);
-		switch (gettype($options['verify'])) {
+		switch (gettype($verify)) {
 			case 'string':
 				$cert = $options['custom_cert'];
 				$verify = 2;
@@ -47,7 +47,7 @@ class deluge
 			throw new Exception($result);
 		}
 	}
-	
+
 	/////////////////////////////
 	//
 	//webapi functions (https://github.com/idlesign/deluge-webapi)
@@ -65,7 +65,7 @@ class deluge
 		//throw new Exception("Login failed");
 		//}
 	}
-	
+
 	//metainfo is base64 encoded torrent data or a magnet url
 	//returns a torrent hash or null if the torrent wasn't aded
 	//params are torrent addition parameters like download_location?
@@ -73,7 +73,7 @@ class deluge
 	{
 		return $this->makeRequest("webapi.add_torrent", array($metaInfo, $params));
 	}
-	
+
 	/*implemented in core
 	public function removeTorrent($hash, $removeData) {
 		return $this->makeRequest("webapi.remove_torrent", array($hash, $removeData));
@@ -82,7 +82,7 @@ class deluge
 	{
 		return $this->makeRequest("webapi.get_api_version", array());
 	}
-	
+
 	/////////////////////////////
 	//
 	//core functions
@@ -99,7 +99,7 @@ class deluge
 	{
 		return $this->makeRequest("core.add_torrent_file", array($filename, $filedump, $options));
 	}
-	
+
 	//Adds a torrent from a magnet link.
 	//Parameters:
 	//uri (string) – the magnet link
@@ -108,7 +108,7 @@ class deluge
 	{
 		return $this->makeRequest("core.add_torrent_magnet", array($uri, $options));
 	}
-	
+
 	//Adds a torrent from a url. Deluge will attempt to fetch the torrentfrom url prior to adding it to the session.
 	//Parameters:
 	//url (string) – the url pointing to the torrent file
@@ -118,173 +118,173 @@ class deluge
 	{
 		return $this->makeRequest("core.add_torrent_url", array($url, $options, $headers));
 	}
-	
+
 	public function connectPeer($torrentId, $ip, $port)
 	{
 		return $this->makeRequest("core.connect_peer", array($torrentId, $ip, $port));
 	}
-	
+
 	public function createTorrent($path, $tracker, $pieceLength, $comment, $target, $webseeds, $private, $createdBy, $trackers, $addToSession)
 	{
 		return $this->makeRequest("core.create_torrent", array($path, $tracker, $pieceLength, $comment, $target, $webseeds, $private, $createdBy, $trackers, $addToSession));
 	}
-	
+
 	public function disablePlugin($plugin)
 	{
 		return $this->makeRequest("core.disable_plugin", array($plugin));
 	}
-	
+
 	public function enablePlugin($plugin)
 	{
 		return $this->makeRequest("core.enable_plugin", array($plugin));
 	}
-	
+
 	public function forceReannounce($torrentIds)
 	{
 		return $this->makeRequest("core.force_reannounce", array($torrentIds));
 	}
-	
+
 	//Forces a data recheck on torrent_ids
 	public function forceRecheck($torrentIds)
 	{
 		return $this->makeRequest("core.force_recheck", array($torrentIds));
 	}
-	
+
 	//Returns a list of plugins available in the core
 	public function getAvailablePlugins()
 	{
 		return $this->makeRequest("core.get_available_plugins", array());
 	}
-	
+
 	//Returns a dictionary of the session’s cache status.
 	public function getCacheStatus()
 	{
 		return $this->makeRequest("core.get_cache_status", array());
 	}
-	
+
 	//Get all the preferences as a dictionary
 	public function getConfig()
 	{
 		return $this->makeRequest("core.get_config", array());
 	}
-	
+
 	//Get the config value for key
 	public function getConfigValue($key)
 	{
 		return $this->makeRequest("core.get_config_value", array($key));
 	}
-	
+
 	//Get the config values for the entered keys
 	public function getConfigValues($keys)
 	{
 		return $this->makeRequest("core.get_config_values", array($keys));
 	}
-	
+
 	//Returns a list of enabled plugins in the core
 	public function getEnabledPlugins()
 	{
 		return $this->makeRequest("core.get_enabled_plugins", array());
 	}
-	
+
 	//returns {field: [(value,count)] }for use in sidebar(s)
 	public function getFilterTree($showZeroHits, $hideCat)
 	{
 		return $this->makeRequest("core.get_filter_tree", array($showZeroHits, $hideCat));
 	}
-	
+
 	//Returns the number of free bytes at path
 	public function getFreeSpace($path)
 	{
 		return $this->makeRequest("core.get_free_space", array($path));
 	}
-	
+
 	//Returns the libtorrent version.
 	public function getLibtorrentVersion()
 	{
 		return $this->makeRequest("core.get_libtorrent_version", array());
 	}
-	
+
 	//Returns the active listen port
 	public function getListenPort()
 	{
 		return $this->makeRequest("core.get_listen_port", array());
 	}
-	
+
 	//Returns the current number of connections
 	public function getNumConnections()
 	{
 		return $this->makeRequest("core.get_num_connections", array());
 	}
-	
+
 	public function getPathSize($path)
 	{
 		return $this->makeRequest("core.get_path_size", array($path));
 	}
-	
+
 	//Returns a list of torrent_ids in the session.
 	public function getSessionState()
 	{
 		return $this->makeRequest("core.get_session_state", array());
 	}
-	
+
 	//Gets the session status values for ‘keys’, these keys are takingfrom libtorrent’s session status.
 	public function getSessionStatus($keys)
 	{
 		return $this->makeRequest("core.get_session_status", array($keys));
 	}
-	
+
 	public function getTorrentStatus($torrentId, $keys, $diff)
 	{
 		return $this->makeRequest("core.get_torrent_status", array($torrentId, $keys, $diff));
 	}
-	
+
 	//returns all torrents , optionally filtered by filter_dict.
 	public function getTorrentsStatus($filterDict, $keys, $diff)
 	{
 		return $this->makeRequest("core.get_torrents_status", array($filterDict, $keys, $diff));
 	}
-	
+
 	public function glob($path)
 	{
 		return $this->makeRequest("core.glob", array($path));
 	}
-	
+
 	public function moveStorage($torrentIds, $dest)
 	{
 		return $this->makeRequest("core.move_storage", array($torrentIds, $dest));
 	}
-	
+
 	//Pause all torrents in the session
 	public function pauseAllTorrents()
 	{
 		return $this->makeRequest("core.pause_all_torrents", array());
 	}
-	
+
 	public function pauseTorrent($torrentIds)
 	{
 		return $this->makeRequest("core.pause_torrent", array($torrentIds));
 	}
-	
+
 	public function queueBottom($torrentIds)
 	{
 		return $this->makeRequest("core.queue_bottom", array($torrentIds));
 	}
-	
+
 	public function queueDown($torrentIds)
 	{
 		return $this->makeRequest("core.queue_down", array($torrentIds));
 	}
-	
+
 	public function queueTop($torrentIds)
 	{
 		return $this->makeRequest("core.queue_top", array($torrentIds));
 	}
-	
+
 	public function queueUp($torrentIds)
 	{
 		return $this->makeRequest("core.queue_up", array($torrentIds));
 	}
-	
+
 	//Removes a torrent from the session.
 	//Parameters:
 	//torrentId (string) – the torrentId of the torrent to remove
@@ -293,7 +293,7 @@ class deluge
 	{
 		return $this->makeRequest("core.remove_torrent", array($torrentId, $removeData));
 	}
-	
+
 	//Rename files in torrent_id.  Since this is an asynchronous operation bylibtorrent, watch for the TorrentFileRenamedEvent to know when thefiles have been renamed.
 	//Parameters:
 	//torrentId (string) – the torrentId to rename files
@@ -302,7 +302,7 @@ class deluge
 	{
 		return $this->makeRequest("core.rename_files", array($torrentId, $filenames));
 	}
-	
+
 	//Renames the ‘folder’ to ‘new_folder’ in ‘torrent_id’.  Watch for theTorrentFolderRenamedEvent which is emitted when the folder has beenrenamed successfully.
 	//Parameters:
 	//torrentId (string) – the torrent to rename folder in
@@ -312,142 +312,142 @@ class deluge
 	{
 		return $this->makeRequest("core.rename_folder", array($torrentId, $folder, $newFolder));
 	}
-	
+
 	//Rescans the plugin folders for new plugins
 	public function rescanPlugins()
 	{
 		return $this->makeRequest("core.rescan_plugins", array());
 	}
-	
+
 	//Resume all torrents in the session
 	public function resumeAllTorrents()
 	{
 		return $this->makeRequest("core.resume_all_torrents", array());
 	}
-	
+
 	public function resumeTorrent($torrentIds)
 	{
 		return $this->makeRequest("core.resume_torrent", array($torrentIds));
 	}
-	
+
 	//Set the config with values from dictionary
 	public function setConfig($config)
 	{
 		return $this->makeRequest("core.set_config", array($config));
 	}
-	
+
 	//Sets the auto managed flag for queueing purposes
 	public function setTorrentAutoManaged($torrentId, $value)
 	{
 		return $this->makeRequest("core.set_torrent_auto_managed", array($torrentId, $value));
 	}
-	
+
 	//Sets a torrents file priorities
 	public function setTorrentFilePriorities($torrentId, $priorities)
 	{
 		return $this->makeRequest("core.set_torrent_file_priorities", array($torrentId, $priorities));
 	}
-	
+
 	//Sets a torrents max number of connections
 	public function setTorrentMaxConnections($torrentId, $value)
 	{
 		return $this->makeRequest("core.set_torrent_max_connections", array($torrentId, $value));
 	}
-	
+
 	//Sets a torrents max download speed
 	public function setTorrentMaxDownloadSpeed($torrentId, $value)
 	{
 		return $this->makeRequest("core.set_torrent_max_download_speed", array($torrentId, $value));
 	}
-	
+
 	//Sets a torrents max number of upload slots
 	public function setTorrentMaxUploadSlots($torrentId, $value)
 	{
 		return $this->makeRequest("core.set_torrent_max_upload_slots", array($torrentId, $value));
 	}
-	
+
 	//Sets a torrents max upload speed
 	public function setTorrentMaxUploadSpeed($torrentId, $value)
 	{
 		return $this->makeRequest("core.set_torrent_max_upload_speed", array($torrentId, $value));
 	}
-	
+
 	//Sets the torrent to be moved when completed
 	public function setTorrentMoveCompleted($torrentId, $value)
 	{
 		return $this->makeRequest("core.set_torrent_move_completed", array($torrentId, $value));
 	}
-	
+
 	//Sets the path for the torrent to be moved when completed
 	public function setTorrentMoveCompletedPath($torrentId, $value)
 	{
 		return $this->makeRequest("core.set_torrent_move_completed_path", array($torrentId, $value));
 	}
-	
+
 	//Sets the torrent options for torrent_ids
 	public function setTorrentOptions($torrentIds, $options)
 	{
 		return $this->makeRequest("core.set_torrent_options", array($torrentIds, $options));
 	}
-	
+
 	//Sets a higher priority to the first and last pieces
 	public function setTorrentPrioritizeFirstLast($torrentId, $value)
 	{
 		return $this->makeRequest("core.set_torrent_prioritize_first_last", array($torrentId, $value));
 	}
-	
+
 	//Sets the torrent to be removed at ‘stop_ratio’
 	public function setTorrentRemoveAtRatio($torrentId, $value)
 	{
 		return $this->makeRequest("core.set_torrent_remove_at_ratio", array($torrentId, $value));
 	}
-	
+
 	//Sets the torrent to stop at ‘stop_ratio’
 	public function setTorrentStopAtRatio($torrentId, $value)
 	{
 		return $this->makeRequest("core.set_torrent_stop_at_ratio", array($torrentId, $value));
 	}
-	
+
 	//Sets the ratio when to stop a torrent if ‘stop_at_ratio’ is set
 	public function setTorrentStopRatio($torrentId, $value)
 	{
 		return $this->makeRequest("core.set_torrent_stop_ratio", array($torrentId, $value));
 	}
-	
+
 	//Sets a torrents tracker list.  trackers will be [{“url”, “tier”}]
 	public function setTorrentTrackers($torrentId, $trackers)
 	{
 		return $this->makeRequest("core.set_torrent_trackers", array($torrentId, $trackers));
 	}
-	
+
 	//Checks if the active port is open
 	public function testListenPort()
 	{
 		return $this->makeRequest("core.test_listen_port", array());
 	}
-	
+
 	public function uploadPlugin($filename, $filedump)
 	{
 		return $this->makeRequest("core.upload_plugin", array($filename, $filedump));
 	}
-	
+
 	//Returns a list of the exported methods.
 	public function getMethodList()
 	{
 		return $this->makeRequest("daemon.get_method_list", array());
 	}
-	
+
 	//Returns some info from the daemon.
 	public function info()
 	{
 		return $this->makeRequest("daemon.info", array());
 	}
-	
+
 	public function shutdown(...$params)
 	{
 		return $this->makeRequest("daemon.shutdown", $params);
 	}
-	
+
 	/////////////////////////////
 	//
 	//web ui functions
@@ -464,38 +464,38 @@ class deluge
 	{
 		return $this->makeRequest("web.add_host", array($host, $port, $username, $password));
 	}
-	
+
 	//Usage
 	public function addTorrents($torrents)
 	{
 		return $this->makeRequest("web.add_torrents", array($torrents));
 	}
-	
+
 	public function connect($hostId)
 	{
 		return $this->makeRequest("web.connect", array($hostId));
 	}
-	
+
 	public function connected()
 	{
 		return $this->makeRequest("web.connected", array());
 	}
-	
+
 	public function deregisterEventListener($event)
 	{
 		return $this->makeRequest("web.deregister_event_listener", array($event));
 	}
-	
+
 	public function disconnect()
 	{
 		return $this->makeRequest("web.disconnect", array());
 	}
-	
+
 	public function downloadTorrentFromUrl($url, $cookie)
 	{
 		return $this->makeRequest("web.download_torrent_from_url", array($url, $cookie));
 	}
-	
+
 	/* in core
 	public function getConfig() {
 		return $this->makeRequest("web.get_config", array());
@@ -504,42 +504,42 @@ class deluge
 	{
 		return $this->makeRequest("web.get_events", array());
 	}
-	
+
 	public function getHost($hostId)
 	{
 		return $this->makeRequest("web.get_host", array($hostId));
 	}
-	
+
 	public function getHostStatus($hostId)
 	{
 		return $this->makeRequest("web.get_host_status", array($hostId));
 	}
-	
+
 	public function getHosts()
 	{
 		return $this->makeRequest("web.get_hosts", array());
 	}
-	
+
 	public function getTorrentFiles($torrentId)
 	{
 		return $this->makeRequest("web.get_torrent_files", array($torrentId));
 	}
-	
+
 	public function getTorrentInfo($filename)
 	{
 		return $this->makeRequest("web.get_torrent_info", array($filename));
 	}
-	
+
 	public function registerEventListener($event)
 	{
 		return $this->makeRequest("web.register_event_listener", array($event));
 	}
-	
+
 	public function removeHost($connectionId)
 	{
 		return $this->makeRequest("web.remove_host", array($connectionId));
 	}
-	
+
 	/*in core
 	public function setConfig($config) {
 		return $this->makeRequest("web.set_config", array($config));
@@ -548,12 +548,12 @@ class deluge
 	{
 		return $this->makeRequest("web.start_daemon", array($port));
 	}
-	
+
 	public function stopDaemon($hostId)
 	{
 		return $this->makeRequest("web.stop_daemon", array($hostId));
 	}
-	
+
 	//Parameters:
 	//keys (list) – the information about the torrents to gather
 	//filterDict (dictionary) – the filters to apply when selecting torrents.
@@ -561,7 +561,7 @@ class deluge
 	{
 		return $this->makeRequest("web.update_ui", array($keys, $filterDict));
 	}
-	
+
 	private function makeRequest($method, $params)
 	{
 		$post_data = array("id" => $this->request_id, "method" => $method, "params" => $params);

Plik diff jest za duży
+ 139 - 113
api/classes/organizr.class.php


+ 1 - 0
api/config/default.php

@@ -618,6 +618,7 @@ return [
 	'logPageSize' => '50',
 	'includeDatabaseQueriesInDebug' => false,
 	'externalPluginMarketplaceRepos' => '',
+	'githubAccessToken' => '',
 	'checkForPluginUpdate' => true,
 	'autoUpdateCronEnabled' => false,
 	'autoUpdateCronSchedule' => '@weekly'

+ 3 - 3
api/functions/auth-functions.php

@@ -41,7 +41,7 @@ trait AuthFunctions
 				// Custom LDAP Options
 				'custom_options' => [
 					// See: http://php.net/ldap_set_option
-					//LDAP_OPT_X_TLS_REQUIRE_CERT => LDAP_OPT_X_TLS_HARD
+					LDAP_OPT_X_TLS_REQUIRE_CERT => LDAP_OPT_X_TLS_ALLOW
 				]
 			];
 			// Add a connection provider to Adldap.
@@ -116,7 +116,7 @@ trait AuthFunctions
 				// Custom LDAP Options
 				'custom_options' => [
 					// See: http://php.net/ldap_set_option
-					//LDAP_OPT_X_TLS_REQUIRE_CERT => LDAP_OPT_X_TLS_HARD
+					LDAP_OPT_X_TLS_REQUIRE_CERT => LDAP_OPT_X_TLS_ALLOW
 				]
 			];
 			// Add a connection provider to Adldap.
@@ -313,7 +313,7 @@ trait AuthFunctions
 				// Custom LDAP Options
 				'custom_options' => [
 					// See: http://php.net/ldap_set_option
-					//LDAP_OPT_X_TLS_REQUIRE_CERT => LDAP_OPT_X_TLS_HARD
+					LDAP_OPT_X_TLS_REQUIRE_CERT => LDAP_OPT_X_TLS_ALLOW
 				]
 			];
 			// Add a connection provider to Adldap.

+ 2 - 0
api/functions/normal-functions.php

@@ -327,6 +327,7 @@ trait NormalFunctions
 		if (!$http) {
 			$HTTPOnly = false;
 		}
+		$_SERVER['HTTP_HOST'] = $_SERVER['HTTP_HOST'] ?? '';
 		$Domain = $this->parseDomain($_SERVER['HTTP_HOST']);
 		$DomainTest = $this->parseDomain($_SERVER['HTTP_HOST'], true);
 		if ($type == 'set') {
@@ -375,6 +376,7 @@ trait NormalFunctions
 		if (!$http) {
 			$HTTPOnly = false;
 		}
+		$_SERVER['HTTP_HOST'] = $_SERVER['HTTP_HOST'] ?? '';
 		$Domain = $this->parseDomain($_SERVER['HTTP_HOST']);
 		$DomainTest = $this->parseDomain($_SERVER['HTTP_HOST'], true);
 		if ($type == 'set') {

+ 12 - 13
api/homepage/radarr.php

@@ -61,7 +61,7 @@ trait RadarrHomepageItem
 		];
 		return array_merge($homepageInformation, $homepageSettings);
 	}
-	
+
 	public function testConnectionRadarr()
 	{
 		if (empty($this->config['radarrURL'])) {
@@ -93,7 +93,6 @@ trait RadarrHomepageItem
 					$errors .= $ip . ': Response was not JSON';
 					$failed = true;
 				}
-				
 			} catch (Exception $e) {
 				$failed = true;
 				$ip = $value['url'];
@@ -109,7 +108,7 @@ trait RadarrHomepageItem
 			return true;
 		}
 	}
-	
+
 	public function radarrHomepagePermissions($key = null)
 	{
 		$permissions = [
@@ -142,7 +141,7 @@ trait RadarrHomepageItem
 		];
 		return $this->homepageCheckKeyPermissions($key, $permissions);
 	}
-	
+
 	public function homepageOrderRadarrQueue()
 	{
 		if ($this->homepageItemPermissions($this->radarrHomepagePermissions('queue'))) {
@@ -161,7 +160,7 @@ trait RadarrHomepageItem
 				';
 		}
 	}
-	
+
 	public function getRadarrQueue()
 	{
 		if (!$this->homepageItemPermissions($this->radarrHomepagePermissions('queue'), true)) {
@@ -176,24 +175,25 @@ trait RadarrHomepageItem
 				$results = $downloader->getQueue();
 				$downloadList = json_decode($results, true);
 				if (is_array($downloadList) || is_object($downloadList)) {
-					$queue = (array_key_exists('error', $downloadList)) ? '' : $downloadList;
+					$queue = (array_key_exists('error', $downloadList)) ? [] : $downloadList;
+					$queue = $queue['records'] ?? $queue;
 				} else {
-					$queue = '';
+					$queue = [];
 				}
 				if (!empty($queue)) {
 					$queueItems = array_merge($queueItems, $queue);
 				}
 			} catch (Exception $e) {
-				$this->writeLog('error', 'Radarr Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
+				$this->logger->error($e);
 			}
 		}
 		$api['content']['queueItems'] = $queueItems;
 		$api['content']['historyItems'] = false;
-		$api['content'] = isset($api['content']) ? $api['content'] : false;
+		$api['content'] = $api['content'] ?? false;
 		$this->setAPIResponse('success', null, 200, $api);
-		return $api;;
+		return $api;
 	}
-	
+
 	public function getRadarrCalendar($startDate = null, $endDate = null)
 	{
 		$startDate = ($startDate) ?? $_GET['start'] ?? date('Y-m-d', strtotime('-' . $this->config['calendarStart'] . ' days'));
@@ -227,7 +227,7 @@ trait RadarrHomepageItem
 		$this->setAPIResponse('success', null, 200, $calendarItems);
 		return $calendarItems;
 	}
-	
+
 	public function formatRadarrCalendar($array, $number, $url)
 	{
 		$url = rtrim($url, '/'); //remove trailing slash
@@ -298,7 +298,6 @@ trait RadarrHomepageItem
 						} else {
 							$banner = $image['url'];
 						}
-						
 					}
 				}
 				if ($banner !== "/plugins/images/cache/no-np.png" || (strpos($banner, 'apikey') !== false)) {

+ 11 - 11
api/homepage/sonarr.php

@@ -62,7 +62,7 @@ trait SonarrHomepageItem
 		];
 		return array_merge($homepageInformation, $homepageSettings);
 	}
-	
+
 	public function testConnectionSonarr()
 	{
 		if (empty($this->config['sonarrURL'])) {
@@ -94,7 +94,6 @@ trait SonarrHomepageItem
 					$errors .= $ip . ': Response was not JSON';
 					$failed = true;
 				}
-				
 			} catch (Exception $e) {
 				$failed = true;
 				$ip = $value['url'];
@@ -110,7 +109,7 @@ trait SonarrHomepageItem
 			return true;
 		}
 	}
-	
+
 	public function sonarrHomepagePermissions($key = null)
 	{
 		$permissions = [
@@ -143,7 +142,7 @@ trait SonarrHomepageItem
 		];
 		return $this->homepageCheckKeyPermissions($key, $permissions);
 	}
-	
+
 	public function homepageOrderSonarrQueue()
 	{
 		if ($this->homepageItemPermissions($this->sonarrHomepagePermissions('queue'))) {
@@ -162,7 +161,7 @@ trait SonarrHomepageItem
 				';
 		}
 	}
-	
+
 	public function getSonarrQueue()
 	{
 		if (!$this->homepageItemPermissions($this->sonarrHomepagePermissions('queue'), true)) {
@@ -177,9 +176,10 @@ trait SonarrHomepageItem
 				$results = $downloader->getQueue();
 				$downloadList = json_decode($results, true);
 				if (is_array($downloadList) || is_object($downloadList)) {
-					$queue = (array_key_exists('error', $downloadList)) ? '' : $downloadList;
+					$queue = (array_key_exists('error', $downloadList)) ? [] : $downloadList;
+					$queue = $queue['records'] ?? $queue;
 				} else {
-					$queue = '';
+					$queue = [];
 				}
 				if (!empty($queue)) {
 					$queueItems = array_merge($queueItems, $queue);
@@ -190,11 +190,11 @@ trait SonarrHomepageItem
 		}
 		$api['content']['queueItems'] = $queueItems;
 		$api['content']['historyItems'] = false;
-		$api['content'] = isset($api['content']) ? $api['content'] : false;
+		$api['content'] = $api['content'] ?? false;
 		$this->setAPIResponse('success', null, 200, $api);
-		return $api;;
+		return $api;
 	}
-	
+
 	public function getSonarrCalendar($startDate = null, $endDate = null)
 	{
 		$startDate = ($startDate) ?? $_GET['start'] ?? date('Y-m-d', strtotime('-' . $this->config['calendarStart'] . ' days'));
@@ -228,7 +228,7 @@ trait SonarrHomepageItem
 		$this->setAPIResponse('success', null, 200, $calendarItems);
 		return $calendarItems;
 	}
-	
+
 	public function formatSonarrCalendar($array, $number)
 	{
 		$array = json_decode($array, true);

+ 6 - 1
api/vendor/kryptonit3/sonarr/src/Sonarr.php

@@ -341,9 +341,14 @@ class Sonarr
             'uri' => $uri,
             'type' => 'get',
             'data' => [
-            	'includeUnknownSeriesItems' => false
+            	'includeUnknownSeriesItems' => 'false',
+	            'pageSize' => 1000
             ]
         ];
+	    if ( $this->type == 'sonarr' ) {
+		    $response['data']['includeSeries'] = 'true';
+		    $response['data']['includeEpisode'] = 'true';
+	    }
 
         return $this->processRequest($response);
     }

+ 3 - 3
cron.php

@@ -64,7 +64,7 @@ if ($Organizr->isLocalOrServer() && $Organizr->hasDB()) {
 								})
 								->at($Organizr->config[$cronJob['schedule']]);
 						} else {
-							$Organizr->warning('Method error', ['cronJob' => $cronJob['class']]);
+							$Organizr->logger->warning('Method error', ['cronJob' => $cronJob['class']]);
 						}
 					} catch (InvalidArgumentException $e) {
 						$Organizr->logger->warning('Cron schedule has failed validation', ['schedule' => $Organizr->config[$cronJob['schedule']]]);
@@ -75,7 +75,7 @@ if ($Organizr->isLocalOrServer() && $Organizr->hasDB()) {
 						break;
 					}
 				} else {
-					$Organizr->warning('Class error', ['cronJob' => $cronJob['class']]);
+					$Organizr->logger->warning('Class error', ['cronJob' => $cronJob['class']]);
 				}
 			} else {
 				$Organizr->logger->debug('Cron job is not enabled', ['cronJob' => $cronJob]);
@@ -108,7 +108,7 @@ if ($Organizr->isLocalOrServer() && $Organizr->hasDB()) {
 	//$Organizr->prettyPrint($scheduler->getFailedJobs());
 	$Organizr->logger->debug('Cron process completion', ['verbose' => $scheduler->getVerboseOutput()]);
 	if (!empty($scheduler->getFailedJobs())) {
-		$Organizr->logger->warning('Cron jobs have failed', ['jobs' => $scheduler->getFailedJobs()]);
+		$Organizr->logger->warning('Cron jobs have failed', ['jobs' => $scheduler->getFailedJobs(), 'verbose' => $scheduler->getVerboseOutput()]);
 	}
 	// End Run and set file with time
 	$Organizr->createCronFile();

+ 5 - 3
js/functions.js

@@ -4352,6 +4352,7 @@ function organizrAPI2(type,path,data=null,asyncValue=true){
 	}
 }
 function loadSettingsPage2(api,element,organizrFn){
+    $(element).html('<h2 class="col-lg-12 text-center well"><i class="fa fa-spin fa-refresh"></i><br> Loading</h2>');
 	organizrAPI2('get',api).success(function(data) {
 		try {
 			var response = data.response;
@@ -5244,7 +5245,7 @@ function buildStreamTooltip(bandwidth, streams, type){
     if(streams['transcode'] !== 0){
         streamText += spacer + streams['transcode']  + ' Transcode(s)';
     }
-    html += '<span class="label label-info m-l-20 mouse" title="" data-toggle="tooltip" data-original-title="'+ streamText + bandwidthText +'"><i class="fa fa-info"></i></span>';
+    html += '<span class="label label-info m-l-20 mouse" title="" data-toggle="tooltip" data-original-title="'+ streamText + bandwidthText +'" data-placement="bottom"><i class="fa fa-info"></i></span>';
     return `
     <script>$('.streamDetails-`+type+`').html('`+html+`');$('[data-toggle="tooltip"]').tooltip();</script>
     `;
@@ -6510,12 +6511,13 @@ function buildDownloaderItem(array, source, type='none'){
 				queue = '<tr><td class="max-texts" lang="en">Nothing in queue</td></tr>';
 				break;
 			}
-			$.each(array.content.queueItems, function(i,v) {
+            let sonarrQueueSet = (typeof array.content.queueItems.records == 'undefined') ? array.content.queueItems : array.content.queueItems.records;
+			$.each(sonarrQueueSet, function(i,v) {
 				count = count + 1;
 				var percent = Math.floor(((v.size - v.sizeleft) / v.size) * 100);
 				percent = (isNaN(percent)) ? '0' : percent;
 				var size = v.size != -1 ? humanFileSize(v.size,false) : "?";
-				v.name = v.series.title;
+                v.name = (typeof v.series == 'undefined') ? v.title : v.series.title;
 				queue += `
                 <tr>
                     <td class="">`+v.name+`</td>

+ 182 - 48
js/langpack/pl[Polish].json

@@ -534,7 +534,7 @@
         "Speedtest": "Test prędkości",
         "Unit of Measurement": "Jednostka miary",
         "Enable Pollen": "Włącz pylenie",
-        "Enable Air Quality": "Włącz jakość powietrza",
+        "Enable Air Quality": "Włącz Air Quality",
         "Enable Weather": "Włącz pogodę",
         "Need Help With Coordinates?": "Potrzebujesz pomocy ze współrzędnymi?",
         "Longitude": "Długość",
@@ -614,54 +614,54 @@
         "Max Login Attempts": "Maksymalna liczba prób logowania",
         "Test Login": "Testuj logowanie",
         "Ignore External 2FA on Local Subnet": "Zignoruj zewnętrzny 2FA w lokalnej podsieci",
-        "Large modal": "Large modal",
-        "An Error Occurred": "An Error Occurred",
-        "Type your message": "Type your message",
-        "Bookmark Tabs": "Bookmark Tabs",
-        "Bookmark Categories": "Bookmark Categories",
+        "Large modal": "Duży popup",
+        "An Error Occurred": "Wystąpił błąd",
+        "Type your message": "Wpisz swoją wiadomość",
+        "Bookmark Tabs": "Karty Zakładek",
+        "Bookmark Categories": "Kategorie Zakładek",
         "Open Collective": "Open Collective",
         "Github Sponsor": "Github Sponsor",
-        "Backers": "Backers",
-        "Tab Folder": "Tab Folder",
+        "Backers": "Wspierający",
+        "Tab Folder": "Folder Kart",
         "Cache Folder": "Cache Folder",
         "Backup": "Backup",
-        "Import Emby Users": "Import Emby Users",
-        "Import Jellyfin Users": "Import Jellyfin Users",
-        "More": "More",
-        "Less": "Less",
+        "Import Emby Users": "Importuj użytkowników Emby",
+        "Import Jellyfin Users": "Importuj użytkowników Jellyfin",
+        "More": "Więcej",
+        "Less": "Mniej",
         "OpenCollective Sponsor": "OpenCollective Sponsor",
         "Patreon Sponsor": "Patreon Sponsor",
         "New Organizr API v2": "New Organizr API v2",
         "Develop Branch Users - Please switch to Master for mean time": "Develop Branch Users - Please switch to Master for mean time",
-        "API V2 TESTING almost complete": "API V2 TESTING almost complete",
+        "API V2 TESTING almost complete": "API V2 TESTING prawie zakończone",
         "Important Messages - Each message can now be ignored using ignore button": "Important Messages - Each message can now be ignored using ignore button",
-        "Minimum PHP Version change": "Minimum PHP Version change",
-        "You": "You",
+        "Minimum PHP Version change": "Zmiana min. wersji PHP",
+        "You": "Ty",
         "Drop Certificate file here to upload": "Drop Certificate file here to upload",
         "Custom Certificate Loaded": "Custom Certificate Loaded",
         "By default, Organizr uses certificates from https://curl.se/docs/caextract.html": "By default, Organizr uses certificates from https://curl.se/docs/caextract.html",
         "If you would like to use your own certificate, please upload it below.  You will then need to enable each homepage item to use it.": "If you would like to use your own certificate, please upload it below.  You will then need to enable each homepage item to use it.",
         "i.e. X-Forwarded-Email": "i.e. X-Forwarded-Email",
         "Auth Proxy Header Name for Email": "Auth Proxy Header Name for Email",
-        "Custom Recover Password Text": "Custom Recover Password Text",
-        "Disable Recover Password": "Disable Recover Password",
+        "Custom Recover Password Text": "Niestandardowy tekst odzyskiwania hasła",
+        "Disable Recover Password": "Wyłącz odzyskiwanie hasła",
         "Blacklisted Error Message": "Blacklisted Error Message",
-        "Blacklisted IP's": "Blacklisted IP's",
-        "http(s)://domain": "http(s)://domain",
+        "Blacklisted IP's": "Blokowane adresy IP",
+        "http(s)://domain": "http(s)://domena",
         "Traefik Domain for Return Override": "Traefik Domain for Return Override",
-        "Jellyfin Token": "Jellyfin Token",
-        "Jellyfin URL": "Jellyfin URL",
-        "Enable LDAP TLS": "Enable LDAP TLS",
-        "Enable LDAP SSL": "Enable LDAP SSL",
+        "Jellyfin Token": "Token Jellyfin",
+        "Jellyfin URL": "URL Jellyfin",
+        "Enable LDAP TLS": "Włącz LDAP TLS",
+        "Enable LDAP SSL": "Włącz LDAP SSL",
         "Bind Password": "Bind Password",
         "http(s) | ftp(s) | ldap(s)://hostname:port": "http(s) | ftp(s) | ldap(s)://hostname:port",
         "Plex Admin Username": "Plex Admin Username",
         "Default Settings Tab": "Default Settings Tab",
-        "Certificate": "Certificate",
+        "Certificate": "Certyfikat",
         "Ping": "Ping",
         "API": "API",
         "Github": "Github",
-        "Settings Page": "Settings Page",
+        "Settings Page": "Ustawienia",
         "http(s)://domain.com": "http(s)://domain.com",
         "Jellyfin SSO URL": "Jellyfin SSO URL",
         "Jellyfin API URL": "Jellyfin API URL",
@@ -675,36 +675,36 @@
         "Overseerr URL": "Overseerr URL",
         "Multiple URL's": "Multiple URL's",
         "Using multiple SSO application will cause your Cookie Header item to increase.  If you haven't increased it by now, please follow this guide": "Using multiple SSO application will cause your Cookie Header item to increase.  If you haven't increased it by now, please follow this guide",
-        "Please Read First": "Please Read First",
+        "Please Read First": "Przeczytaj najpierw",
         "Jellyfin": "Jellyfin",
         "Petio": "Petio",
         "Overseerr": "Overseerr",
         "FYI": "FYI",
         "https://app.plex.tv/auth#?resetPassword": "https://app.plex.tv/auth#?resetPassword",
-        "Change Password on Plex Website": "Change Password on Plex Website",
-        "Action": "Action",
+        "Change Password on Plex Website": "Zmień hasło w Plex Website",
+        "Action": "Akcja",
         "IP": "IP",
-        "Browser": "Browser",
-        "Expires": "Expires",
-        "Created": "Created",
-        "Version": "Version",
-        "Files": "Files",
-        "Backup Organizr": "Backup Organizr",
+        "Browser": "Przeglądarka",
+        "Expires": "Wygasa",
+        "Created": "Utworzony",
+        "Version": "Wersja",
+        "Files": "Pliki",
+        "Backup Organizr": "Kopia zapasowa Organizr",
         "Create Backup": "Create Backup",
         "Select or type Image": "Select or type Image",
         "Choose": "Choose",
-        "Choose Blackberry Theme Icon": "Choose Blackberry Theme Icon",
-        "Save Tab Order": "Save Tab Order",
+        "Choose Blackberry Theme Icon": "Wybierz ikonę motywu Blackberry",
+        "Save Tab Order": "Zapisz kolejność kart",
         "Drag Homepage Items to Order Them": "Drag Homepage Items to Order Them",
-        "Preview": "Preview",
-        "Text Color": "Text Color",
-        "Background Color": "Background Color",
-        "Bookmark Tab Editor": "Bookmark Tab Editor",
-        "Add New Bookmark Category": "Add New Bookmark Category",
-        "Bookmark Category Editor": "Bookmark Category Editor",
-        "Auto-Expand Nav Bar": "Auto-Expand Nav Bar",
-        "Auto-Collapse Categories": "Auto-Collapse Categories",
-        "Expand All Categories": "Expand All Categories",
+        "Preview": "Podgląd",
+        "Text Color": "Kolor tekstu",
+        "Background Color": "Kolor tła",
+        "Bookmark Tab Editor": "Edytor Kart Zakładek",
+        "Add New Bookmark Category": "Dodaj Nową Kategorię Zakładek",
+        "Bookmark Category Editor": "Edytuj Kategorię Zakładek",
+        "Auto-Expand Nav Bar": "Automatycznie rozwijaj Pasek nawigacyjny",
+        "Auto-Collapse Categories": "Automatyczne zwijanie Kategorii",
+        "Expand All Categories": "Rozwiń wszystkie Kategorie",
         "Show Organizr Sign out & in Button on Sidebar": "Show Organizr Sign out & in Button on Sidebar",
         "Show Organizr Docs Link": "Show Organizr Docs Link",
         "Show Organizr Support Link": "Show Organizr Support Link",
@@ -713,13 +713,147 @@
         "Theme CSS": "Theme CSS",
         "Custom CSS": "Custom CSS",
         "FavIcon": "FavIcon",
-        "Notifications": "Notifications",
+        "Notifications": "Powiadomienia",
         "Colors & Themes": "Colors & Themes",
         "Options": "Options",
-        "Login Page": "Login Page",
+        "Login Page": "Strona logowania",
         "Top Bar": "Top Bar",
         "Bookmark Settings": "Bookmark Settings",
         "HnL Settings": "HnL Settings",
-        "Not Installed": "Not Installed"
+        "Not Installed": "Not Installed",
+        "Money not an option?  No problem.  Show some love to this Google Ad below:": "Money not an option?  No problem.  Show some love to this Google Ad below:",
+        "Please click the button to continue.": "Naciśnij przycisk aby kontynuować",
+        "Need specialized support or just want to support Organizr?  If so head to Open Collective...": "Need specialized support or just want to support Organizr?  If so head to Open Collective...",
+        "Need specialized support or just want to support Organizr?  If so head to Patreon...": "Need specialized support or just want to support Organizr?  If so head to Patreon...",
+        "Want to donate a small amount of Crypto?.": "Want to donate a small amount of Crypto?.",
+        "Please use the QR Code or Wallet ID.": "Please use the QR Code or Wallet ID.",
+        "If you use the Square Cash App, you can donate with that if you like.": "If you use the Square Cash App, you can donate with that if you like.",
+        "I have chosen to go with PayPal Pools so everyone can see how much people have donated.": "I have chosen to go with PayPal Pools so everyone can see how much people have donated.",
+        "Want to show support on Github?  Sponsor me :)": "Want to show support on Github?  Sponsor me :)",
+        "If messages get stuck sending, please turn this option off.": "If messages get stuck sending, please turn this option off.",
+        "Save and reload!": "Save and reload!",
+        "Copy and paste the 4 values into Organizr": "Copy and paste the 4 values into Organizr",
+        "Click the overview tab on top left": "Click the overview tab on top left",
+        "Frontend (JQuery) - Backend (PHP)": "Frontend (JQuery) - Backend (PHP)",
+        "Create an App called whatever you like and choose a cluster (Close to you)": "Create an App called whatever you like and choose a cluster (Close to you)",
+        "Signup for Pusher [FREE]": "Signup for Pusher [FREE]",
+        "Connection": "Connection",
+        "Enabled": "Enabled",
+        "Internal URL": "Internal URL",
+        "External URL": "External URL",
+        "UUID": "UUID",
+        "Service Name": "Service Name",
+        "Make sure to save before using the import button on Services tab": "Make sure to save before using the import button on Services tab",
+        "Do not use a Read-Only Token as that will not give a correct UUID for sending the results to HealthChecks.io": "Do not use a Read-Only Token as that will not give a correct UUID for sending the results to HealthChecks.io",
+        "Please use a Full Access Token": "Please use a Full Access Token",
+        "URL for HealthChecks API": "URL for HealthChecks API",
+        "403 Error as Success": "403 Error as Success",
+        "401 Error as Success": "401 Error as Success",
+        "HealthChecks Ping URL": "HealthChecks Ping URL",
+        "URL for HealthChecks Ping": "URL for HealthChecks Ping",
+        "As often as you like - i.e. every 1 minute": "As often as you like - i.e. every 1 minute",
+        "Frequency": "Frequency",
+        "CRON Job URL": "CRON Job URL",
+        "Once this plugin is setup, you will need to setup a CRON job": "Once this plugin is setup, you will need to setup a CRON job",
+        "Services": "Services",
+        "Import Services": "Import Services",
+        "Add New Service": "Add New Service",
+        "After enabling for the first time, please reload the page - Menu is located under User menu on top right": "After enabling for the first time, please reload the page - Menu is located under User menu on top right",
+        "Emby Settings": "Emby Settings",
+        "Plex Settings": "Plex Settings",
+        "Backend": "Backend",
+        "Templates": "Templates",
+        "Test & Options": "Test & Options",
+        "Sender Information": "Sender Information",
+        "Host": "Host",
+        "Open your custom Bookmark page via menu.": "Open your custom Bookmark page via menu.",
+        "Create Bookmark tabs in the new area in": "Create Bookmark tabs in the new area in",
+        "Create Bookmark categories in the new area in": "Create Bookmark categories in the new area in",
+        "Add tab that points to": "Add tab that points to",
+        "and set it's type to": "and set it's type to",
+        "Checking for bookmark default category...": "Checking for bookmark default category...",
+        "Checking for Bookmark tab...": "Checking for Bookmark tab...",
+        "Automatic Setup Tasks": "Automatic Setup Tasks",
+        "Located at": "Located at",
+        "Custom Certificate Status": "Custom Certificate Status",
+        "Will play a sound if the server goes down and will play sound if comes back up.": "Will play a sound if the server goes down and will play sound if comes back up.",
+        "Please choose a unique value for added security": "Please choose a unique value for added security",
+        "IPv4 only at the moment - This must be set to work, will accept subnet or IP address": "IPv4 only at the moment - This must be set to work, will accept subnet or IP address",
+        "Enable option to set Auth Proxy Header Login": "Enable option to set Auth Proxy Header Login",
+        "Text or HTML for recovery password section": "Text or HTML for recovery password section",
+        "Disables recover password area": "Disables recover password area",
+        "Enables the local address forward if on local address and accessed from WAN Domain": "Enables the local address forward if on local address and accessed from WAN Domain",
+        "Full local address of organizr install - i.e. http://home.local or http://192.168.0.100": "Full local address of organizr install - i.e. http://home.local or http://192.168.0.100",
+        "Enter domain if you wish to be forwarded to a local address - Local Address filled out on next item": "Enter domain if you wish to be forwarded to a local address - Local Address filled out on next item",
+        "IPv4 only at the moment - This will set your login as local if your IP falls within the From and To": "IPv4 only at the moment - This will set your login as local if your IP falls within the From and To",
+        "Default status of Remember Me button on login screen": "Default status of Remember Me button on login screen",
+        "Number of days cookies and tokens will be valid for": "Number of days cookies and tokens will be valid for",
+        "Enable this to hide the Registration button on the login screen": "Enable this to hide the Registration button on the login screen",
+        "Sets the password for the Registration form on the login screen": "Sets the password for the Registration form on the login screen",
+        "WARNING! This will block anyone with these IP's": "WARNING! This will block anyone with these IP's",
+        "WARNING! This can potentially mess up your iFrames": "WARNING! This can potentially mess up your iFrames",
+        "Please use a FQDN on this URL Override": "Please use a FQDN on this URL Override",
+        "This will enable the webserver to forward errors so traefik will accept them": "This will enable the webserver to forward errors so traefik will accept them",
+        "Please make sure to use local IP address and port - You also may use local dns name too.": "Please make sure to use local IP address and port - You also may use local dns name too.",
+        "Remember! Please save before using the test button!": "Remember! Please save before using the test button!",
+        "This will enable the use of TLS for LDAP connections": "This will enable the use of TLS for LDAP connections",
+        "This will enable the use of SSL for LDAP connections": "This will enable the use of SSL for LDAP connections",
+        "Enabling this will bypass external 2FA security if user is on local Subnet": "Enabling this will bypass external 2FA security if user is on local Subnet",
+        "Enabling this will only allow Friends that have shares to the Machine ID entered above to login, Having this disabled will allow all Friends on your Friends list to login": "Enabling this will only allow Friends that have shares to the Machine ID entered above to login, Having this disabled will allow all Friends on your Friends list to login",
+        "Since you are using the official Docker image, you can just restart your Docker container to update Organizr": "Since you are using the official Docker image, you can just restart your Docker container to update Organizr",
+        "Since you are using the Official Docker image, Change the image to change the branch": "Since you are using the Official Docker image, Change the image to change the branch",
+        "Choose which Settings Tab to be default when opening settings page": "Choose which Settings Tab to be default when opening settings page",
+        "Please make sure to use the same (sub)domain to access Jellyfin as Organizr's": "Please make sure to use the same (sub)domain to access Jellyfin as Organizr's",
+        "Please make sure to use the local address to the API": "Please make sure to use the local address to the API",
+        "DO NOT SET THIS TO YOUR ADMIN ACCOUNT. We recommend you create a local account as a \"catch all\" for when Organizr is unable to perform SSO.  Organizr will request a User Token based off of this user credentials": "DO NOT SET THIS TO YOUR ADMIN ACCOUNT. We recommend you create a local account as a \"catch all\" for when Organizr is unable to perform SSO.  Organizr will request a User Token based off of this user credentials",
+        "Purge Log": "Purge Log",
+        "Avatar": "Avatar",
+        "Date Registered": "Date Registered",
+        "Group": "Group",
+        "Locked": "Locked",
+        "Copy to Clipboard": "Copy to Clipboard",
+        "Choose action:": "Choose action:",
+        "You may enter multiple URL's using the CSV format.  i.e. link#1,link#2,link#3": "You may enter multiple URL's using the CSV format.  i.e. link#1,link#2,link#3",
+        "Used to set the description for SEO meta tags": "Used to set the description for SEO meta tags",
+        "Also sets the title of your site": "Also sets the title of your site",
+        "Up to date": "Up to date",
+        "Loading Pihole...": "Loading Pihole...",
+        "Loading Unifi...": "Loading Unifi...",
+        "Loading Weather...": "Loading Weather...",
+        "Loading Tautulli...": "Loading Tautulli...",
+        "Loading Health Checks...": "Loading Health Checks...",
+        "Health Checks": "Health Checks",
+        "UniFi": "UniFi",
+        "Connection Error to rTorrent": "Connection Error to rTorrent",
+        "Request a Show or Movie": "Request a Show or Movie",
+        "Set": "Set",
+        "Set WAL Mode": "Set WAL Mode",
+        "Set DELETE Mode (Default)": "Set DELETE Mode (Default)",
+        "Journal Mode Status": "Journal Mode Status",
+        "This feature is experimental - You may face unexpected database is locked errors in logs": "This feature is experimental - You may face unexpected database is locked errors in logs",
+        "Warning": "Warning",
+        "Tab Help": "Tab Help",
+        "Toggle this tab to loaded in the background on page load": "Toggle this tab to loaded in the background on page load",
+        "Preload": "Preload",
+        "Enable Organizr to ping the status of the local URL of this tab": "Enable Organizr to ping the status of the local URL of this tab",
+        "Toggle this to add the tab to the Splash Page on page load": "Toggle this to add the tab to the Splash Page on page load",
+        "Splash": "Splash",
+        "Either mark a tab as active or inactive": "Either mark a tab as active or inactive",
+        "You can choose one tab to be the first opened tab on page load": "You can choose one tab to be the first opened tab on page load",
+        "Default": "Default",
+        "Internal is for Organizr pages": "Internal is for Organizr pages",
+        "iFrame is for all others": "iFrame is for all others",
+        "New Window is for items to open in a new window": "New Window is for items to open in a new window",
+        "The lowest Group that will have access to this tab": "The lowest Group that will have access to this tab",
+        "Each Tab is assigned a Category, the default is unsorted.  You may create new categories on the Category settings tab": "Each Tab is assigned a Category, the default is unsorted.  You may create new categories on the Category settings tab",
+        "Category": "Category",
+        "The text that will be displayed for that certain tab": "The text that will be displayed for that certain tab",
+        "Please Save before Testing. Note that using a blank password might not work correctly.": "Please Save before Testing. Note that using a blank password might not work correctly.",
+        "Use Custom Certificate": "Use Custom Certificate",
+        "Note that using a blank password might not work correctly.": "Note that using a blank password might not work correctly.",
+        "Database Password": "Database Password",
+        "Database Username": "Database Username",
+        "Database Host": "Host bazy danych",
+        ".": "."
     }
 }

+ 147 - 13
js/langpack/ru[Russian].json

@@ -536,15 +536,15 @@
         "Enable Pollen": "Enable Pollen",
         "Enable Air Quality": "Enable Air Quality",
         "Enable Weather": "Enable Weather",
-        "Need Help With Coordinates?": "Need Help With Coordinates?",
-        "Longitude": "Longitude",
-        "Latitude": "Latitude",
-        "Weather-Air": "Weather-Air",
-        "Compact view": "Compact view",
+        "Need Help With Coordinates?": "Помочь с координатами?",
+        "Longitude": "Долгота",
+        "Latitude": "Широта",
+        "Weather-Air": "Погода-Воздух",
+        "Compact view": "Простой вид",
         "http://domain.com/monitorr/": "http://domain.com/monitorr/",
         "Monitorr": "Monitorr",
         "Top Platforms": "Top Platforms",
-        "Top Users": "Top Users",
+        "Top Users": "Топ пользователей",
         "http://<ip>:<port>": "http://<ip>:<port>",
         "Tautulli": "Tautulli",
         "Combine stat cards": "Combine stat cards",
@@ -559,16 +559,16 @@
         "Grab Unifi Site": "Grab Unifi Site",
         "Site Name": "Site Name",
         "Unifi API URL": "Unifi API URL",
-        "Show Denied": "Show Denied",
-        "Show Unapproved": "Show Unapproved",
-        "Show Approved": "Show Approved",
+        "Show Denied": "Показать Отклонённые",
+        "Show Unapproved": "Показать Неподтвержденные",
+        "Show Approved": "Показать Подтвержденные",
         "Show Unavailable": "Show Unavailable",
-        "Show Available": "Show Available",
+        "Show Available": "Показать Доступные",
         "Disable Certificate Check": "Disable Certificate Check",
         "Organizr appends the url with": "Organizr appends the url with",
         "unless the URL ends in": "unless the URL ends in",
-        "ATTENTION": "ATTENTION",
-        "API Version": "API Version",
+        "ATTENTION": "ВНИМАНИЕ",
+        "API Version": "Версия API",
         "JDownloader": "JDownloader",
         "http(s)://hostname:port - make sure if Jelly fin to end url with /jellyfin": "http(s)://hostname:port - make sure if Jelly fin to end url with /jellyfin",
         "Emby-Jellyfin": "Emby-Jellyfin",
@@ -720,6 +720,140 @@
         "Top Bar": "Top Bar",
         "Bookmark Settings": "Bookmark Settings",
         "HnL Settings": "HnL Settings",
-        "Not Installed": "Not Installed"
+        "Not Installed": "Not Installed",
+        "Money not an option?  No problem.  Show some love to this Google Ad below:": "Money not an option?  No problem.  Show some love to this Google Ad below:",
+        "Please click the button to continue.": "Please click the button to continue.",
+        "Need specialized support or just want to support Organizr?  If so head to Open Collective...": "Need specialized support or just want to support Organizr?  If so head to Open Collective...",
+        "Need specialized support or just want to support Organizr?  If so head to Patreon...": "Need specialized support or just want to support Organizr?  If so head to Patreon...",
+        "Want to donate a small amount of Crypto?.": "Want to donate a small amount of Crypto?.",
+        "Please use the QR Code or Wallet ID.": "Please use the QR Code or Wallet ID.",
+        "If you use the Square Cash App, you can donate with that if you like.": "If you use the Square Cash App, you can donate with that if you like.",
+        "I have chosen to go with PayPal Pools so everyone can see how much people have donated.": "I have chosen to go with PayPal Pools so everyone can see how much people have donated.",
+        "Want to show support on Github?  Sponsor me :)": "Want to show support on Github?  Sponsor me :)",
+        "If messages get stuck sending, please turn this option off.": "If messages get stuck sending, please turn this option off.",
+        "Save and reload!": "Save and reload!",
+        "Copy and paste the 4 values into Organizr": "Copy and paste the 4 values into Organizr",
+        "Click the overview tab on top left": "Click the overview tab on top left",
+        "Frontend (JQuery) - Backend (PHP)": "Frontend (JQuery) - Backend (PHP)",
+        "Create an App called whatever you like and choose a cluster (Close to you)": "Create an App called whatever you like and choose a cluster (Close to you)",
+        "Signup for Pusher [FREE]": "Signup for Pusher [FREE]",
+        "Connection": "Connection",
+        "Enabled": "Enabled",
+        "Internal URL": "Internal URL",
+        "External URL": "External URL",
+        "UUID": "UUID",
+        "Service Name": "Service Name",
+        "Make sure to save before using the import button on Services tab": "Make sure to save before using the import button on Services tab",
+        "Do not use a Read-Only Token as that will not give a correct UUID for sending the results to HealthChecks.io": "Do not use a Read-Only Token as that will not give a correct UUID for sending the results to HealthChecks.io",
+        "Please use a Full Access Token": "Please use a Full Access Token",
+        "URL for HealthChecks API": "URL for HealthChecks API",
+        "403 Error as Success": "403 Error as Success",
+        "401 Error as Success": "401 Error as Success",
+        "HealthChecks Ping URL": "HealthChecks Ping URL",
+        "URL for HealthChecks Ping": "URL for HealthChecks Ping",
+        "As often as you like - i.e. every 1 minute": "As often as you like - i.e. every 1 minute",
+        "Frequency": "Frequency",
+        "CRON Job URL": "CRON Job URL",
+        "Once this plugin is setup, you will need to setup a CRON job": "Once this plugin is setup, you will need to setup a CRON job",
+        "Services": "Services",
+        "Import Services": "Import Services",
+        "Add New Service": "Add New Service",
+        "After enabling for the first time, please reload the page - Menu is located under User menu on top right": "After enabling for the first time, please reload the page - Menu is located under User menu on top right",
+        "Emby Settings": "Emby Settings",
+        "Plex Settings": "Plex Settings",
+        "Backend": "Backend",
+        "Templates": "Templates",
+        "Test & Options": "Test & Options",
+        "Sender Information": "Sender Information",
+        "Host": "Host",
+        "Open your custom Bookmark page via menu.": "Open your custom Bookmark page via menu.",
+        "Create Bookmark tabs in the new area in": "Create Bookmark tabs in the new area in",
+        "Create Bookmark categories in the new area in": "Create Bookmark categories in the new area in",
+        "Add tab that points to": "Add tab that points to",
+        "and set it's type to": "and set it's type to",
+        "Checking for bookmark default category...": "Checking for bookmark default category...",
+        "Checking for Bookmark tab...": "Checking for Bookmark tab...",
+        "Automatic Setup Tasks": "Automatic Setup Tasks",
+        "Located at": "Located at",
+        "Custom Certificate Status": "Custom Certificate Status",
+        "Will play a sound if the server goes down and will play sound if comes back up.": "Will play a sound if the server goes down and will play sound if comes back up.",
+        "Please choose a unique value for added security": "Please choose a unique value for added security",
+        "IPv4 only at the moment - This must be set to work, will accept subnet or IP address": "IPv4 only at the moment - This must be set to work, will accept subnet or IP address",
+        "Enable option to set Auth Proxy Header Login": "Enable option to set Auth Proxy Header Login",
+        "Text or HTML for recovery password section": "Text or HTML for recovery password section",
+        "Disables recover password area": "Disables recover password area",
+        "Enables the local address forward if on local address and accessed from WAN Domain": "Enables the local address forward if on local address and accessed from WAN Domain",
+        "Full local address of organizr install - i.e. http://home.local or http://192.168.0.100": "Full local address of organizr install - i.e. http://home.local or http://192.168.0.100",
+        "Enter domain if you wish to be forwarded to a local address - Local Address filled out on next item": "Enter domain if you wish to be forwarded to a local address - Local Address filled out on next item",
+        "IPv4 only at the moment - This will set your login as local if your IP falls within the From and To": "IPv4 only at the moment - This will set your login as local if your IP falls within the From and To",
+        "Default status of Remember Me button on login screen": "Default status of Remember Me button on login screen",
+        "Number of days cookies and tokens will be valid for": "Number of days cookies and tokens will be valid for",
+        "Enable this to hide the Registration button on the login screen": "Enable this to hide the Registration button on the login screen",
+        "Sets the password for the Registration form on the login screen": "Sets the password for the Registration form on the login screen",
+        "WARNING! This will block anyone with these IP's": "WARNING! This will block anyone with these IP's",
+        "WARNING! This can potentially mess up your iFrames": "WARNING! This can potentially mess up your iFrames",
+        "Please use a FQDN on this URL Override": "Please use a FQDN on this URL Override",
+        "This will enable the webserver to forward errors so traefik will accept them": "This will enable the webserver to forward errors so traefik will accept them",
+        "Please make sure to use local IP address and port - You also may use local dns name too.": "Please make sure to use local IP address and port - You also may use local dns name too.",
+        "Remember! Please save before using the test button!": "Remember! Please save before using the test button!",
+        "This will enable the use of TLS for LDAP connections": "This will enable the use of TLS for LDAP connections",
+        "This will enable the use of SSL for LDAP connections": "This will enable the use of SSL for LDAP connections",
+        "Enabling this will bypass external 2FA security if user is on local Subnet": "Enabling this will bypass external 2FA security if user is on local Subnet",
+        "Enabling this will only allow Friends that have shares to the Machine ID entered above to login, Having this disabled will allow all Friends on your Friends list to login": "Enabling this will only allow Friends that have shares to the Machine ID entered above to login, Having this disabled will allow all Friends on your Friends list to login",
+        "Since you are using the official Docker image, you can just restart your Docker container to update Organizr": "Since you are using the official Docker image, you can just restart your Docker container to update Organizr",
+        "Since you are using the Official Docker image, Change the image to change the branch": "Since you are using the Official Docker image, Change the image to change the branch",
+        "Choose which Settings Tab to be default when opening settings page": "Choose which Settings Tab to be default when opening settings page",
+        "Please make sure to use the same (sub)domain to access Jellyfin as Organizr's": "Please make sure to use the same (sub)domain to access Jellyfin as Organizr's",
+        "Please make sure to use the local address to the API": "Please make sure to use the local address to the API",
+        "DO NOT SET THIS TO YOUR ADMIN ACCOUNT. We recommend you create a local account as a \"catch all\" for when Organizr is unable to perform SSO.  Organizr will request a User Token based off of this user credentials": "DO NOT SET THIS TO YOUR ADMIN ACCOUNT. We recommend you create a local account as a \"catch all\" for when Organizr is unable to perform SSO.  Organizr will request a User Token based off of this user credentials",
+        "Purge Log": "Purge Log",
+        "Avatar": "Avatar",
+        "Date Registered": "Date Registered",
+        "Group": "Group",
+        "Locked": "Locked",
+        "Copy to Clipboard": "Copy to Clipboard",
+        "Choose action:": "Choose action:",
+        "You may enter multiple URL's using the CSV format.  i.e. link#1,link#2,link#3": "You may enter multiple URL's using the CSV format.  i.e. link#1,link#2,link#3",
+        "Used to set the description for SEO meta tags": "Used to set the description for SEO meta tags",
+        "Also sets the title of your site": "Also sets the title of your site",
+        "Up to date": "Up to date",
+        "Loading Pihole...": "Loading Pihole...",
+        "Loading Unifi...": "Loading Unifi...",
+        "Loading Weather...": "Loading Weather...",
+        "Loading Tautulli...": "Loading Tautulli...",
+        "Loading Health Checks...": "Loading Health Checks...",
+        "Health Checks": "Health Checks",
+        "UniFi": "UniFi",
+        "Connection Error to rTorrent": "Connection Error to rTorrent",
+        "Request a Show or Movie": "Request a Show or Movie",
+        "Set": "Set",
+        "Set WAL Mode": "Set WAL Mode",
+        "Set DELETE Mode (Default)": "Set DELETE Mode (Default)",
+        "Journal Mode Status": "Journal Mode Status",
+        "This feature is experimental - You may face unexpected database is locked errors in logs": "This feature is experimental - You may face unexpected database is locked errors in logs",
+        "Warning": "Warning",
+        "Tab Help": "Tab Help",
+        "Toggle this tab to loaded in the background on page load": "Toggle this tab to loaded in the background on page load",
+        "Preload": "Preload",
+        "Enable Organizr to ping the status of the local URL of this tab": "Enable Organizr to ping the status of the local URL of this tab",
+        "Toggle this to add the tab to the Splash Page on page load": "Toggle this to add the tab to the Splash Page on page load",
+        "Splash": "Splash",
+        "Either mark a tab as active or inactive": "Either mark a tab as active or inactive",
+        "You can choose one tab to be the first opened tab on page load": "You can choose one tab to be the first opened tab on page load",
+        "Default": "Default",
+        "Internal is for Organizr pages": "Internal is for Organizr pages",
+        "iFrame is for all others": "iFrame is for all others",
+        "New Window is for items to open in a new window": "New Window is for items to open in a new window",
+        "The lowest Group that will have access to this tab": "The lowest Group that will have access to this tab",
+        "Each Tab is assigned a Category, the default is unsorted.  You may create new categories on the Category settings tab": "Each Tab is assigned a Category, the default is unsorted.  You may create new categories on the Category settings tab",
+        "Category": "Category",
+        "The text that will be displayed for that certain tab": "The text that will be displayed for that certain tab",
+        "Please Save before Testing. Note that using a blank password might not work correctly.": "Please Save before Testing. Note that using a blank password might not work correctly.",
+        "Use Custom Certificate": "Use Custom Certificate",
+        "Note that using a blank password might not work correctly.": "Note that using a blank password might not work correctly.",
+        "Database Password": "Database Password",
+        "Database Username": "Database Username",
+        "Database Host": "Database Host",
+        ".": "."
     }
 }

Plik diff jest za duży
+ 0 - 0
js/version.json


+ 513 - 0
plugins/bower_components/ace/ext-searchbox.js

@@ -0,0 +1,513 @@
+define("ace/ext/searchbox",["require","exports","module","ace/lib/dom","ace/lib/lang","ace/lib/event","ace/keyboard/hash_handler","ace/lib/keys"], function(require, exports, module) {
+    "use strict";
+
+    var dom = require("../lib/dom");
+    var lang = require("../lib/lang");
+    var event = require("../lib/event");
+    var searchboxCss = "\
+.ace_search {\
+background-color: #ddd;\
+color: #666;\
+border: 1px solid #cbcbcb;\
+border-top: 0 none;\
+overflow: hidden;\
+margin: 0;\
+padding: 4px 6px 0 4px;\
+position: absolute;\
+top: 0;\
+z-index: 99;\
+white-space: normal;\
+}\
+.ace_search.left {\
+border-left: 0 none;\
+border-radius: 0px 0px 5px 0px;\
+left: 0;\
+}\
+.ace_search.right {\
+border-radius: 0px 0px 0px 5px;\
+border-right: 0 none;\
+right: 0;\
+}\
+.ace_search_form, .ace_replace_form {\
+margin: 0 20px 4px 0;\
+overflow: hidden;\
+line-height: 1.9;\
+}\
+.ace_replace_form {\
+margin-right: 0;\
+}\
+.ace_search_form.ace_nomatch {\
+outline: 1px solid red;\
+}\
+.ace_search_field {\
+border-radius: 3px 0 0 3px;\
+background-color: white;\
+color: black;\
+border: 1px solid #cbcbcb;\
+border-right: 0 none;\
+outline: 0;\
+padding: 0;\
+font-size: inherit;\
+margin: 0;\
+line-height: inherit;\
+padding: 0 6px;\
+min-width: 17em;\
+vertical-align: top;\
+min-height: 1.8em;\
+box-sizing: content-box;\
+}\
+.ace_searchbtn {\
+border: 1px solid #cbcbcb;\
+line-height: inherit;\
+display: inline-block;\
+padding: 0 6px;\
+background: #fff;\
+border-right: 0 none;\
+border-left: 1px solid #dcdcdc;\
+cursor: pointer;\
+margin: 0;\
+position: relative;\
+color: #666;\
+}\
+.ace_searchbtn:last-child {\
+border-radius: 0 3px 3px 0;\
+border-right: 1px solid #cbcbcb;\
+}\
+.ace_searchbtn:disabled {\
+background: none;\
+cursor: default;\
+}\
+.ace_searchbtn:hover {\
+background-color: #eef1f6;\
+}\
+.ace_searchbtn.prev, .ace_searchbtn.next {\
+padding: 0px 0.7em\
+}\
+.ace_searchbtn.prev:after, .ace_searchbtn.next:after {\
+content: \"\";\
+border: solid 2px #888;\
+width: 0.5em;\
+height: 0.5em;\
+border-width:  2px 0 0 2px;\
+display:inline-block;\
+transform: rotate(-45deg);\
+}\
+.ace_searchbtn.next:after {\
+border-width: 0 2px 2px 0 ;\
+}\
+.ace_searchbtn_close {\
+background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAcCAYAAABRVo5BAAAAZ0lEQVR42u2SUQrAMAhDvazn8OjZBilCkYVVxiis8H4CT0VrAJb4WHT3C5xU2a2IQZXJjiQIRMdkEoJ5Q2yMqpfDIo+XY4k6h+YXOyKqTIj5REaxloNAd0xiKmAtsTHqW8sR2W5f7gCu5nWFUpVjZwAAAABJRU5ErkJggg==) no-repeat 50% 0;\
+border-radius: 50%;\
+border: 0 none;\
+color: #656565;\
+cursor: pointer;\
+font: 16px/16px Arial;\
+padding: 0;\
+height: 14px;\
+width: 14px;\
+top: 9px;\
+right: 7px;\
+position: absolute;\
+}\
+.ace_searchbtn_close:hover {\
+background-color: #656565;\
+background-position: 50% 100%;\
+color: white;\
+}\
+.ace_button {\
+margin-left: 2px;\
+cursor: pointer;\
+-webkit-user-select: none;\
+-moz-user-select: none;\
+-o-user-select: none;\
+-ms-user-select: none;\
+user-select: none;\
+overflow: hidden;\
+opacity: 0.7;\
+border: 1px solid rgba(100,100,100,0.23);\
+padding: 1px;\
+box-sizing:    border-box!important;\
+color: black;\
+}\
+.ace_button:hover {\
+background-color: #eee;\
+opacity:1;\
+}\
+.ace_button:active {\
+background-color: #ddd;\
+}\
+.ace_button.checked {\
+border-color: #3399ff;\
+opacity:1;\
+}\
+.ace_search_options{\
+margin-bottom: 3px;\
+text-align: right;\
+-webkit-user-select: none;\
+-moz-user-select: none;\
+-o-user-select: none;\
+-ms-user-select: none;\
+user-select: none;\
+clear: both;\
+}\
+.ace_search_counter {\
+float: left;\
+font-family: arial;\
+padding: 0 8px;\
+}";
+    var HashHandler = require("../keyboard/hash_handler").HashHandler;
+    var keyUtil = require("../lib/keys");
+
+    var MAX_COUNT = 999;
+
+    dom.importCssString(searchboxCss, "ace_searchbox", false);
+
+    var SearchBox = function(editor, range, showReplaceForm) {
+        var div = dom.createElement("div");
+        dom.buildDom(["div", {class:"ace_search right"},
+            ["span", {action: "hide", class: "ace_searchbtn_close"}],
+            ["div", {class: "ace_search_form"},
+                ["input", {class: "ace_search_field", placeholder: "Search for", spellcheck: "false"}],
+                ["span", {action: "findPrev", class: "ace_searchbtn prev"}, "\u200b"],
+                ["span", {action: "findNext", class: "ace_searchbtn next"}, "\u200b"],
+                ["span", {action: "findAll", class: "ace_searchbtn", title: "Alt-Enter"}, "All"]
+            ],
+            ["div", {class: "ace_replace_form"},
+                ["input", {class: "ace_search_field", placeholder: "Replace with", spellcheck: "false"}],
+                ["span", {action: "replaceAndFindNext", class: "ace_searchbtn"}, "Replace"],
+                ["span", {action: "replaceAll", class: "ace_searchbtn"}, "All"]
+            ],
+            ["div", {class: "ace_search_options"},
+                ["span", {action: "toggleReplace", class: "ace_button", title: "Toggle Replace mode",
+                    style: "float:left;margin-top:-2px;padding:0 5px;"}, "+"],
+                ["span", {class: "ace_search_counter"}],
+                ["span", {action: "toggleRegexpMode", class: "ace_button", title: "RegExp Search"}, ".*"],
+                ["span", {action: "toggleCaseSensitive", class: "ace_button", title: "CaseSensitive Search"}, "Aa"],
+                ["span", {action: "toggleWholeWords", class: "ace_button", title: "Whole Word Search"}, "\\b"],
+                ["span", {action: "searchInSelection", class: "ace_button", title: "Search In Selection"}, "S"]
+            ]
+        ], div);
+        this.element = div.firstChild;
+
+        this.setSession = this.setSession.bind(this);
+
+        this.$init();
+        this.setEditor(editor);
+        dom.importCssString(searchboxCss, "ace_searchbox", editor.container);
+    };
+
+    (function() {
+        this.setEditor = function(editor) {
+            editor.searchBox = this;
+            editor.renderer.scroller.appendChild(this.element);
+            this.editor = editor;
+        };
+
+        this.setSession = function(e) {
+            this.searchRange = null;
+            this.$syncOptions(true);
+        };
+
+        this.$initElements = function(sb) {
+            this.searchBox = sb.querySelector(".ace_search_form");
+            this.replaceBox = sb.querySelector(".ace_replace_form");
+            this.searchOption = sb.querySelector("[action=searchInSelection]");
+            this.replaceOption = sb.querySelector("[action=toggleReplace]");
+            this.regExpOption = sb.querySelector("[action=toggleRegexpMode]");
+            this.caseSensitiveOption = sb.querySelector("[action=toggleCaseSensitive]");
+            this.wholeWordOption = sb.querySelector("[action=toggleWholeWords]");
+            this.searchInput = this.searchBox.querySelector(".ace_search_field");
+            this.replaceInput = this.replaceBox.querySelector(".ace_search_field");
+            this.searchCounter = sb.querySelector(".ace_search_counter");
+        };
+
+        this.$init = function() {
+            var sb = this.element;
+
+            this.$initElements(sb);
+
+            var _this = this;
+            event.addListener(sb, "mousedown", function(e) {
+                setTimeout(function(){
+                    _this.activeInput.focus();
+                }, 0);
+                event.stopPropagation(e);
+            });
+            event.addListener(sb, "click", function(e) {
+                var t = e.target || e.srcElement;
+                var action = t.getAttribute("action");
+                if (action && _this[action])
+                    _this[action]();
+                else if (_this.$searchBarKb.commands[action])
+                    _this.$searchBarKb.commands[action].exec(_this);
+                event.stopPropagation(e);
+            });
+
+            event.addCommandKeyListener(sb, function(e, hashId, keyCode) {
+                var keyString = keyUtil.keyCodeToString(keyCode);
+                var command = _this.$searchBarKb.findKeyCommand(hashId, keyString);
+                if (command && command.exec) {
+                    command.exec(_this);
+                    event.stopEvent(e);
+                }
+            });
+
+            this.$onChange = lang.delayedCall(function() {
+                _this.find(false, false);
+            });
+
+            event.addListener(this.searchInput, "input", function() {
+                _this.$onChange.schedule(20);
+            });
+            event.addListener(this.searchInput, "focus", function() {
+                _this.activeInput = _this.searchInput;
+                _this.searchInput.value && _this.highlight();
+            });
+            event.addListener(this.replaceInput, "focus", function() {
+                _this.activeInput = _this.replaceInput;
+                _this.searchInput.value && _this.highlight();
+            });
+        };
+        this.$closeSearchBarKb = new HashHandler([{
+            bindKey: "Esc",
+            name: "closeSearchBar",
+            exec: function(editor) {
+                editor.searchBox.hide();
+            }
+        }]);
+        this.$searchBarKb = new HashHandler();
+        this.$searchBarKb.bindKeys({
+            "Ctrl-f|Command-f": function(sb) {
+                var isReplace = sb.isReplace = !sb.isReplace;
+                sb.replaceBox.style.display = isReplace ? "" : "none";
+                sb.replaceOption.checked = false;
+                sb.$syncOptions();
+                sb.searchInput.focus();
+            },
+            "Ctrl-H|Command-Option-F": function(sb) {
+                if (sb.editor.getReadOnly())
+                    return;
+                sb.replaceOption.checked = true;
+                sb.$syncOptions();
+                sb.replaceInput.focus();
+            },
+            "Ctrl-G|Command-G": function(sb) {
+                sb.findNext();
+            },
+            "Ctrl-Shift-G|Command-Shift-G": function(sb) {
+                sb.findPrev();
+            },
+            "esc": function(sb) {
+                setTimeout(function() { sb.hide();});
+            },
+            "Return": function(sb) {
+                if (sb.activeInput == sb.replaceInput)
+                    sb.replace();
+                sb.findNext();
+            },
+            "Shift-Return": function(sb) {
+                if (sb.activeInput == sb.replaceInput)
+                    sb.replace();
+                sb.findPrev();
+            },
+            "Alt-Return": function(sb) {
+                if (sb.activeInput == sb.replaceInput)
+                    sb.replaceAll();
+                sb.findAll();
+            },
+            "Tab": function(sb) {
+                (sb.activeInput == sb.replaceInput ? sb.searchInput : sb.replaceInput).focus();
+            }
+        });
+
+        this.$searchBarKb.addCommands([{
+            name: "toggleRegexpMode",
+            bindKey: {win: "Alt-R|Alt-/", mac: "Ctrl-Alt-R|Ctrl-Alt-/"},
+            exec: function(sb) {
+                sb.regExpOption.checked = !sb.regExpOption.checked;
+                sb.$syncOptions();
+            }
+        }, {
+            name: "toggleCaseSensitive",
+            bindKey: {win: "Alt-C|Alt-I", mac: "Ctrl-Alt-R|Ctrl-Alt-I"},
+            exec: function(sb) {
+                sb.caseSensitiveOption.checked = !sb.caseSensitiveOption.checked;
+                sb.$syncOptions();
+            }
+        }, {
+            name: "toggleWholeWords",
+            bindKey: {win: "Alt-B|Alt-W", mac: "Ctrl-Alt-B|Ctrl-Alt-W"},
+            exec: function(sb) {
+                sb.wholeWordOption.checked = !sb.wholeWordOption.checked;
+                sb.$syncOptions();
+            }
+        }, {
+            name: "toggleReplace",
+            exec: function(sb) {
+                sb.replaceOption.checked = !sb.replaceOption.checked;
+                sb.$syncOptions();
+            }
+        }, {
+            name: "searchInSelection",
+            exec: function(sb) {
+                sb.searchOption.checked = !sb.searchRange;
+                sb.setSearchRange(sb.searchOption.checked && sb.editor.getSelectionRange());
+                sb.$syncOptions();
+            }
+        }]);
+
+        this.setSearchRange = function(range) {
+            this.searchRange = range;
+            if (range) {
+                this.searchRangeMarker = this.editor.session.addMarker(range, "ace_active-line");
+            } else if (this.searchRangeMarker) {
+                this.editor.session.removeMarker(this.searchRangeMarker);
+                this.searchRangeMarker = null;
+            }
+        };
+
+        this.$syncOptions = function(preventScroll) {
+            dom.setCssClass(this.replaceOption, "checked", this.searchRange);
+            dom.setCssClass(this.searchOption, "checked", this.searchOption.checked);
+            this.replaceOption.textContent = this.replaceOption.checked ? "-" : "+";
+            dom.setCssClass(this.regExpOption, "checked", this.regExpOption.checked);
+            dom.setCssClass(this.wholeWordOption, "checked", this.wholeWordOption.checked);
+            dom.setCssClass(this.caseSensitiveOption, "checked", this.caseSensitiveOption.checked);
+            var readOnly = this.editor.getReadOnly();
+            this.replaceOption.style.display = readOnly ? "none" : "";
+            this.replaceBox.style.display = this.replaceOption.checked && !readOnly ? "" : "none";
+            this.find(false, false, preventScroll);
+        };
+
+        this.highlight = function(re) {
+            this.editor.session.highlight(re || this.editor.$search.$options.re);
+            this.editor.renderer.updateBackMarkers();
+        };
+        this.find = function(skipCurrent, backwards, preventScroll) {
+            var range = this.editor.find(this.searchInput.value, {
+                skipCurrent: skipCurrent,
+                backwards: backwards,
+                wrap: true,
+                regExp: this.regExpOption.checked,
+                caseSensitive: this.caseSensitiveOption.checked,
+                wholeWord: this.wholeWordOption.checked,
+                preventScroll: preventScroll,
+                range: this.searchRange
+            });
+            var noMatch = !range && this.searchInput.value;
+            dom.setCssClass(this.searchBox, "ace_nomatch", noMatch);
+            this.editor._emit("findSearchBox", { match: !noMatch });
+            this.highlight();
+            this.updateCounter();
+        };
+        this.updateCounter = function() {
+            var editor = this.editor;
+            var regex = editor.$search.$options.re;
+            var all = 0;
+            var before = 0;
+            if (regex) {
+                var value = this.searchRange
+                    ? editor.session.getTextRange(this.searchRange)
+                    : editor.getValue();
+
+                var offset = editor.session.doc.positionToIndex(editor.selection.anchor);
+                if (this.searchRange)
+                    offset -= editor.session.doc.positionToIndex(this.searchRange.start);
+
+                var last = regex.lastIndex = 0;
+                var m;
+                while ((m = regex.exec(value))) {
+                    all++;
+                    last = m.index;
+                    if (last <= offset)
+                        before++;
+                    if (all > MAX_COUNT)
+                        break;
+                    if (!m[0]) {
+                        regex.lastIndex = last += 1;
+                        if (last >= value.length)
+                            break;
+                    }
+                }
+            }
+            this.searchCounter.textContent = before + " of " + (all > MAX_COUNT ? MAX_COUNT + "+" : all);
+        };
+        this.findNext = function() {
+            this.find(true, false);
+        };
+        this.findPrev = function() {
+            this.find(true, true);
+        };
+        this.findAll = function(){
+            var range = this.editor.findAll(this.searchInput.value, {
+                regExp: this.regExpOption.checked,
+                caseSensitive: this.caseSensitiveOption.checked,
+                wholeWord: this.wholeWordOption.checked
+            });
+            var noMatch = !range && this.searchInput.value;
+            dom.setCssClass(this.searchBox, "ace_nomatch", noMatch);
+            this.editor._emit("findSearchBox", { match: !noMatch });
+            this.highlight();
+            this.hide();
+        };
+        this.replace = function() {
+            if (!this.editor.getReadOnly())
+                this.editor.replace(this.replaceInput.value);
+        };
+        this.replaceAndFindNext = function() {
+            if (!this.editor.getReadOnly()) {
+                this.editor.replace(this.replaceInput.value);
+                this.findNext();
+            }
+        };
+        this.replaceAll = function() {
+            if (!this.editor.getReadOnly())
+                this.editor.replaceAll(this.replaceInput.value);
+        };
+
+        this.hide = function() {
+            this.active = false;
+            this.setSearchRange(null);
+            this.editor.off("changeSession", this.setSession);
+
+            this.element.style.display = "none";
+            this.editor.keyBinding.removeKeyboardHandler(this.$closeSearchBarKb);
+            this.editor.focus();
+        };
+        this.show = function(value, isReplace) {
+            this.active = true;
+            this.editor.on("changeSession", this.setSession);
+            this.element.style.display = "";
+            this.replaceOption.checked = isReplace;
+
+            if (value)
+                this.searchInput.value = value;
+
+            this.searchInput.focus();
+            this.searchInput.select();
+
+            this.editor.keyBinding.addKeyboardHandler(this.$closeSearchBarKb);
+
+            this.$syncOptions(true);
+        };
+
+        this.isFocused = function() {
+            var el = document.activeElement;
+            return el == this.searchInput || el == this.replaceInput;
+        };
+    }).call(SearchBox.prototype);
+
+    exports.SearchBox = SearchBox;
+
+    exports.Search = function(editor, isReplace) {
+        var sb = editor.searchBox || new SearchBox(editor);
+        sb.show(editor.session.getTextRange(), isReplace);
+    };
+
+});                (function() {
+    window.require(["ace/ext/searchbox"], function(m) {
+        if (typeof module == "object" && typeof exports == "object" && module) {
+            module.exports = m;
+        }
+    });
+})();

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików