Ver Fonte

Merge pull request #1752 from causefx/v2-develop

V2 develop
causefx há 4 anos atrás
pai
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
 - Custom tabs for your services
 - Customise the top bar by adding your own site logo or site name
 - Customise the top bar by adding your own site logo or site name
 - Enable or disable iFrame for your tabs
 - 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
 - Fullscreen Support
 - Gravatar Support
 - Gravatar Support
 - Keyboard shortcut support (Check help tab in settings)
 - Keyboard shortcut support (Check help tab in settings)
 - Login with Plex/Emby/LDAP or sFTP credentials
 - Login with Plex/Emby/LDAP or sFTP credentials
 - Mobile support
 - Mobile support
 - Multiple login 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
 - Organizr login log viewer
 - Personalise any theme: Customise the look and feel of Organizr with access to the colour palette
 - Personalise any theme: Customise the look and feel of Organizr with access to the colour palette
 - Pin/Unpin sidebar
 - Pin/Unpin sidebar

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

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

Diff do ficheiro suprimidas por serem muito extensas
+ 139 - 113
api/classes/organizr.class.php


+ 1 - 0
api/config/default.php

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

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

@@ -41,7 +41,7 @@ trait AuthFunctions
 				// Custom LDAP Options
 				// Custom LDAP Options
 				'custom_options' => [
 				'custom_options' => [
 					// See: http://php.net/ldap_set_option
 					// 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.
 			// Add a connection provider to Adldap.
@@ -116,7 +116,7 @@ trait AuthFunctions
 				// Custom LDAP Options
 				// Custom LDAP Options
 				'custom_options' => [
 				'custom_options' => [
 					// See: http://php.net/ldap_set_option
 					// 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.
 			// Add a connection provider to Adldap.
@@ -313,7 +313,7 @@ trait AuthFunctions
 				// Custom LDAP Options
 				// Custom LDAP Options
 				'custom_options' => [
 				'custom_options' => [
 					// See: http://php.net/ldap_set_option
 					// 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.
 			// Add a connection provider to Adldap.

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

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

+ 12 - 13
api/homepage/radarr.php

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

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

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

+ 3 - 3
cron.php

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

+ 5 - 3
js/functions.js

@@ -4352,6 +4352,7 @@ function organizrAPI2(type,path,data=null,asyncValue=true){
 	}
 	}
 }
 }
 function loadSettingsPage2(api,element,organizrFn){
 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) {
 	organizrAPI2('get',api).success(function(data) {
 		try {
 		try {
 			var response = data.response;
 			var response = data.response;
@@ -5244,7 +5245,7 @@ function buildStreamTooltip(bandwidth, streams, type){
     if(streams['transcode'] !== 0){
     if(streams['transcode'] !== 0){
         streamText += spacer + streams['transcode']  + ' Transcode(s)';
         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 `
     return `
     <script>$('.streamDetails-`+type+`').html('`+html+`');$('[data-toggle="tooltip"]').tooltip();</script>
     <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>';
 				queue = '<tr><td class="max-texts" lang="en">Nothing in queue</td></tr>';
 				break;
 				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;
 				count = count + 1;
 				var percent = Math.floor(((v.size - v.sizeleft) / v.size) * 100);
 				var percent = Math.floor(((v.size - v.sizeleft) / v.size) * 100);
 				percent = (isNaN(percent)) ? '0' : percent;
 				percent = (isNaN(percent)) ? '0' : percent;
 				var size = v.size != -1 ? humanFileSize(v.size,false) : "?";
 				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 += `
 				queue += `
                 <tr>
                 <tr>
                     <td class="">`+v.name+`</td>
                     <td class="">`+v.name+`</td>

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

@@ -534,7 +534,7 @@
         "Speedtest": "Test prędkości",
         "Speedtest": "Test prędkości",
         "Unit of Measurement": "Jednostka miary",
         "Unit of Measurement": "Jednostka miary",
         "Enable Pollen": "Włącz pylenie",
         "Enable Pollen": "Włącz pylenie",
-        "Enable Air Quality": "Włącz jakość powietrza",
+        "Enable Air Quality": "Włącz Air Quality",
         "Enable Weather": "Włącz pogodę",
         "Enable Weather": "Włącz pogodę",
         "Need Help With Coordinates?": "Potrzebujesz pomocy ze współrzędnymi?",
         "Need Help With Coordinates?": "Potrzebujesz pomocy ze współrzędnymi?",
         "Longitude": "Długość",
         "Longitude": "Długość",
@@ -614,54 +614,54 @@
         "Max Login Attempts": "Maksymalna liczba prób logowania",
         "Max Login Attempts": "Maksymalna liczba prób logowania",
         "Test Login": "Testuj logowanie",
         "Test Login": "Testuj logowanie",
         "Ignore External 2FA on Local Subnet": "Zignoruj zewnętrzny 2FA w lokalnej podsieci",
         "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",
         "Open Collective": "Open Collective",
         "Github Sponsor": "Github Sponsor",
         "Github Sponsor": "Github Sponsor",
-        "Backers": "Backers",
-        "Tab Folder": "Tab Folder",
+        "Backers": "Wspierający",
+        "Tab Folder": "Folder Kart",
         "Cache Folder": "Cache Folder",
         "Cache Folder": "Cache Folder",
         "Backup": "Backup",
         "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",
         "OpenCollective Sponsor": "OpenCollective Sponsor",
         "Patreon Sponsor": "Patreon Sponsor",
         "Patreon Sponsor": "Patreon Sponsor",
         "New Organizr API v2": "New Organizr API v2",
         "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",
         "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",
         "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",
         "Drop Certificate file here to upload": "Drop Certificate file here to upload",
         "Custom Certificate Loaded": "Custom Certificate Loaded",
         "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",
         "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.",
         "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",
         "i.e. X-Forwarded-Email": "i.e. X-Forwarded-Email",
         "Auth Proxy Header Name for Email": "Auth Proxy Header Name for 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 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",
         "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",
         "Bind Password": "Bind Password",
         "http(s) | ftp(s) | ldap(s)://hostname:port": "http(s) | ftp(s) | ldap(s)://hostname:port",
         "http(s) | ftp(s) | ldap(s)://hostname:port": "http(s) | ftp(s) | ldap(s)://hostname:port",
         "Plex Admin Username": "Plex Admin Username",
         "Plex Admin Username": "Plex Admin Username",
         "Default Settings Tab": "Default Settings Tab",
         "Default Settings Tab": "Default Settings Tab",
-        "Certificate": "Certificate",
+        "Certificate": "Certyfikat",
         "Ping": "Ping",
         "Ping": "Ping",
         "API": "API",
         "API": "API",
         "Github": "Github",
         "Github": "Github",
-        "Settings Page": "Settings Page",
+        "Settings Page": "Ustawienia",
         "http(s)://domain.com": "http(s)://domain.com",
         "http(s)://domain.com": "http(s)://domain.com",
         "Jellyfin SSO URL": "Jellyfin SSO URL",
         "Jellyfin SSO URL": "Jellyfin SSO URL",
         "Jellyfin API URL": "Jellyfin API URL",
         "Jellyfin API URL": "Jellyfin API URL",
@@ -675,36 +675,36 @@
         "Overseerr URL": "Overseerr URL",
         "Overseerr URL": "Overseerr URL",
         "Multiple URL's": "Multiple URL's",
         "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",
         "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",
         "Jellyfin": "Jellyfin",
         "Petio": "Petio",
         "Petio": "Petio",
         "Overseerr": "Overseerr",
         "Overseerr": "Overseerr",
         "FYI": "FYI",
         "FYI": "FYI",
         "https://app.plex.tv/auth#?resetPassword": "https://app.plex.tv/auth#?resetPassword",
         "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",
         "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",
         "Create Backup": "Create Backup",
         "Select or type Image": "Select or type Image",
         "Select or type Image": "Select or type Image",
         "Choose": "Choose",
         "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",
         "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 Sign out & in Button on Sidebar": "Show Organizr Sign out & in Button on Sidebar",
         "Show Organizr Docs Link": "Show Organizr Docs Link",
         "Show Organizr Docs Link": "Show Organizr Docs Link",
         "Show Organizr Support Link": "Show Organizr Support Link",
         "Show Organizr Support Link": "Show Organizr Support Link",
@@ -713,13 +713,147 @@
         "Theme CSS": "Theme CSS",
         "Theme CSS": "Theme CSS",
         "Custom CSS": "Custom CSS",
         "Custom CSS": "Custom CSS",
         "FavIcon": "FavIcon",
         "FavIcon": "FavIcon",
-        "Notifications": "Notifications",
+        "Notifications": "Powiadomienia",
         "Colors & Themes": "Colors & Themes",
         "Colors & Themes": "Colors & Themes",
         "Options": "Options",
         "Options": "Options",
-        "Login Page": "Login Page",
+        "Login Page": "Strona logowania",
         "Top Bar": "Top Bar",
         "Top Bar": "Top Bar",
         "Bookmark Settings": "Bookmark Settings",
         "Bookmark Settings": "Bookmark Settings",
         "HnL Settings": "HnL 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 Pollen": "Enable Pollen",
         "Enable Air Quality": "Enable Air Quality",
         "Enable Air Quality": "Enable Air Quality",
         "Enable Weather": "Enable Weather",
         "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/",
         "http://domain.com/monitorr/": "http://domain.com/monitorr/",
         "Monitorr": "Monitorr",
         "Monitorr": "Monitorr",
         "Top Platforms": "Top Platforms",
         "Top Platforms": "Top Platforms",
-        "Top Users": "Top Users",
+        "Top Users": "Топ пользователей",
         "http://<ip>:<port>": "http://<ip>:<port>",
         "http://<ip>:<port>": "http://<ip>:<port>",
         "Tautulli": "Tautulli",
         "Tautulli": "Tautulli",
         "Combine stat cards": "Combine stat cards",
         "Combine stat cards": "Combine stat cards",
@@ -559,16 +559,16 @@
         "Grab Unifi Site": "Grab Unifi Site",
         "Grab Unifi Site": "Grab Unifi Site",
         "Site Name": "Site Name",
         "Site Name": "Site Name",
         "Unifi API URL": "Unifi API URL",
         "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 Unavailable": "Show Unavailable",
-        "Show Available": "Show Available",
+        "Show Available": "Показать Доступные",
         "Disable Certificate Check": "Disable Certificate Check",
         "Disable Certificate Check": "Disable Certificate Check",
         "Organizr appends the url with": "Organizr appends the url with",
         "Organizr appends the url with": "Organizr appends the url with",
         "unless the URL ends in": "unless the URL ends in",
         "unless the URL ends in": "unless the URL ends in",
-        "ATTENTION": "ATTENTION",
-        "API Version": "API Version",
+        "ATTENTION": "ВНИМАНИЕ",
+        "API Version": "Версия API",
         "JDownloader": "JDownloader",
         "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",
         "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",
         "Emby-Jellyfin": "Emby-Jellyfin",
@@ -720,6 +720,140 @@
         "Top Bar": "Top Bar",
         "Top Bar": "Top Bar",
         "Bookmark Settings": "Bookmark Settings",
         "Bookmark Settings": "Bookmark Settings",
         "HnL Settings": "HnL 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",
+        ".": "."
     }
     }
 }
 }

Diff do ficheiro suprimidas por serem muito extensas
+ 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;
+        }
+    });
+})();

Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff