Browse Source

Merge pull request #1095 from causefx/v2-develop

V2 develop
causefx 7 years ago
parent
commit
e1daffdef6

+ 25 - 2
README.md

@@ -16,7 +16,7 @@ Do you have quite a bit of services running on your computer or server?  Do you
 
 - PHP 7.1.3+
 - [Official Site](https://organizr.app) - Will be refreshed soon!
-- [Official Discord](https://organizr.app/chat)
+- [Official Discord](https://organizr.app/discord)
 - [See Wiki](https://github.com/causefx/Organizr/wiki) - Will be updated soon!
 - [Docker](https://hub.docker.com/r/organizrtools/organizr-v2/)
 
@@ -57,7 +57,30 @@ Do you have quite a bit of services running on your computer or server?  Do you
 
 [![Feature Requests](http://feathub.com/causefx/Organizr?format=svg)](http://feathub.com/causefx/Organizr)
 
-Thanks to everyone that helped!  
+<img src="https://user-images.githubusercontent.com/16184466/53667702-fcdcc600-3c2e-11e9-8828-860e531e8096.png">
+
+##### Usage
+```
+docker create \
+  --name=organizr \
+  -v <path to data>:/config \
+  -e PGID=<gid> -e PUID=<uid>  \
+  -p 80:80 \
+  organizrtools/organizr-v2
+```
+##### Parameters
+The parameters are split into two halves, separated by a colon, the left hand side representing the host and the right the container side. For example with a port -p external:internal - what this shows is the port mapping from internal to external of the container. So `-p 8080:80` would expose port 80 from inside the container to be accessible from the host's IP on port 8080 and `http://192.168.x.x:8080` would show you what's running INSIDE the container on port 80.
+
+* `-p 80` - The port(s)
+* `-v /config` - Mapping the config files for Organizr
+* `-e PGID` Used for GroupID - see below for explanation
+* `-e PUID` Used for UserID - see below for explanation
+
+##### Info
+* Shell access whilst the container is running: `docker exec -it organizr /bin/bash`
+* To monitor the logs of the container in realtime: `docker logs -f organizr`
+* Container version number: `docker inspect -f '{{ index .Config.Labels "build_version" }}' organizr`
+* Image version number: `docker inspect -f '{{ index .Config.Labels "build_version" }}' organizrtools/docker-organizr-v2`
 
 <img src="https://user-images.githubusercontent.com/16184466/53614287-a9b73480-3b96-11e9-9c8e-e32b4ae20c0d.png">
 

+ 21 - 10
api/functions/api-functions.php

@@ -546,6 +546,9 @@ function adminEditUser($array)
 {
 	switch ($array['data']['action']) {
 		case 'changeGroup':
+			if ($array['data']['newGroupID'] == 0) {
+				return false;
+			}
 			try {
 				$connect = new Dibi\Connection([
 					'driver' => 'sqlite3',
@@ -790,6 +793,8 @@ function editTabs($array)
 					'url_local' => $array['data']['tabLocalURL'],
 					'ping_url' => $array['data']['pingURL'],
 					'image' => $array['data']['tabImage'],
+					'timeout' => $array['data']['tabActionType'],
+					'timeout_ms' => $array['data']['tabActionTime'],
 				], '
                     WHERE id=?', $array['data']['id']);
 				writeLog('success', 'Tab Editor Function -  Edited Tab Info for [' . $array['data']['tabName'] . ']', $GLOBALS['organizrUser']['username']);
@@ -837,7 +842,9 @@ function editTabs($array)
 					'enabled' => 1,
 					'group_id' => $array['data']['tabGroupID'],
 					'image' => $array['data']['tabImage'],
-					'type' => $array['data']['tabType']
+					'type' => $array['data']['tabType'],
+					'timeout' => $array['data']['tabActionType'],
+					'timeout_ms' => $array['data']['tabActionTime'],
 				];
 				$connect->query('INSERT INTO [tabs]', $newTab);
 				writeLog('success', 'Tab Editor Function - Created Tab for: ' . $array['data']['tabName'], $GLOBALS['organizrUser']['username']);
@@ -1161,14 +1168,18 @@ function revokeToken($array)
 
 function getSchema()
 {
-	try {
-		$connect = new Dibi\Connection([
-			'driver' => 'sqlite3',
-			'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
-		]);
-		$result = $connect->fetchAll(' SELECT name, sql FROM sqlite_master WHERE type=\'table\' ORDER BY name');
-		return $result;
-	} catch (Dibi\Exception $e) {
-		return false;
+	if (file_exists('config' . DIRECTORY_SEPARATOR . 'config.php')) {
+		try {
+			$connect = new Dibi\Connection([
+				'driver' => 'sqlite3',
+				'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
+			]);
+			$result = $connect->fetchAll(' SELECT name, sql FROM sqlite_master WHERE type=\'table\' ORDER BY name');
+			return $result;
+		} catch (Dibi\Exception $e) {
+			return false;
+		}
+	} else {
+		return 'DB not set yet...';
 	}
 }

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

@@ -154,25 +154,25 @@ function plugin_auth_plex($username, $password)
 {
 	try {
 		$usernameLower = strtolower($username);
-		if ((!empty($GLOBALS['plexAdmin']) && strtolower($GLOBALS['plexAdmin']) == $usernameLower) || checkPlexUser($username)) {
-			//Login User
-			$url = 'https://plex.tv/users/sign_in.json';
-			$headers = array(
-				'Accept' => 'application/json',
-				'Content-Type' => 'application/x-www-form-urlencoded',
-				'X-Plex-Product' => 'Organizr',
-				'X-Plex-Version' => '2.0',
-				'X-Plex-Client-Identifier' => $GLOBALS['uuid'],
-			);
-			$data = array(
-				'user[login]' => $username,
-				'user[password]' => $password,
-			);
-			$response = Requests::post($url, $headers, $data);
-			if ($response->success) {
-				$json = json_decode($response->body, true);
-				if ((is_array($json) && isset($json['user']) && isset($json['user']['username'])) && strtolower($json['user']['username']) == $usernameLower || strtolower($json['user']['email']) == $usernameLower) {
-					//writeLog("success", $json['user']['username']." was logged into organizr using plex credentials");
+		//Login User
+		$url = 'https://plex.tv/users/sign_in.json';
+		$headers = array(
+			'Accept' => 'application/json',
+			'Content-Type' => 'application/x-www-form-urlencoded',
+			'X-Plex-Product' => 'Organizr',
+			'X-Plex-Version' => '2.0',
+			'X-Plex-Client-Identifier' => $GLOBALS['uuid'],
+		);
+		$data = array(
+			'user[login]' => $username,
+			'user[password]' => $password,
+		);
+		$response = Requests::post($url, $headers, $data);
+		if ($response->success) {
+			$json = json_decode($response->body, true);
+			if ((is_array($json) && isset($json['user']) && isset($json['user']['username'])) && strtolower($json['user']['username']) == $usernameLower || strtolower($json['user']['email']) == $usernameLower) {
+				//writeLog("success", $json['user']['username']." was logged into organizr using plex credentials");
+				if ((!empty($GLOBALS['plexAdmin']) && (strtolower($GLOBALS['plexAdmin']) == strtolower($json['user']['username'])) || (strtolower($GLOBALS['plexAdmin']) == strtolower($json['user']['email']))) || checkPlexUser($json['user']['username'])) {
 					return array(
 						'username' => $json['user']['username'],
 						'email' => $json['user']['email'],

+ 9 - 9
api/functions/homepage-connect-functions.php

@@ -1251,7 +1251,7 @@ function getSonarrCalendar($array, $number)
 			"id" => "Sonarr-" . $number . "-" . $i,
 			"title" => $seriesName,
 			"start" => $child['airDateUtc'],
-			"className" => "bg-calendar calendar-item tvID--" . $episodeID,
+			"className" => "inline-popups bg-calendar calendar-item tvID--" . $episodeID,
 			"imagetype" => "tv " . $downloaded,
 			"imagetypeFilter" => "tv",
 			"downloadFilter" => $downloaded,
@@ -1315,7 +1315,7 @@ function getLidarrCalendar($array, $number)
 			"id" => "Lidarr-" . $number . "-" . $i,
 			"title" => $artistName,
 			"start" => $child['releaseDate'],
-			"className" => "bg-calendar calendar-item musicID--",
+			"className" => "inline-popups bg-calendar calendar-item musicID--",
 			"imagetype" => "music " . $downloaded,
 			"imagetypeFilter" => "music",
 			"downloadFilter" => $downloaded,
@@ -1409,7 +1409,7 @@ function getRadarrCalendar($array, $number, $url)
 				"id" => "Radarr-" . $number . "-" . $i,
 				"title" => $movieName,
 				"start" => $physicalRelease,
-				"className" => "bg-calendar movieID--" . $movieID,
+				"className" => "inline-popups bg-calendar movieID--" . $movieID,
 				"imagetype" => "film " . $downloaded,
 				"imagetypeFilter" => "film",
 				"downloadFilter" => $downloaded,
@@ -1494,7 +1494,7 @@ function getCouchCalendar($array, $number)
 				"id" => "CouchPotato-" . $number . "-" . $i,
 				"title" => $movieName,
 				"start" => $physicalRelease,
-				"className" => "bg-calendar calendar-item movieID--" . $movieID,
+				"className" => "inline-popups bg-calendar calendar-item movieID--" . $movieID,
 				"imagetype" => "film " . $downloaded,
 				"imagetypeFilter" => "film",
 				"downloadFilter" => $downloaded,
@@ -1563,7 +1563,7 @@ function getSickrageCalendarWanted($array, $number)
 			"id" => "Sick-" . $number . "-Miss-" . $i,
 			"title" => $seriesName,
 			"start" => $episodeAirDate,
-			"className" => "bg-calendar calendar-item tvID--" . $episodeID,
+			"className" => "inline-popups bg-calendar calendar-item tvID--" . $episodeID,
 			"imagetype" => "tv " . $downloaded,
 			"imagetypeFilter" => "tv",
 			"downloadFilter" => $downloaded,
@@ -1620,7 +1620,7 @@ function getSickrageCalendarWanted($array, $number)
 			"id" => "Sick-" . $number . "-Today-" . $i,
 			"title" => $seriesName,
 			"start" => $episodeAirDate,
-			"className" => "bg-calendar calendar-item tvID--" . $episodeID,
+			"className" => "inline-popups bg-calendar calendar-item tvID--" . $episodeID,
 			"imagetype" => "tv " . $downloaded,
 			"imagetypeFilter" => "tv",
 			"downloadFilter" => $downloaded,
@@ -1677,7 +1677,7 @@ function getSickrageCalendarWanted($array, $number)
 			"id" => "Sick-" . $number . "-Soon-" . $i,
 			"title" => $seriesName,
 			"start" => $episodeAirDate,
-			"className" => "bg-calendar calendar-item tvID--" . $episodeID,
+			"className" => "inline-popups bg-calendar calendar-item tvID--" . $episodeID,
 			"imagetype" => "tv " . $downloaded,
 			"imagetypeFilter" => "tv",
 			"downloadFilter" => $downloaded,
@@ -1734,7 +1734,7 @@ function getSickrageCalendarWanted($array, $number)
 			"id" => "Sick-" . $number . "-Later-" . $i,
 			"title" => $seriesName,
 			"start" => $episodeAirDate,
-			"className" => "bg-calendar calendar-item tvID--" . $episodeID,
+			"className" => "inline-popups bg-calendar calendar-item tvID--" . $episodeID,
 			"imagetype" => "tv " . $downloaded,
 			"imagetypeFilter" => "tv",
 			"downloadFilter" => $downloaded,
@@ -1788,7 +1788,7 @@ function getSickrageCalendarHistory($array, $number)
 			"id" => "Sick-" . $number . "-History-" . $i,
 			"title" => $seriesName,
 			"start" => $episodeAirDate,
-			"className" => "bg-calendar calendar-item tvID--" . $episodeID,
+			"className" => "inline-popups bg-calendar calendar-item tvID--" . $episodeID,
 			"imagetype" => "tv " . $downloaded,
 			"imagetypeFilter" => "tv",
 			"downloadFilter" => $downloaded,

+ 66 - 15
api/functions/organizr-functions.php

@@ -77,15 +77,16 @@ function organizrSpecialSettings()
 		'user' => array(
 			'agent' => isset($_SERVER ['HTTP_USER_AGENT']) ? $_SERVER ['HTTP_USER_AGENT'] : null,
 			'oAuthLogin' => isset($_COOKIE['oAuth']) ? true : false,
-			'local' => (isLocal()) ? true : false
+			'local' => (isLocal()) ? true : false,
+			'ip' => userIP()
 		),
 		'login' => array(
 			'rememberMe' => $GLOBALS['rememberMe'],
 			'rememberMeDays' => $GLOBALS['rememberMeDays'],
 		),
 		'misc' => array(
-			'installedPlugins' => $GLOBALS['installedPlugins'],
-			'installedThemes' => $GLOBALS['installedThemes'],
+			'installedPlugins' => qualifyRequest(1) ? $GLOBALS['installedPlugins'] : '',
+			'installedThemes' => qualifyRequest(1) ? $GLOBALS['installedThemes'] : '',
 			'return' => isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : false,
 			'authDebug' => $GLOBALS['authDebug'],
 			'minimalLoginScreen' => $GLOBALS['minimalLoginScreen'],
@@ -94,9 +95,9 @@ function organizrSpecialSettings()
 			'authBackend' => $GLOBALS['authBackend'],
 			'newMessageSound' => (isset($GLOBALS['CHAT-newMessageSound-include'])) ? $GLOBALS['CHAT-newMessageSound-include'] : '',
 			'uuid' => $GLOBALS['uuid'],
-			'docker' => $GLOBALS['docker'],
-			'githubCommit' => $GLOBALS['commit'],
-			'schema' => getSchema(),
+			'docker' => qualifyRequest(1) ? $GLOBALS['docker'] : '',
+			'githubCommit' => qualifyRequest(1) ? $GLOBALS['commit'] : '',
+			'schema' => qualifyRequest(1) ? getSchema() : '',
 			'debugArea' => qualifyRequest($GLOBALS['debugAreaAuth'])
 		)
 	);
@@ -369,6 +370,28 @@ function qualifyRequest($accessLevelNeeded)
 	}
 }
 
+function isApprovedRequest($method)
+{
+	$requesterToken = isset(getallheaders()['Token']) ? getallheaders()['Token'] : (isset($_GET['apikey']) ? $_GET['apikey'] : false);
+	$requesterFormKeyHeader = isset(getallheaders()['Formkey']) ? getallheaders()['Formkey'] : false;
+	// Check token or API key
+	// If API key, return 0 for admin
+	if (strlen($requesterToken) == 20 && $requesterToken == $GLOBALS['organizrAPI']) {
+		//DO API CHECK
+		return true;
+	} elseif ($method == 'POST') {
+		$formKey = (isset($_POST['data']['formKey'])) ? $_POST['data']['formKey'] : '';
+		if (password_verify(substr($GLOBALS['quickConfig']['organizrHash'], 2, 10), $formKey)) {
+			return true;
+		} elseif (($requesterFormKeyHeader) && password_verify(substr($GLOBALS['quickConfig']['organizrHash'], 2, 10), $requesterFormKeyHeader)) {
+			return true;
+		}
+	} else {
+		return true;
+	}
+	return false;
+}
+
 function getUserLevel()
 {
 	// Grab token
@@ -443,7 +466,7 @@ function getSettingsMain()
 				'label' => 'Branch',
 				'value' => $GLOBALS['branch'],
 				'options' => getBranches(),
-				'disabled' => $GLOBALS['branch'],
+				'disabled' => $GLOBALS['docker'],
 				'help' => ($GLOBALS['docker']) ? 'Since you are using the Official Docker image, Change the image to change the branch' : 'Choose which branch to download from'
 			),
 			array(
@@ -1212,7 +1235,7 @@ function getCustomizeAppearance()
 					'type' => 'html',
 					'override' => 12,
 					'label' => 'Custom CSS [Can replace colors from above]',
-					'html' => '<button type="button" class="hidden saveCss btn btn-info btn-circle pull-right m-r-5 m-l-10"><i class="fa fa-save"></i> </button><div id="customCSSEditor" style="height:300px">' . $GLOBALS['customCss'] . '</div>'
+					'html' => '<button type="button" class="hidden saveCss btn btn-info btn-circle pull-right m-r-5 m-l-10"><i class="fa fa-save"></i> </button><div id="customCSSEditor" style="height:300px">' . htmlentities($GLOBALS['customCss']) . '</div>'
 				),
 				array(
 					'type' => 'textbox',
@@ -1229,7 +1252,7 @@ function getCustomizeAppearance()
 					'type' => 'html',
 					'override' => 12,
 					'label' => 'Theme CSS [Can replace colors from above]',
-					'html' => '<button type="button" class="hidden saveCssTheme btn btn-info btn-circle pull-right m-r-5 m-l-10"><i class="fa fa-save"></i> </button><div id="customThemeCSSEditor" style="height:300px">' . $GLOBALS['customThemeCss'] . '</div>'
+					'html' => '<button type="button" class="hidden saveCssTheme btn btn-info btn-circle pull-right m-r-5 m-l-10"><i class="fa fa-save"></i> </button><div id="customThemeCSSEditor" style="height:300px">' . htmlentities($GLOBALS['customThemeCss']) . '</div>'
 				),
 				array(
 					'type' => 'textbox',
@@ -1246,7 +1269,7 @@ function getCustomizeAppearance()
 					'type' => 'html',
 					'override' => 12,
 					'label' => 'Custom Javascript',
-					'html' => '<button type="button" class="hidden saveJava btn btn-info btn-circle pull-right m-r-5 m-l-10"><i class="fa fa-save"></i> </button><div id="customJavaEditor" style="height:300px">' . $GLOBALS['customJava'] . '</div>'
+					'html' => '<button type="button" class="hidden saveJava btn btn-info btn-circle pull-right m-r-5 m-l-10"><i class="fa fa-save"></i> </button><div id="customJavaEditor" style="height:300px">' . htmlentities($GLOBALS['customJava']) . '</div>'
 				),
 				array(
 					'type' => 'textbox',
@@ -1263,7 +1286,7 @@ function getCustomizeAppearance()
 					'type' => 'html',
 					'override' => 12,
 					'label' => 'Theme Javascript',
-					'html' => '<button type="button" class="hidden saveJavaTheme btn btn-info btn-circle pull-right m-r-5 m-l-10"><i class="fa fa-save"></i> </button><div id="customThemeJavaEditor" style="height:300px">' . $GLOBALS['customThemeJava'] . '</div>'
+					'html' => '<button type="button" class="hidden saveJavaTheme btn btn-info btn-circle pull-right m-r-5 m-l-10"><i class="fa fa-save"></i> </button><div id="customThemeJavaEditor" style="height:300px">' . htmlentities($GLOBALS['customThemeJava']) . '</div>'
 				),
 				array(
 					'type' => 'textbox',
@@ -1518,15 +1541,17 @@ function editImages()
 	$array = array();
 	$postCheck = array_filter($_POST);
 	$filesCheck = array_filter($_FILES);
+	$approvedPath = 'plugins/images/tabs/';
 	if (!empty($postCheck)) {
-		if ($_POST['data']['action'] == 'deleteImage') {
-			if (file_exists(dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . $_POST['data']['imagePath'])) {
+		$removeImage = $approvedPath . pathinfo($_POST['data']['imagePath'], PATHINFO_BASENAME);
+		if ($_POST['data']['action'] == 'deleteImage' && approvedFileExtension($removeImage)) {
+			if (file_exists(dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . $removeImage)) {
 				writeLog('success', 'Image Manager Function -  Deleted Image [' . $_POST['data']['imageName'] . ']', $GLOBALS['organizrUser']['username']);
-				return (unlink(dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . $_POST['data']['imagePath'])) ? true : false;
+				return (unlink(dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . $removeImage)) ? true : false;
 			}
 		}
 	}
-	if (!empty($filesCheck)) {
+	if (!empty($filesCheck) && approvedFileExtension($_FILES['file']['name']) && strpos($_FILES['file']['type'], 'image/') !== false) {
 		ini_set('upload_max_filesize', '10M');
 		ini_set('post_max_size', '10M');
 		$tempFile = $_FILES['file']['tmp_name'];
@@ -1537,6 +1562,21 @@ function editImages()
 	return false;
 }
 
+function approvedFileExtension($filename)
+{
+	$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
+	switch ($ext) {
+		case 'gif':
+		case 'png':
+		case 'jpeg':
+		case 'jpg':
+			return true;
+			break;
+		default:
+			return false;
+	}
+}
+
 function getThemes()
 {
 	$themes = array();
@@ -2117,6 +2157,17 @@ function dockerUpdate()
 	return $dockerUpdate;
 }
 
+function windowsUpdate()
+{
+	$branch = ($GLOBALS['branch'] == 'v2-master') ? '-m' : '-d';
+	ini_set('max_execution_time', 0);
+	set_time_limit(0);
+	$logFile = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'scripts' . DIRECTORY_SEPARATOR . 'log.txt';
+	$windowsScript = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'scripts' . DIRECTORY_SEPARATOR . 'windows-update.bat ' . $branch . ' > ' . $logFile . ' 2>&1';
+	$windowsUpdate = shell_exec($windowsScript);
+	return ($windowsUpdate) ? $windowsUpdate : 'Update Complete - check log.txt for output';
+}
+
 function checkHostPrefix($s)
 {
 	if (empty($s)) {

+ 15 - 7
api/functions/static-globals.php

@@ -1,7 +1,7 @@
 <?php
 // ===================================
 // Organizr Version
-$GLOBALS['installedVersion'] = '2.0.0';
+$GLOBALS['installedVersion'] = '2.0.32';
 // ===================================
 // Quick php Version check
 $GLOBALS['minimumPHP'] = '7.1.3';
@@ -13,6 +13,7 @@ $GLOBALS['userConfigPath'] = dirname(__DIR__, 1) . DIRECTORY_SEPARATOR . 'config
 $GLOBALS['defaultConfigPath'] = dirname(__DIR__, 1) . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'default.php';
 $GLOBALS['currentTime'] = gmdate("Y-m-d\TH:i:s\Z");
 $GLOBALS['docker'] = (file_exists(dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'Docker.txt')) ? true : false;
+$GLOBALS['quickConfig'] = (file_exists($GLOBALS['userConfigPath'])) ? loadConfigOnce($GLOBALS['userConfigPath']) : null;
 // Quick function for plugins
 function pluginFiles($type)
 {
@@ -46,6 +47,16 @@ function loadConfigOnce($path = null)
 	}
 }
 
+function formKey()
+{
+	if (isset($GLOBALS['quickConfig']['organizrAPI'])) {
+		if ($GLOBALS['quickConfig']['organizrAPI'] !== '') {
+			$hash = password_hash(substr($GLOBALS['quickConfig']['organizrHash'], 2, 10), PASSWORD_BCRYPT);
+			return '<script>local("s","formKey","' . $hash . '");</script>';
+		}
+	}
+}
+
 function favIcons()
 {
 	$favicon = '
@@ -60,12 +71,9 @@ function favIcons()
 	<meta name="msapplication-config" content="plugins/images/favicon/browserconfig.xml">
 	<meta name="theme-color" content="#ffffff">
 	';
-	if (file_exists($GLOBALS['userConfigPath'])) {
-		$config = loadConfigOnce($GLOBALS['userConfigPath']);
-		if (isset($config['favIcon'])) {
-			if ($config['favIcon'] !== '') {
-				$favicon = $config['favIcon'];
-			}
+	if (isset($GLOBALS['quickConfig']['favIcon'])) {
+		if ($GLOBALS['quickConfig']['favIcon'] !== '') {
+			$favicon = $GLOBALS['quickConfig']['favIcon'];
 		}
 	}
 	return $favicon;

+ 27 - 0
api/index.php

@@ -15,6 +15,14 @@ if ($function === false) {
 	$result['statusText'] = "No API Path Supplied";
 	exit(json_encode($result));
 }
+if ($function !== 'v1_auth' && $function !== 'v1_wizard_config' && $function !== 'v1_login' && $function !== 'v1_wizard_path') {
+	if (isApprovedRequest($method, $_POST) === false) {
+		$result['status'] = "error";
+		$result['statusText'] = "Not Authorized";
+		writeLog('success', 'Killed Attack From [' . (isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : 'No Referer') . ']', $GLOBALS['organizrUser']['username']);
+		exit(json_encode($result));
+	}
+}
 $result['request'] = key($_GET);
 $result['params'] = $_POST;
 switch ($function) {
@@ -1293,6 +1301,25 @@ switch ($function) {
 				break;
 		}
 		break;
+	case 'v1_windows_update':
+		switch ($method) {
+			case 'GET':
+				if (qualifyRequest(1)) {
+					$result['status'] = 'success';
+					$result['statusText'] = 'success';
+					$result['data'] = windowsUpdate();
+				} else {
+					$result['status'] = 'error';
+					$result['statusText'] = 'API/Token invalid or not set';
+					$result['data'] = null;
+				}
+				break;
+			default:
+				$result['status'] = 'error';
+				$result['statusText'] = 'The function requested is not defined for method: ' . $method;
+				break;
+		}
+		break;
 	default:
 		//No Function Available
 		$result['status'] = 'error';

+ 1 - 1
api/pages/settings-image-manager.php

@@ -1,10 +1,10 @@
 <?php
-
 $pageSettingsImageManager = '
 <script>
 	buildImageManagerView();
     var myDropzone = new Dropzone("#new-image-form", {
       url: "api/?v1/settings/image/manager/view",
+      headers:{ "formKey": local("g","formKey") },
       init: function() {
         this.on("complete", function(file) {
             buildImageManagerView();

+ 28 - 0
api/pages/settings-tab-editor-tabs.php

@@ -82,6 +82,20 @@ allIcons().success(function(data) {
             <label class="control-label" for="new-tab-form-inputPingURLNew" lang="en">Ping URL</label>
             <input type="text" class="form-control" id="new-tab-form-inputPingURLNew" name="pingURL"  placeholder="host/ip:port">
         </div>
+        <div class="row">
+	        <div class="form-group col-lg-6">
+	            <label class="control-label" for="new-tab-form-inputTabActionTypeNew" lang="en">Tab Auto Action</label>
+	                <select class="form-control" id="new-tab-form-inputTabActionTypeNew" name="tabActionType">
+	                    <option value="null">None</option>
+	                    <option value="1">Auto Close</option>
+	                    <option value="2">Auto Reload</option>
+					</select>
+	        </div>
+	        <div class="form-group col-lg-6">
+	            <label class="control-label" for="new-tab-form-inputTabActionTimeNew" lang="en">Tab Auto Action Minutes</label>
+	                <input type="number" class="form-control" id="new-tab-form-inputTabActionTimeNew" name="tabActionTime"  placeholder="0">
+	        </div>
+	    </div>
         <div class="row">
 	        <div class="form-group col-lg-6">
 	            <label class="control-label" for="new-tab-form-chooseImage" lang="en">Choose Image</label>
@@ -130,6 +144,20 @@ allIcons().success(function(data) {
             <label class="control-label" for="edit-tab-form-pingURL" lang="en">Ping URL</label>
             <input type="text" class="form-control" id="edit-tab-form-pingURL" name="pingURL" placeholder="host/ip:port">
         </div>
+        <div class="row">
+	        <div class="form-group col-lg-6">
+	            <label class="control-label" for="edit-tab-form-inputTabActionTypeNew" lang="en">Tab Auto Action</label>
+	                <select class="form-control" id="edit-tab-form-inputTabActionTypeNew" name="tabActionType">
+	                    <option value="null">None</option>
+	                    <option value="1">Auto Close</option>
+	                    <option value="2">Auto Reload</option>
+					</select>
+	        </div>
+	        <div class="form-group col-lg-6">
+	            <label class="control-label" for="edit-tab-form-inputTabActionTimeNew" lang="en">Tab Auto Action Minutes</label>
+	                <input type="number" class="form-control" id="edit-tab-form-inputTabActionTimeNew" name="tabActionTime">
+	        </div>
+	    </div>
         <div class="row">
 	        <div class="form-group col-lg-6">
 	            <label class="control-label" for="edit-tab-form-chooseImage" lang="en">Choose Image</label>

+ 2 - 1
index.php

@@ -291,7 +291,8 @@
 <script id="custom-theme-javascript"></script>
 <script id="custom-javascript"></script>
 <script src="https://js.pusher.com/4.1/pusher.min.js"></script>
-<?php echo pluginFiles('js'); ?>
+<?php echo pluginFiles('js');
+echo formKey(); ?>
 </body>
 
 </html>

+ 22 - 0
js/custom.js

@@ -843,8 +843,24 @@ $(document).on("click", ".deleteTab", function () {
         }
     });
 });
+function convertMsToMinutes(ms){
+    if(ms === false || ms === 0 || ms === "0"){
+        return 0;
+    }else{
+        return (ms / 1000) / 60;
+    }
+}
+function convertMinutesToMs(minutes){
+    if(minutes === false || minutes === 0 || minutes === "0"){
+        return 0;
+    }else{
+        return (minutes * 1000) * 60;
+    }
+}
 //EDIT TAB GET ID
 $(document).on("click", ".editTabButton", function () {
+    //tabActionTime
+    //tabActionType
     $('#edit-tab-form [name=tabName]').val($(this).parent().parent().attr("data-name"));
     $('#originalTabName').html($(this).parent().parent().attr("data-name"));
     $('#edit-tab-form [name=tabURL]').val($(this).parent().parent().attr("data-url"));
@@ -852,6 +868,8 @@ $(document).on("click", ".editTabButton", function () {
     $('#edit-tab-form [name=pingURL]').val($(this).parent().parent().attr("data-ping-url"));
     $('#edit-tab-form [name=tabImage]').val($(this).parent().parent().attr("data-image"));
     $('#edit-tab-form [name=id]').val($(this).parent().parent().attr("data-id"));
+    $('#edit-tab-form [name=tabActionTime]').val(convertMsToMinutes($(this).parent().parent().attr("data-tab-action-time")));
+    $('#edit-tab-form [name=tabActionType]').val($(this).parent().parent().attr("data-tab-action-type"));
     if( $(this).parent().parent().attr("data-url").indexOf('/?v') > 0){
         $('#edit-tab-form [name=tabURL]').prop('disabled', 'true');
     }else{
@@ -871,6 +889,8 @@ $(document).on("click", ".editTab", function () {
         tabURL:$('#edit-tab-form [name=tabURL]').val(),
         tabLocalURL:$('#edit-tab-form [name=tabLocalURL]').val(),
         pingURL:$('#edit-tab-form [name=pingURL]').val(),
+        tabActionTime:convertMinutesToMs($('#edit-tab-form [name=tabActionTime]').val()),
+        tabActionType:$('#edit-tab-form [name=tabActionType]').val(),
         messageTitle:'',
         messageBody:'Edited Tab '+$('#edit-tab-form [name=tabName]').val(),
         error:'Organizr Function: Tab Editor API Connection Failed'
@@ -911,6 +931,8 @@ $(document).on("click", ".addNewTab", function () {
         tabURL:$('#new-tab-form [name=tabURL]').val(),
         tabLocalURL:$('#new-tab-form [name=tabLocalURL]').val(),
         pingURL:$('#new-tab-form [name=pingURL]').val(),
+        tabActionTime:convertMinutesToMs($('#new-tab-form [name=tabActionTime]').val()),
+        tabActionType:$('#new-tab-form [name=tabActionType]').val(),
         tabGroupID:1,
         tabEnabled:0,
         tabDefault:0,

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


+ 157 - 17
js/functions.js

@@ -14,6 +14,12 @@ lang.init({
 	allowCookieOverride: true
 });
 var timeouts = {};
+var increment = 0;
+var tabInformation = {};
+var tabActionsList = [];
+tabActionsList['refresh'] = [];
+tabActionsList['close'] = [];
+
 // Start Organizr
 $(document).ready(function () {
     launch();
@@ -192,7 +198,67 @@ function isNumberKey(evt) {
         return false;
     return true;
 }
+function setTabInfo(tab,action,value){
+    if(tab !== null && action !== null && value !== null){
+        switch(action){
+            case 'active':
+                $.each(tabInformation, function(i,v) {
+                    tabInformation[i]['active'] = false;
+                });
+                break;
+            default:
+            //nada
+        }
+        tabInformation[tab][action] = value;
+    }else{
+        return false;
+    }
+}
+function tabTimerAction(){
+    if(tabActionsList.close.length > 0){
+        $.each(tabActionsList.close, function(i,v) {
+            var tab = v.tab;
+            var minutes = (tabInformation[tab]['tabInfo']['timeout_ms'] / 1000) /60;
+            var process = false;
+            if(tabInformation[tab]['loaded']){
+                if(tabInformation[tab]['active'] && idleTime >= 1){
+                    process = true;
+                }
+                if(tabInformation[tab]['active'] === false){
+                    process = true;
+                }
+                if(process){
+                    tabInformation[tab]['increments'] = tabInformation[tab]['increments'] + 1;
+                    if(tabInformation[tab]['increments'] >= minutes){
+                        tabInformation[tab]['increments'] = 0;
+                        console.log('Tab Function: Auto Closing tab: '+tab);
+                        closeTab(tab);
+                    }
+                }
+
+            }
+        });
+    }
+    if(tabActionsList.refresh.length > 0){
+        $.each(tabActionsList.refresh, function(i,v) {
+            var tab = v.tab;
+            var minutes = (tabInformation[tab]['tabInfo']['timeout_ms'] / 1000) /60;
+            var process = false;
+            if(tabInformation[tab]['loaded']){
+                tabInformation[tab]['increments'] = tabInformation[tab]['increments'] + 1;
+                if(tabInformation[tab]['increments'] >= minutes){
+                    tabInformation[tab]['increments'] = 0;
+                    console.log('Tab Function: Auto Reloading tab: '+tab);
+                    reloadTab(tab, tabInformation[tab]['tabInfo']['type']);
+                }
+            }
+        });
+    }
+
+}
 function timerIncrement() {
+    increment = increment + 1;
+    tabTimerAction();
     //check for cookieExpiry
     if(hasCookie){
         if(getCookie('organizrToken')){
@@ -474,12 +540,15 @@ function switchTab(tab, type){
 			if(newTab.hasClass('loaded')){
 				console.log('Tab Function: Switching to tab: '+tab);
 				newTab.addClass("show").removeClass('hidden');
+                setTabInfo(cleanClass(tab),'active',true);
 			}else{
 				$("#preloader").fadeIn();
 				console.log('Tab Function: Loading new tab for: '+tab);
 				$('#menu-'+tab+' a').children().addClass('tabLoaded');
 				newTab.addClass("show loaded").removeClass('hidden');
 				loadInternal(tabURL,cleanClass(tab));
+                setTabInfo(cleanClass(tab),'active',true);
+                setTabInfo(cleanClass(tab),'loaded',true);
 				$("#preloader").fadeOut();
 			}
 			break;
@@ -493,12 +562,15 @@ function switchTab(tab, type){
 			if(newTab.hasClass('loaded')){
 				console.log('Tab Function: Switching to tab: '+tab);
 				newTab.addClass("show").removeClass('hidden');
+                setTabInfo(cleanClass(tab),'active',true);
 			}else{
 				$("#preloader").fadeIn();
 				console.log('Tab Function: Loading new tab for: '+tab);
 				$('#menu-'+tab+' a').children().addClass('tabLoaded');
 				newTab.addClass("show loaded").removeClass('hidden');
 				$(buildFrame(tab,tabURL)).appendTo(newTab);
+                setTabInfo(cleanClass(tab),'active',true);
+                setTabInfo(cleanClass(tab),'loaded',true);
 				$("#preloader").fadeOut();
 			}
             $('#frame-'+tab).focus();
@@ -556,12 +628,18 @@ function closeTab(tab){
                case 0:
                case '0':
                case 'internal':
+                   // quick check if homepage
+                   if($('#menu-'+tab).attr('data-url') == 'api/?v1/homepage/page'){
+                       console.log('Organizr Function - Clearing All Homepage AJAX calls');
+                       clearAJAX('homepage');
+                   }
                    console.log('Tab Function: Closing tab: '+tab);
                    $('#internal-'+cleanClass(tab)).html('');
                    $('#menu-'+cleanClass(tab)+' a').removeClass("active");
                    $('#menu-'+tab+' a').children().removeClass('tabLoaded');
                    $('#internal-'+cleanClass(tab)).removeClass("loaded show");
                    $('#menu-'+cleanClass(tab)).removeClass("active");
+                   setTabInfo(cleanClass(tab),'loaded',false);
                    break;
                case 1:
                case '1':
@@ -571,6 +649,7 @@ function closeTab(tab){
                    $('#menu-'+tab+' a').children().removeClass('tabLoaded');
                    $('#container-'+cleanClass(tab)).removeClass("loaded show");
                    $('#frame-'+cleanClass(tab)).remove();
+                   setTabInfo(cleanClass(tab),'loaded',false);
                    break;
                case 2:
                case 3:
@@ -676,12 +755,19 @@ function closeCurrentTab(){
 		case '0':
 		case 'internal':
 			var tab = $('.internal-listing').find('.show').attr('data-name');
+            // quick check if homepage
+            if($('#menu-'+cleanClass(tab)).attr('data-url') == 'api/?v1/homepage/page'){
+                console.log('Organizr Function - Clearing All Homepage AJAX calls');
+                clearAJAX('homepage');
+            }
 			console.log('Tab Function: Closing tab: '+tab);
 			$('#internal-'+cleanClass(tab)).html('');
 			$('#menu-'+cleanClass(tab)+' a').removeClass("active");
 			$('#menu-'+tab+' a').children().removeClass('tabLoaded');
 			$('#internal-'+cleanClass(tab)).removeClass("loaded show");
 			$('#menu-'+cleanClass(tab)).removeClass("active");
+            setTabInfo(cleanClass(tab),'loaded',false);
+            setTabInfo(cleanClass(tab),'active',false);
 			loadNextTab();
 			break;
 		case 1:
@@ -693,6 +779,8 @@ function closeCurrentTab(){
 			$('#menu-'+tab+' a').children().removeClass('tabLoaded');
 			$('#container-'+cleanClass(tab)).removeClass("loaded show");
 			$('#frame-'+cleanClass(tab)).remove();
+            setTabInfo(cleanClass(tab),'loaded',false);
+            setTabInfo(cleanClass(tab),'active',false);
 			loadNextTab();
 			break;
 		case 2:
@@ -2367,6 +2455,19 @@ function tabProcess(arrayItems) {
 	if (Array.isArray(arrayItems['data']['tabs']) && arrayItems['data']['tabs'].length > 0) {
 		$.each(arrayItems['data']['tabs'], function(i,v) {
 			if(v.enabled === 1 && v.access_url){
+                tabInformation[cleanClass(v.name)] = {"active":false,"loaded":false,"increments":0,"tabInfo":v};
+                switch(v.timeout){
+                    case 1:
+                    case '1':
+                        tabActionsList['close'].push({"tab":cleanClass(v.name),"action_ms":v.timeout_ms});
+                        break;
+                    case 2:
+                    case '2':
+                        tabActionsList['refresh'].push({"tab":cleanClass(v.name),"action_ms":v.timeout_ms});
+                        break;
+                    default:
+                        //nada
+                }
                 if(v.default === 1){
                     defaultTabName = cleanClass(v.name);
                     defaultTabType = v.type;
@@ -2697,7 +2798,7 @@ function buildTabEditorItem(array){
 		var buttonDisabled = v.url.indexOf('/settings/') > 0 ? 'disabled' : '';
         var typeDisabled = v.url.indexOf('/?v1/') > 0 ? 'disabled' : '';
 		tabList += `
-		<tr class="tabEditor" data-order="`+v.order+`" data-id="`+v.id+`" data-group-id="`+v.group_id+`" data-category-id="`+v.category_id+`" data-name="`+v.name+`" data-url="`+v.url+`" data-local-url="`+v.url_local+`" data-ping-url="`+v.ping_url+`" data-image="`+v.image+`">
+		<tr class="tabEditor" data-order="`+v.order+`" data-id="`+v.id+`" data-group-id="`+v.group_id+`" data-category-id="`+v.category_id+`" data-name="`+v.name+`" data-url="`+v.url+`" data-local-url="`+v.url_local+`" data-ping-url="`+v.ping_url+`" data-image="`+v.image+`" data-tab-action-type="`+v.timeout+`" data-tab-action-time="`+v.timeout_ms+`">
 			<input type="hidden" class="form-control" name="tab[`+v.id+`].id" value="`+v.id+`">
 			<input type="hidden" class="form-control order" name="tab[`+v.id+`].order" value="`+v.order+`">
 			<input type="hidden" class="form-control" name="tab[`+v.id+`].originalOrder" value="`+v.order+`">
@@ -2706,6 +2807,8 @@ function buildTabEditorItem(array){
 			<input type="hidden" class="form-control" name="tab[`+v.id+`].url" value="`+v.url+`">
 			<input type="hidden" class="form-control" name="tab[`+v.id+`].ping_url" value="`+v.ping_url+`">
 			<input type="hidden" class="form-control" name="tab[`+v.id+`].image" value="`+v.image+`">
+			<input type="hidden" class="form-control" name="tab[`+v.id+`].timeout" value="`+v.timeout+`">
+			<input type="hidden" class="form-control" name="tab[`+v.id+`].timeout_ms" value="`+v.timeout_ms+`">
 			<td style="text-align:center" class="text-center el-element-overlay">
 				<div class="el-card-item p-0">
 					<div class="el-card-avatar el-overlay-1 m-0">
@@ -2944,10 +3047,12 @@ function updateCheck(){
 			var latest = a;
 			break;
 		}
-		if(latest !== currentVersion){
-			console.log('Update Function: Update to '+latest+' is available');
-			message(window.lang.translate('Update Available'),latest+' '+window.lang.translate('is available, goto')+' <a href="javascript:void(0)" onclick="tabActions(event,\'Settings\',0);clickPath(\'update\')"><span lang="en">Update Tab</span></a>',activeInfo.settings.notifications.position,'#FFF','update','60000');
-		}
+		if(latest !== currentVersion) {
+            console.log('Update Function: Update to ' + latest + ' is available');
+            if (activeInfo.settings.misc.docker === false) {
+                message(window.lang.translate('Update Available'), latest + ' ' + window.lang.translate('is available, goto') + ' <a href="javascript:void(0)" onclick="tabActions(event,\'Settings\',0);clickPath(\'update\')"><span lang="en">Update Tab</span></a>', activeInfo.settings.notifications.position, '#FFF', 'update', '60000');
+            }
+        }
 		$('#githubVersions').html(buildVersion(reverseObject(response)));
 	}).fail(function(xhr) {
 		console.error("Organizr Function: Github Connection Failed");
@@ -3156,7 +3261,28 @@ function dockerUpdate(){
         });
     }
 }
+function windowsUpdate(){
+    if(activeInfo.serverOS == 'win'){
+        $(updateBar()).appendTo('.organizr-area');
+        updateUpdateBar('Starting Download','20%');
+        messageSingle(window.lang.translate('[DO NOT CLOSE WINDOW]'),window.lang.translate('Starting Update Process'),activeInfo.settings.notifications.position,'#FFF','success','60000');
+        organizrAPI('GET','api/?v1/windows/update').success(function(data) {
+            try {
+                var json = JSON.parse(data);
+            }catch(e) {
+                console.log(e + ' error: ' + data);
+                orgErrorAlert('<h4>' + e + '</h4>' + formatDebug(data));
+                return false;
+            }
+            updateUpdateBar('Restarting Organizr in', '100%', true);
+            messageSingle(window.lang.translate('[DO NOT CLOSE WINDOW]'),json.data,activeInfo.settings.notifications.position,'#FFF','success','60000');
+        }).fail(function(xhr) {
+            console.error("Organizr Function: Reboot Failed");
+        });
+    }
+}
 function updateNow(){
+    clearAJAX();
     if(activeInfo.settings.misc.docker){
         dockerUpdate();
         return false;
@@ -3243,7 +3369,14 @@ function updateNow(){
 	});
 }
 function organizrAPI(type,path,data=null){
-	//console.log('Organizr API: Calling API: '+path);
+	var timeout = 10000;
+    switch(path){
+        case 'api/?v1/windows/update':
+            timeout = 120000;
+            break;
+        default:
+            timeout = 10000;
+    }
 	switch (type) {
 		case 'get':
 		case 'GET':
@@ -3254,12 +3387,13 @@ function organizrAPI(type,path,data=null){
 				beforeSend: function(request) {
 					request.setRequestHeader("Token", activeInfo.token);
 				},
-				timeout: 10000,
+				timeout: timeout,
 			});
 			break;
 		case 'post':
 		case 'POST':
 		case 'p':
+		    data.formKey = local('g','formKey');
 			return $.ajax({
 				url:path,
 				method:"POST",
@@ -3281,12 +3415,12 @@ function githubVersions() {
 }
 function sponsorsJSON() {
     return $.ajax({
-        url: "https://raw.githubusercontent.com/causefx/Organizr/"+activeInfo.branch+"/js/sponsors.json",
+        url: "https://raw.githubusercontent.com/causefx/Organizr/v2-develop/js/sponsors.json",
     });
 }
 function newsJSON() {
     return $.ajax({
-        url: "https://raw.githubusercontent.com/causefx/Organizr/"+activeInfo.branch+"/js/news.json",
+        url: "https://raw.githubusercontent.com/causefx/Organizr/v2-develop/js/news.json",
     });
 }
 function getLatestCommitJSON() {
@@ -5214,7 +5348,7 @@ function homepageDownloader(type, timeout){
 	}).fail(function(xhr) {
 		console.error("Organizr Function: API Connection Failed");
 	});
-	var timeoutTitle = type+'-Downloader';
+	var timeoutTitle = type+'-Downloader-Homepage';
 	if(typeof timeouts[timeoutTitle] !== 'undefined'){ clearTimeout(timeouts[timeoutTitle]); }
 	timeouts[timeoutTitle] = setTimeout(function(){ homepageDownloader(type,timeout); }, timeout);
 }
@@ -5243,7 +5377,7 @@ function homepageStream(type, timeout){
 	}).fail(function(xhr) {
 		console.error("Organizr Function: API Connection Failed");
 	});
-	var timeoutTitle = type+'-Stream';
+	var timeoutTitle = type+'-Stream-Homepage';
 	if(typeof timeouts[timeoutTitle] !== 'undefined'){ clearTimeout(timeouts[timeoutTitle]); }
 	timeouts[timeoutTitle] = setTimeout(function(){ homepageStream(type,timeout); }, timeout);
 }
@@ -5281,7 +5415,7 @@ function homepageRecent(type, timeout){
 	}).fail(function(xhr) {
 		console.error("Organizr Function: API Connection Failed");
 	});
-	var timeoutTitle = type+'-Recent';
+	var timeoutTitle = type+'-Recent-Homepage';
 	if(typeof timeouts[timeoutTitle] !== 'undefined'){ clearTimeout(timeouts[timeoutTitle]); }
 	timeouts[timeoutTitle] = setTimeout(function(){ homepageRecent(type,timeout); }, timeout);
 }
@@ -5341,8 +5475,8 @@ function homepageRequests(timeout){
 	}).fail(function(xhr) {
 		console.error("Organizr Function: API Connection Failed");
 	});
-	if(typeof timeouts['ombi'] !== 'undefined'){ clearTimeout(timeouts['ombi']); }
-	timeouts['ombi'] = setTimeout(function(){ homepageRequests(timeout); }, timeout);
+	if(typeof timeouts['ombi-Homepage'] !== 'undefined'){ clearTimeout(timeouts['ombi-Homepage']); }
+	timeouts['ombi-Homepage'] = setTimeout(function(){ homepageRequests(timeout); }, timeout);
 }
 function testAPIConnection(service){
     messageSingle('',' Testing now...',activeInfo.settings.notifications.position,'#FFF','info','10000');
@@ -5386,8 +5520,8 @@ function homepageCalendar(timeout){
 	}).fail(function(xhr) {
 		console.error("Organizr Function: API Connection Failed");
 	});
-	if(typeof timeouts['calendar'] !== 'undefined'){ clearTimeout(timeouts['calendar']); }
-	timeouts['calendar'] = setTimeout(function(){ homepageCalendar(timeout); }, timeout);
+	if(typeof timeouts['calendar-Homepage'] !== 'undefined'){ clearTimeout(timeouts['calendar-Homepage']); }
+	timeouts['calendar-Homepage'] = setTimeout(function(){ homepageCalendar(timeout); }, timeout);
 }
 // Thanks Swifty!
 function PopupCenter(url, title, w, h) {
@@ -5557,7 +5691,13 @@ function clearAJAX(id='all'){
 		$.each(timeouts, function(i,v) {
 			clearTimeout(timeouts[i]);
 		});
-	}else{
+	}else if(id == 'homepage'){
+        $.each(timeouts, function(i,v) {
+            if(i.indexOf('-Homepage') > 0 ){
+                clearTimeout(timeouts[i]);
+            }
+        })
+    }else{
 		clearTimeout(timeouts[id]);
 	}
 }

+ 14 - 0
js/version.json

@@ -138,5 +138,19 @@
     "new": "",
     "fixed": "",
     "notes": "Please report bugs in GitHub issues page"
+  },
+  "2.0.20": {
+    "date": "2019-03-04 23:00",
+    "title": "Security Fixes",
+    "new": "",
+    "fixed": "Security issue around CRFS and file upload and delete (#1086)|Clicking on iCal Calendar Item Locks Up GUI (#1092)|Custom js and css ace boxes not encoding correctly|Possibility for co-admin to become admin|Branch being disabled",
+    "notes": "Please report bugs in GitHub issues page"
+  },
+  "2.0.32": {
+    "date": "2019-03-05 19:50",
+    "title": "Auto-Reload and Auto-Close",
+    "new": "Auto Close and Auto reload feature (#1071)",
+    "fixed": "Undefined index dbLocation (#1093)|Disable show update to new version if on docker|Ombi sso only works with full email as user id (#1047)|Clear ajax calls if homepage is closed|Hardcoded news and sponsors branch",
+    "notes": "Please report bugs in GitHub issues page"
   }
 }

+ 2 - 2
plugins/bower_components/calendar/dist/fullcalendar.js

@@ -6696,7 +6696,7 @@ DayGrid.mixin({
 				(htmlEscape(event.imagetype || '') || '&nbsp;') + // we always want one line of height
 			'"></i></span>';
 
-		return '<a class="inline-popups ' + classes.join(' ') + '"' +
+		return '<a class=" ' + classes.join(' ') + '"' +
 				(event.id ?
 					' data-effect="mfp-zoom-out" data-target="'+ htmlEscape(event.id) +'" data-details="'+htmlEscape(detailsJSON) +'" data-mfp-src="#' + htmlEscape(event.id) + '"' :
 					''
@@ -14571,7 +14571,7 @@ var ListViewGrid = Grid.extend({
 				(htmlEscape(event.imagetype || '') || '&nbsp;') + // we always want one line of height
 			'"></i></span>';
 
-		return '<a class="inline-popups ' + classes.join(' ') + '"' +
+		return '<a class=" ' + classes.join(' ') + '"' +
 				(event.id ?
 					' data-effect="mfp-zoom-out" data-target="'+ htmlEscape(event.id) +'" data-details="'+htmlEscape(detailsJSON) +'" data-mfp-src="#' + htmlEscape(event.id) + '"' :
 					''

+ 89 - 0
scripts/windows-update.bat

@@ -0,0 +1,89 @@
+@ECHO off
+SET ou_v=v2.6
+TITLE Organizr v2 Updater
+COLOR 03
+ECHO      ___           ___
+ECHO     /  /\         /  /\           ___
+ECHO    /  /::\       /  /:/_         /__/\
+ECHO   /  /:/\:\     /  /:/ /\        \__\:\
+ECHO  /  /:/  \:\   /  /:/ /:/_       /  /::\
+ECHO /__/:/ \__\:\ /__/:/ /:/ /\   __/  /:/\/
+ECHO \  \:\ /  /:/ \  \:\/:/ /:/  /__/\/:/
+ECHO  \  \:\  /:/   \  \::/ /:/   \  \::/
+ECHO   \  \:\/:/     \  \:\/:/     \  \:\
+ECHO    \  \::/       \  \::/       \__\/
+ECHO     \__\/         \__\/             ~~ %ou_v%
+ECHO.
+ECHO Organizr v2 Updater
+ECHO.
+@ECHO Started: %date% %time%
+ECHO Running from: %~dp0
+ECHO.
+CD /d %~dp0
+
+IF "%*"=="" GOTO :master_vars
+IF "%*"=="-m" GOTO :master_vars
+IF "%*"=="-d" GOTO :dev_vars
+
+:master_vars
+ECHO Master Branch
+SET branch=Master
+SET org_url=https://github.com/causefx/Organizr/archive/v2-master.zip
+SET orgzip_extract_name=Organizr-2-master
+GOTO :STARTUPDATE
+
+:dev_vars
+ECHO Dev Branch
+SET branch=Dev
+SET org_url=https://github.com/causefx/Organizr/archive/v2-develop.zip
+SET orgzip_extract_name=Organizr-2-develop
+GOTO :STARTUPDATE
+
+:STARTUPDATE
+REM CD /d %~dp0
+ECHO.
+IF NOT EXIST "%~dp0organizr" GOTO UPDATE
+ECHO ##############################
+ECHO Cleanup in progress
+ECHO ##############################
+RMDIR /s /q %~dp0organizr
+ECHO.
+ECHO Deleted
+ECHO.
+
+:UPDATE
+ECHO #############################
+ECHO Updating OrganizrV2-(%branch%)
+ECHO #############################
+ECHO.
+ECHO.
+ECHO Download In Progress...
+powershell -command "$clnt = new-object System.Net.WebClient; $clnt.DownloadFile(\"%org_url%\", \"organizr.zip\")"
+ECHO.
+
+ECHO Extraction In Progress...
+ECHO.
+powershell.exe -nologo -noprofile -command "& { Add-Type -A 'System.IO.Compression.FileSystem'; [IO.Compression.ZipFile]::ExtractToDirectory('organizr.zip', '.'); }"
+
+ECHO Applying Update...
+ECHO.
+MOVE %~dp0%orgzip_extract_name% organizr >nul 2>&1
+DEL /s /q %~dp0organizr.zip
+ROBOCOPY organizr ..\ /E /MOVE /NFL /NDL /NJH /nc /ns /np
+
+IF NOT EXIST "%~dp0organizr" GOTO END
+ECHO ##############################
+ECHO Cleanup in progress
+ECHO ##############################
+RMDIR /s /q %~dp0organizr
+ECHO.
+ECHO Deleted
+
+:END
+ECHO.
+ECHO %branch% Update Completed...
+
+ECHO.
+@ECHO ENDED: %date% %time%
+ECHO.
+REM pause

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