Location: PHPKode > projects > ProjectPress > projectpress/installer/assets/helper.functions.php
<?php if(!defined('INST_BASEDIR')) die('Direct access is not allowed!');

    /* ====================================================================
    *
    *                            PHP Setup Wizard
    *
    *                         -= HELPER FUNCTIONS =-
    *
    *  Do not change anything in this file unless you are certain what
    *  you are doing. Any modifycations here might break the installer!
    *
    *  ================================================================= */


	
	
	/* =========================[ SESSION FUNCTIONS ]========================= */

	/* --------------------( Database Installation Status )------------------- */

	/** 
	 *  The progress of executing SQL queries in a database are set outside this 
	 *  helper, and the installer should not handle any sessions. So, this method 
	 *  is a work-around to allow the installer to update a session without direct 
	 *  manipulation. A database status is set by status message and a prefix
	 */
	function SetDatabaseInstallStatus($database, $state, $prefix='', $successCount=0, $failedCount=0) 
	{
		global $config;
		$ID = $config['session_prefix'];
		$sql = 'dbinstall';

		// Set the session to empty array if not set
		if(!isset($_SESSION[$ID][$sql])) 
			$_SESSION[$ID][$sql] = array();

		// Set a status for a given database (name)
		$_SESSION[$ID][$sql][$database] = array('state'=>$state, 'prefix'=>$prefix, 'okaycount'=>$successCount, 'failcount'=>$failedCount);
	}

	/**
	 *  Get the list of databases or some spesific database only, and their
	 *  installation status. This is a work-around function to allow installer
	 *  to access data directly from sessions
	 */
	function GetDatabaseInstallStatus($database='') 
	{
		global $config;
		$ID = $config['session_prefix'];
		$sql = 'dbinstall';

		// Set the session to empty array if not set
		if(!isset($_SESSION[$ID][$sql])) 
			$_SESSION[$ID][$sql] = array();

		// If get all database statuses
		if($database == '')
			return $_SESSION[$ID][$sql];

		// If the database has been spesified - return only its status
		else if(array_key_exists($database, $_SESSION[$ID][$sql]))
			return $_SESSION[$ID][$sql][$database];

		// The database does not have a status yet
		else
			return false;
	}

	/**
	 *  Remove some spesific database from sessions, rendering it
	 *  "unknown" or available for installation again. Used when a
	 *  database has a successful install but does not exist, thus
	 *  needs to be removed from sessions
	 */ 
	function RemoveDatabaseInstallStatus($database)
	{
		global $config;
		$ID = $config['session_prefix'];
		$sql = 'dbinstall';

		if(array_key_exists($database, $_SESSION[$ID][$sql]))
			unset($_SESSION[$ID][$sql][$database]);
	}

	/**
	 *  Reset installation session
	 */
	function ResetDatabaseInstallStatus()
	{
		global $config;
		$ID = $config['session_prefix'];
		$sql = 'dbinstall';

		unset($_SESSION[$ID][$sql]);
	}

	
	/* --------------------( Table Prefix )------------------- */
	
	/**
	 *  Set the prefix value to sessions. The installer might need to update
	 *  the session outside the prefix step.
	 */ 
	function SetSessionPrefix($prefix='')
	{
		global $steps;
		global $config;
		$ID = $config['session_prefix'];
		$connect = 'connection'; 
		$key = 'dbprefix';

		// If a separator must be added at the end of the prefix
		if(strlen($steps[STEP_DBPREFIX]['separator']) > 0)
		{
			// If the prefix is empty, and the separator is not
			// optional - the prefix becomes the separator
			if(strlen($prefix) == 0)
			{
				if(!$steps[STEP_DBPREFIX]['optional'])
					$prefix = $steps[STEP_DBPREFIX]['separator'];
			}

			// Prefix has value, make sure the separator is not 
			// added multiple times at the end of it
			else
			{
				$serp = $steps[STEP_DBPREFIX]['separator'];
				$start = strlen($prefix) - strlen($serp); # where does the $serp string start in $prefix 
				$start = ($start < 0) ? 0 : $start; # make sure $start does not go below zero

				// If the last part of $prefix does not match the separator then add
				// the separator at the end of $prefix before storing it to session
				if(substr($prefix, $start, strlen($serp)) != $steps[STEP_DBPREFIX]['separator'])
					$prefix = $prefix.$steps[STEP_DBPREFIX]['separator'];
			}
		}

		// Finally set the prefix to session
		$_SESSION[$ID][$connect][$key] = $prefix;
	}


	/* --------------------( Administrator Account )------------------- */

	/**
	 *  Set if a administrator account exists, or has been approved
	 */ 
	function SetAdminAccountStatus($status='none') # success , failed
	{
		global $config;
		$ID = $config['session_prefix'];
		$admin = 'adminaccount';

		// Set the session to false if not set
		if(!isset($_SESSION[$ID][$admin])) 
			$_SESSION[$ID][$admin] = 'none';

		// Set a status for administrator creation
		$_SESSION[$ID][$admin] = $status;
	}

	/**
	 *  Get a administrator account status
	 */ 
	function GetAdminAccountStatus()
	{
		global $config;
		$ID = $config['session_prefix'];
		$admin = 'adminaccount';

		// Set the session to false if not set
		if(!isset($_SESSION[$ID][$admin])) 
			$_SESSION[$ID][$admin] = 'none';

		// Get the administrator creation status
		return $_SESSION[$ID][$admin];
	}

	/**
	 *  Get if an administrator account has been created or not
	 */
	function AdminAccountExists()
	{
		return (GetAdminAccountStatus() != 'none') ? true : false;
	}

	/**
	 *  Get if an administrator account has been created successfully
	 */
	function AdminAccountSuccess()
	{
		return (GetAdminAccountStatus() == 'success') ? true : false;
	}

	/**
	 *  Get if an administrator account failed to insert 
	 */
	function AdminAccountFailed()
	{
		return (GetAdminAccountStatus() == 'failed') ? true : false;
	}


	/* --------------------( Serial Key Confirmation )------------------- */

	/**
	 *  Verify that the entered serial key matches the one kept in 
	 *  the configuration file
	 */
	function IsSerialKeyMatch($serialInput)
	{
		/* IF YOU PLAN ON USING A WEBSERVICE FOR THIS - MODIFY THIS FUNCION TO DO SO!! */

		global $steps;

		if(is_array($steps[STEP_SERIALKEY]['serialkeys']))
			return in_array(strtoupper($serialInput), $steps[STEP_SERIALKEY]['serialkeys']);
		else
			return (strtoupper($serialInput) == strtoupper($steps[STEP_SERIALKEY]['serialkeys'])) ? true : false;
	}


	/* --------------------( Additional Information )------------------- */

	/**
	 *  Get additional step's control value that has been posted, or 
	 *  empty string if not set 
	 */
	function HasAnyAdditionalStepValueBeenPostedYet()
	{
		global $config;
		$ID = $config['session_prefix'];
		return is_array($_SESSION[$ID][STEP_ADDEDINFO]);
	}

	/**
	 *  Get additional step's control value that has been posted, or 
	 *  empty string if not set 
	 */
	function GetAdditionalControlValue($keywordName, $defaultValue)
	{
		global $config;
		$ID = $config['session_prefix'];
	
		// If the session contains this value - return it as string format	
		if(isset($_SESSION[$ID][STEP_ADDEDINFO][$keywordName]))
		{
			 if(strlen($_SESSION[$ID][STEP_ADDEDINFO][$keywordName]) > 0)
				  return strval($_SESSION[$ID][STEP_ADDEDINFO][$keywordName]);
			 else return strval($defaultValue);
		}

		// If it does not - get boolean "false" instead
		else 
			return false;
	}

	/**
	 *  Evaluate text input controls (DOM objects, components or what ever you like to call them)
	 *  on the page and display warning boxes if is invalid
	 */
	function HasTextInputErrors($data, $value)
	{
		global $page;		

		// Do not even evaluate if no value has yet to be posted
		if(!HasAnyAdditionalStepValueBeenPostedYet())
			return false;

		// Only check textboxes and textareas
		if(isset($data['type']) && ($data['type'] == 'textbox' || $data['type'] == 'textarea'))
		{
			// If the box must be filled with some value	
			$mustfill = (isset($data['mustfill']) && $data['mustfill'] === true) ? true : false;

			// Numeric value
			if(isset($data['numeric']) && $data['numeric'] === true)
			{
				// Minval defines the lowest accepted value, and maxval the maximum accepted value
				$minval = (isset($data['minval']) && is_numeric($data['minval'])) ? $data['minval'] : false;
				$maxval = (isset($data['maxval']) && is_numeric($data['maxval'])) ? $data['maxval'] : false;				
				
				// Check numeric bounds
				$errorMsg = IsNumericInBounds($value, $minval, $maxval, $mustfill);
				if($errorMsg == '')
					return false;
				else
				{
					$page->WarningBox($errorMsg);
					return true;
				}
			}


			// String value
			else
			{
				$MAX = 2147483647;

				// If minlen is set, it must be 0 or higher. 1 or higher if "mustfill" flag is enabled
				if($mustfill)
					 $minlen = (isset($data['minlen']) && $data['minlen'] >  0) ? $data['minlen'] : 1;
				else $minlen = (isset($data['minlen']) && $data['minlen'] >= 0) ? $data['minlen'] : false;

				// If maxlen is set, it must be equal-or-higher than minlen, othervice just very very hight number
				$maxlen = (isset($data['maxlen']) && $data['maxlen'] >= $minlen) ? $data['maxlen'] : false;

				// Check string bounds
				$errorMsg = IsStringInBounds($value, $minlen, $maxlen, $mustfill);
				if($errorMsg == '')
					return false;
				else
				{
					$page->WarningBox($errorMsg);
					return true;
				}
			}
		}
		
		return false;
	}

	/**
	 *  Is some numeric value within the min-max bounds
	 */
	function IsNumericInBounds($value, $minval, $maxval, $mustfill)
	{
		// If range is configured badly - ignore it!
		if($minval != false && $maxval != false && $minval > $maxval)
		{
			$minval = false;
			$maxval = false;
		}

		// Get if the ranges are set or not
		$minset = ($minval === false) ? false : true;
		$maxset = ($maxval === false) ? false : true;
		$orBlank = (!$mustfill) ? ', or keep this field blank' : '';

		// If there is some value, it must be numeric
		if(strlen($value) > 0 && !is_numeric($value))
			return 'The value <b>'.$value.'</b> is not numeric, please enter only numeric digits in this box';

		// No need to check anything if empty values are accepted
		else if(!$mustfill && strlen($value) == 0)
			return '';

		// If no range is defined, but there must be some value
		else if(!$minset && !$maxset)
		{
			if($mustfill && strlen($value) == 0)
				return 'This field must contain numeric value';
		}

		// If minimum is set, but not maximum
		else if($minset && !$maxset)
		{
			if($mustfill && strlen($value) == 0)
				return 'This field must contain numeric value, starting from <b>'.$minval.'</b>';

			else if($value < $minval)
				return 'The value must be equal or higher than <b>'.$minval.'</b>'.$orBlank;
		}

		// If maximum is set, but not minimum
		else if(!$minset && $maxset)
		{
			if($mustfill && strlen($value) == 0)
				 return 'This field must contain numeric value, up to maximum of <b>'.$maxval.'</b>';

			else if($value > $maxval)
				return 'The value must be equal or lower than <b>'.$maxval.'</b>'.$orBlank;
		}

		// If both limits are set
		else if($minset && $maxset)
		{
			if($mustfill && $minval == $maxval && $value != $minval)
				return 'This field only accepts the value <b>'.$minval.'</b>';

			else if($mustfill && strlen($value) == 0)
				 return 'This field must contain numeric value between <b>'.$minval.'</b> and <b>'.$maxval.'</b>';

			else if($value < $minval || $value > $maxval)
				return 'The value must be between <b>'.$minval.'</b> and <b>'.$maxval.'</b>'.$orBlank;
		}

		return '';
	}

	function IsStringInBounds($value, $minlen, $maxlen, $mustfill)
	{
		// If range is configured badly - ignore it!
		if($minlen != false && $maxlen != false && $minlen > $maxlen)
		{
			$minlen = false;
			$maxlen = false;
		}

		// Get if the ranges are set or not
		$minset = ($minlen === false) ? false : true;
		$maxset = ($maxlen === false) ? false : true;
		$valueLength = strlen($value);
		$orBlank = (!$mustfill) ? ', or keep this field blank' : '';


		// No need to check anything if empty values are accepted
		if(!$mustfill && $valueLength == 0)
			return '';

		// If no range is defined, but there must be some value
		else if(!$minset && !$maxset)
		{
			if($mustfill && $valueLength == 0)
				return 'This field cannot be empty';
		}

		// If minimum is set, but not maximum
		else if($minset && !$maxset)
		{
			if($mustfill && $valueLength == 0)
				return 'This field must contain text that is at least <b>'.$minlen.'</b> characters long';

			else if($valueLength < $minlen)
				return 'The current value is '.$valueLength.' long, but it must be at least <b>'.$minlen.'</b> characters long'.$orBlank;
		}

		// If maximum is set, but not minimum
		else if(!$minset && $maxset)
		{
			if($mustfill && $valueLength == 0)
				 return 'This field must contain text that is equal or below <b>'.$maxlen.'</b> characters long';

			else if($valueLength > $maxlen)
				return 'The current value is '.$valueLength.' long, but it cannot be longer than <b>'.$maxlen.'</b> characters long'.$orBlank;
		}

		// If both limits are set
		else if($minset && $maxset)
		{
			if($mustfill && $minlen == $maxlen && $valueLength != $minlen)
				return 'This field only accepts values of length <b>'.$minlen.'</b>, current length is '.$valueLength;

			else if($mustfill && $valueLength == 0)
				 return 'This field must contain value of length between <b>'.$minlen.'</b> and <b>'.$maxlen.'</b>, current is '.$valueLength;

			else if($valueLength < $minlen || $valueLength > $maxlen)
				return 'The current value is '.$valueLength.' long, but it must be between <b>'.$minlen.'</b> and <b>'.$maxlen.'</b> characters long'.$orBlank;
		}

		return '';
	}




	/* =========================[ $STEPS FUNCTIONS ]========================= */

	/**
	 *  Get the next step index key, taking configuration into account
	 *  NOTE: false is returned if there is none
	 */
	function GetNextStep($stepKey)
	{
		global $steps;

		$next = false;
		$breakNext = false;
		foreach($steps as $key=>$step)
		{
			$next = $key;

			if($breakNext)
			{
				if( (isset($steps[$next]['enabled']) && !$steps[$next]['enabled']) || 
					(isset($steps[$next]['ishidden']) && $steps[$next]['ishidden']) )
					 continue;
				else break;
			}

			if($stepKey == $key)
				$breakNext = true;
		}
		return $next;
	}

	/**
	 *  Get the previous step index key, taking configuration into account.
	 *  NOTE: false is returned if there is none
	 */
	function GetPrevStep($stepKey)
	{
		global $steps;

		$prev = false;
		$breakNext = false;

		end($steps);
		while($current = prev($steps))
		{
			$prev = key($steps);

			if($breakNext)
			{
				if( (isset($steps[$prev]['enabled']) && !$steps[$prev]['enabled']) || 
					(isset($steps[$prev]['ishidden']) && $steps[$prev]['ishidden']) )
					 continue;
				else break;
			}

			if($stepKey == key($steps))
				$breakNext = true;
		}

		// Return false if the same as current
		if($prev == $stepKey) 
			 return false;

		// If the while ended and the $prev value
		// is set to disabled or ishidden step, then
		// return false!
		if( (isset($steps[$prev]['enabled']) && !$steps[$prev]['enabled']) || 
			(isset($steps[$prev]['ishidden']) && $steps[$prev]['ishidden']) )
			return false;
		
		// The step must be valid then :)
		else 
			return $prev;
	}


	/* =========================[ ENCRYPTION / DECRYPTION ]========================= */

	/**
	 *  Encrypt string
	 */
	function Encrypt($string) 
	{
		if(IsExtensionInstalled('mcrypt'))
		{
			global $config;
			return mcrypt_ecb(MCRYPT_BLOWFISH, $config['encryption_key'], $string, MCRYPT_ENCRYPT);
		}
		return $string;
	}

	/**
	 *  Decrypt string
	 */
	function Decrypt($string) 
	{
		if(IsExtensionInstalled('mcrypt'))
		{
			global $config;
			return mcrypt_ecb(MCRYPT_BLOWFISH, $config['encryption_key'], $string, MCRYPT_DECRYPT);
		}
		return $string;
	}


	/* =========================[ READ/WRITEABLE TESTS ]========================= */

	/** 
	 *  Test if some folder can be created if it does not exist, and then check if it is writeable or not
	 */
	function TestFolderWriteability($folderPath='')
	{
		global $page;

		if(strlen($folderPath) == 0)
			return TestResults('ignored', 'There is no folder to check');	

		else if(is_dir($folderPath))
		{
			if(is_writeable($folderPath))
				 return TestResults('success', 'Config folder <b>'.$folderPath.'</b> is writeable');
			else return TestResults('failed',  'Config folder <b>'.$folderPath.'</b> is not writeable');
		}
		else
		{
			if(mkdir($folderPath))
			{
				if(is_writeable($folderPath))
					 return TestResults('success', 'Config folder <b>'.$folderPath.'</b> has been created successfully, and it is writeable');
				else return TestResults('failed',  'Config folder <b>'.$folderPath.'</b> has been created successfully, but it is not writeable');
			}
			else 
			{
				$page->ErrorBox('The Installer is unable to create the folder <b>'.$folderPath.'</b>, check your <tt>chmod</tt> '.
							    'permissions or contact support to get this issue resolved.');
				return TestResults('failed', 'Unable to create the folder <b>'.$folderPath.'</b>');
			}
		}
	}

	/** 
	 *  Test if some mask file can be read or not
	 */
	function TestFileReadability($maskname)
	{
		global $mask;
		global $page;
		global $config;

		// Test the read ability (very likely to pass in most cases)
		$maskContent = $mask->GetMask($maskname);
		if($maskContent === false)
			return TestResults('failed', 'The mask file <b>'.$maskname.'</b> cannot be found! Error in installer setup!');
		else if(strlen($maskContent) > 0)
			return TestResults('success', 'The mask file for <b>'.$maskname.'</b> is readable');
		else
		{
			$page->ErrorBox('The Installer is unable to read the mask file <b>'.$config['mask_folder_name']. 
							DIRECTORY_SEPARATOR.$maskname.'</b>, check your <tt>chmod</tt> '.
							'permissions or contact support to get this issue resolved.');
			return TestResults('failed', 'Unable to read the mask of <b>'.$maskname.'</b>');
		}
	}

	/** 
	 *  Test if a file can be written to or not
	 */
	function TestFileWriteability($folderPath, $fileName)
	{
		global $mask;
		global $page;		

		if(file_put_contents($folderPath.$fileName, "Installer: can I write to a file... "))
			return TestResults('success', 'The test-file <b>'.$fileName.'</b> was written too successfully');
		else
		{
			$page->ErrorBox('The Installer is unable to create and write to <b>'.$folderPath.$fileName.'</b>, check your <tt>chmod</tt> '.
							'permissions or contact support to get this issue resolved.');

			return TestResults('failed', 'Unable to create the test-file <b>'.$fileName.'</b>');
		}
	}

	/** 
	 *  Test if a file can be deleted or not
	 */
	function TestFileDeletion($folderPath, $fileName)
	{
		if(unlink($folderPath.$fileName))
			return TestResults('success', 'The test-file <b>'.$fileName.'</b> has been removed');
		else
		{			
			$page->ErrorBox('The Installer is unable to delete <b>'.$folderPath.$fileName.'</b>, check your <tt>chmod</tt> permissions or contact '.
							'support to get this issue resolved.');

			return TestResults('failed', 'Unable to delete the test-file <b>'.$fileName.'</b>');
		}
	}


	/**
	 *  Get test results in custom array
	 */
	function TestResults($state, $msg)
	{
		return array(
				'state' => $state,
				'msg'   => $msg);
	}


	/* =========================[ FINAL CONFIG FUNCTIONS ]========================= */

	/** 
	 *  Combine the final output path and the config filename. This is important
	 *  method because if broken - the installer will not function
	 */
	function FixPath($configSavePath='')
	{
		// Do not check slashes if value is empty
		$configSavePath = trim($configSavePath);
		if(strlen($configSavePath) == 0)
			return '';

		// If for some reason the Php does not support both types of slashes,
		// this small clean-up script is to fix all slashes such that they
		// are all facing the same way - and the same way as DIRECTORY_SEPARATOR,
		// and if there are two slashes together, replace them with a single one
		$slashes = array("\\","/","\\\\","//");
		foreach($slashes as $slash)
			$configSavePath = str_replace($slash, DIRECTORY_SEPARATOR, $configSavePath);

		// If the 'savetofolder' does not end with a slash, add it!
		if(substr($configSavePath, -1, 1) != DIRECTORY_SEPARATOR)
			$configSavePath .= DIRECTORY_SEPARATOR;

		return $configSavePath;
	}
	
	/** 
	 *  Does the final output config file exist or not. If in $steps, the setting 
	 *  'updateonzero' is enabled - this method will check if the config has 
	 *  zero bytes or not as well.
	 */
	function IsInstallerDone()
	{
		global $steps;

		$isDone = true;

		foreach($steps[STEP_WRITECONFIG]['configs'] as $file)
		{
			$savepath = FixPath($file['savetofolder']);

			// Directory exists - check the output config file
			if(is_dir($savepath) || strlen($savepath) == 0)
			{
				// Update savepath to include the filename and get 
				// "onzero" setting to variable for simpler code
				$savepath = $savepath.$file['maskname'];
				$updateOnZero = $steps[STEP_WRITECONFIG]['updateonzero'];				
			
				// The config does not exist, check next config or continue with installing
				if(!is_file($savepath))
				{
					$isDone = false;
					break;
				}
				
				// If the config does exists, and file should NOT be
				// updated if it contains zero bytes - we are done!
				else if(is_file($savepath) && !$updateOnZero)
					continue;

				// The output config file does exist, but we only continue
				// if the file does only contain zero bytes
				else
				{
					$bytes = filesize($savepath);
					if ($bytes == 0)
					{
						$isDone = false;
						break;
					}
					else 
						continue;
				}
			}
			else
			{
				$isDone = false;
				break;
			}
		}

		return $isDone;
	}

	/**
	 *  Delete the final output config and return a boolean
	 *  notifing if it was successful or not
	 */
	function DeleteFinalOutputConfig()
	{
		global $steps;

		// Get the name of final output name and path folder
		$folder = FixPath($steps[STEP_WRITECONFIG]['savetofolder']);
		$file = trim($steps[STEP_WRITECONFIG]['maskname']);

		// If deletion was successful
		if(is_file($folder.$file) && unlink($folder.$file))
			 return true;
		else return false;
	}


	/* =========================[ VERSION AND EXTENSIONS ]========================= */

	/**
	 *  Is some extensions loaded or not
	 */
	function IsExtensionInstalled($moduleName)
	{
		// The faster "less-reliable" alternative which is not used because
		// a module (or extension) names could be in different casing, so
		// 'Mysql' should be approved even though only 'mysql' is loaded		
		## return extension_loaded($moduleName);

		// Set the module name to lower case and get all loaded extensions 
		$moduleName = strtolower($moduleName);
		$extensions = get_loaded_extensions();
		foreach($extensions as $ext)
		{
			if($moduleName == strtolower($ext))
				return true;
		}

		return false;
	}

	/**
	 *  Convert version string to parts array
	 */
	function VersionStringToArray($anyVersion, $paddedValue='x')
	{
		$anyVersion = str_replace('x', $paddedValue, $anyVersion);
		$parts = explode('.', $anyVersion);
		while(count($parts) < 3)
			$parts[] = $paddedValue;
		return $parts;
	}

	/**
	 *  Get version bounds from two version strings, that can be on the
	 *  format '5', '5.2', '5.x.x' even be 'false'
	 */
	function GetVersionBounds($minimumVersion, $maximumVersion)
	{
		$current = VersionStringToArray(phpversion()); # PHP ALWAYS HAS FULL VERSION STRING!!
		$minimum = VersionStringToArray($minimumVersion, '0');

		// Any higher version is accepted!
		if($maximumVersion === false)
		{
			return array(
				'current' => $current,
				'lower' => $minimum,
				'upper' => VersionStringToArray('99.99.99'));
		}

		// If max version is defined - check within range
		else if($maximumVersion !== false)
		{
			return array(
				'current' => $current,
				'lower' => $minimum,
				'upper' => VersionStringToArray($maximumVersion, '99'));
		}

		// Only validate the defined minimum version string (which can NEVER
		// occur because the first if catches FALSE, and the other if catches
		// ANY other state - so this is kept here for future versions if needed
		else 
		{
			// Create upper bounds from the required version, such that
			// any undefined values will be maximum
			return array(
				'current' => $current,
				'lower' => $minimum,
				'upper' => VersionStringToArray($minversion, '99'));
		}
	}

	/**
	 *  Check if version is within bounds of lower and upper, in mathematical
	 *  sens it would be:  $lowerBound <= $currentVersion < $upperBound
	 */
	function VersionInBounds($currentVersion, $lowerBound, $upperBound)
	{
		$CurLow = CompareVersons($currentVersion, $lowerBound);
		$CurHig = CompareVersons($currentVersion, $upperBound);

		// If current version is equal or higher than lower bound
		// and is below the upper bound - then accepted!
		if ($CurLow >= 0 && $CurHig < 0)
			 return true;
		else return false;
	}
	
	/** 
	 *  Compare two versions and get which is higher
	 *  returns are:  A<B : -1, A=B : 0, A>B : 1
	 */
	function CompareVersons($versionA, $versionB)
	{
		if($versionA[0] < $versionB[0]) 
			return -1;
		else if($versionA[0] == $versionB[0])
		{
			if($versionA[1] < $versionB[1]) 
				return -1;
			else if($versionA[1] == $versionB[1])
			{
				if($versionA[2] < $versionB[2]) 
					return -1;
				else if($versionA[2] == $versionB[2])
					return 0;
				else if($versionA[2] > $versionB[2])
					return 1;
			}
			else if($versionA[1] > $versionB[1])
				return 1;
		}
		else if($versionA[0] > $versionB[0])
			return 1;
	}


	/* =========================[ MISC FUNCTIONS ]========================= */

	/** 
	 *  Does the given input only contain numeric digits from 0 to 9  
	 */
	function IsNumericOnly($input)
	{
		/*  NOTE: The PHP function "is_numeric()" evaluates "1e4" to true
		 *        and "is_int()" only evaluates actual integers, not 
		 *        numeric strings. */

		return preg_match("/^[0-9]*$/", $input);
	}

	function GetAsRed($string, $inBold=false)
	{
		return GetAsColor($string, 'FF0000', $inBold);
	}

	function GetAsGreen($string, $inBold=false)
	{
		return GetAsColor($string, '279B00', $inBold);
	}

	function GetAsBlue($string, $inBold=false)
	{
		return GetAsColor($string, '0000FF', $inBold);
	}

	function GetAsOrange($string, $inBold=false)
	{
		return GetAsColor($string, 'FF9933', $inBold);
	}

	function GetAsColor($string, $colorHex, $inBold)
	{
		$string = ($string === false || $string === 0) ? '0' : $string;
		if($inBold) $string = '<b>'.$string.'</b>';
		return '<span style="color:#'.$colorHex.'">'.$string.'</span>';
	}
Return current item: ProjectPress