';
		echo GetMessages();
		echo $this->contentBuffer;
		echo '';
	}
	function __construct($process='page'){
		$this->GetData();
		//called from admin_tools.php
		if( $process == 'embededcheck' ){
			$this->DoRemoteCheck();
			header('content-type: image/gif');
			echo base64_decode('R0lGODlhAQABAIAAAAAAAAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=='); // send 1x1 transparent gif
			die();
		}
		ob_start();
		$this->Run();
		$this->contentBuffer = ob_get_clean();
	}
	function Run(){
		if( !$this->CheckPHP() ){
			echo $this->output_phpcheck;
			return;
		}
		$cmd = \gp\tool::GetCommand();
		$show = true;
		switch($cmd){
			case 'checkremote':
				$this->DoRemoteCheck(true);
			break;
			case 'update';
				if( gp_remote_update && $this->UpdateCMS() ){
					$show = false;
				}
			break;
		}
		if( $show ){
			$this->ShowStatus();
			echo '
Status
';
			$this->CheckStatus();
			echo $this->output_phpcheck;
		}
	}
	/**
	 * $update_data['packages'][id] = array()
	 *
	 *		array['id']			= id of addon (unique across all types), "core" if type is "core"
	 *		array['type']		= [core,plugin,theme]
	 *		array['md5']		= expected md5 sum of zip file
	 *		array['file']		= file on local system
	 *		array['version']	= version of the package
	 *
	 */
	function GetData(){
		$this->data_timestamp	= \gp\admin\Tools::VersionData($update_data);
		$this->update_data		= $update_data;
		if( isset($this->update_data['packages']['core']) ){
			$this->core_package =& $this->update_data['packages']['core'];
		}
	}
	function CheckPHP(){
		global $dataDir, $langmessage;
		ob_start();
		$passed = true;
		echo '';
		echo '| ';
		echo $langmessage['Test'];
		echo ' | ';
		echo $langmessage['Value'];
		echo ' | ';
		echo $langmessage['Expected'];
		echo ' | 
';
		// RemoteGet
		echo '| ';
		echo 'RemoteGet';
		echo ' | ';
		if( \gp\tool\RemoteGet::Test() !== false ){
			echo ''.$langmessage['True'].'';
		}else{
			$passed = false;
			echo ''.$langmessage['False'].'';
		}
		echo ' | ';
		echo $langmessage['True'];
		echo ' | 
';
		//root installation
		echo '| ';
		echo 'Root Installation';
		echo ' | ';
		if( !defined('multi_site_unique') ){
			echo ''.$langmessage['True'].'';
		}else{
			echo ''.$langmessage['False'].'';
			if( gpdebug ){
				msg('This feature is not normally available in a multi-site installation.
						It is currently accessible because gpdebug is set to true.
						Continuing is not recommended.');
			}else{
				$passed = false;
			}
		}
		echo ' | ';
		echo $langmessage['True'];
		echo ' | 
';
		echo '
';
		if( !$passed ){
			echo '';
			echo $langmessage['Server_isnt_supported'];
			echo '
';
		}
		$this->output_phpcheck = ob_get_clean();
		return $passed;
	}
	function CheckStatus(){
		global $langmessage;
		$diff = time() - $this->data_timestamp;
		if( $this->data_timestamp > 0 ){
			echo '';
			echo sprintf($langmessage['Software_updates_checked'],\gp\tool::date($langmessage['strftime_datetime'],$this->data_timestamp));
			echo '
';
		}
		//one hour old
		if( $diff > 3600 ){
			echo '';
			echo ''.$langmessage['Check Now'].'';
			echo '
';
		}
	}
	function DoRemoteCheck($ForceCheck = false){
		global $langmessage;
		$diff = time() - $this->data_timestamp;
		//604800 one week
		if( !$ForceCheck && ($diff < 604800) ){
			return;
		}
		if( !$this->DoRemoteCheck2() ){
			msg($langmessage['check_remote_failed']);
		}else{
			msg($langmessage['check_remote_success']);
			$this->data_timestamp = time();
		}
		\gp\admin\Tools::VersionData($this->update_data);
	}
	/**
	 * Update available package information
	 *
	 */
	function DoRemoteCheck2(){
		global $config, $dataDir;
		$path = \gp\tool::IdUrl();
		//add any locally available themes with addon ids
		$dir = $dataDir.'/themes';
		$themes = scandir($dir);
		$theme_ids = array();
		foreach($themes as $name){
			if( $name == '.' || $name == '..' ){
				continue;
			}
			$full_dir		= $dir.'/'.$name;
			if( !is_dir($full_dir) ){
				continue;
			}
			$templateFile	= $full_dir.'/template.php';
			$ini_file		= $full_dir.'/Addon.ini';
			if( !file_exists($templateFile) ){
				continue;
			}
			$ini_info = array();
			if( file_exists($ini_file) ){
				$ini_info = \gp\tool\Ini::ParseFile($ini_file);
			}
			if( isset($ini_info['Addon_Unique_ID']) ){
				$theme_ids[] = $ini_info['Addon_Unique_ID'];
			}
		}
		$theme_ids  = array_unique($theme_ids );
		if( count($theme_ids) ){
			$path .= '&th='.implode('-',$theme_ids );
		}
		//get data
		$result = \gp\tool\RemoteGet::Get_Successful($path);
		if( !$result ){
			$this->msg(\gp\tool\RemoteGet::Debug('Sorry, data not fetched'));
			return false;
		}
		//zipped data possible since 4.1
		/*
		if( function_exists('gzinflate') ){
			$temp = gzinflate($result);
			if( $temp ){
				$result = $temp;
			}
		}
		*/
		$result = trim($result);
		$array	= json_decode($result, true); //json since 4.1
		if( !is_array($array) ){
			$debug						= array();
			$debug['Type']				= gettype($array);
			$debug['json_last_error']	= json_last_error();
			$debug['Two']				= substr($result,0,20);
			$this->msg(\gp\tool\RemoteGet::Debug('Sorry, data not fetched',$debug));
			return false;
		}
		if( !$array ){
			$debug				= array();
			$debug['Count']		= count($array);
			$debug['Two']		= substr($result,0,20);
			$this->msg(\gp\tool\RemoteGet::Debug('Sorry, data not fetched',$debug));
			return false;
		}
		$this->update_data['packages'] = array();
		foreach($array as $info){
			$id =& $info['id'];
			if( !is_numeric($id) ){
				continue;
			}
			if( !isset($info['type']) ){
				continue;
			}
			if( $info['type'] == 'core' ){
				$id = 'core';
			}
			$this->update_data['packages'][$id] = $info;
		}
		if( isset($this->update_data['packages']['core']) ){
			$this->core_package = $this->update_data['packages']['core'];
		}
		return true;
	}
	function ShowStatus(){
		global $langmessage;
		if( !$this->core_package ){
			return;
		}
		echo '';
		if( version_compare(gpversion,$this->core_package['version'],'<') ){
			echo '
';
			echo $langmessage['New_version_available'];
			echo '   ';
			echo '';
			if( gp_remote_update ){
				echo '
 » '.$langmessage['Update_Now'].' « ';
			}else{
				echo 'Remote Updating is not available';
			}
			echo '
';
			echo '';
			echo '| '.$langmessage['Your_version'].' | ';
			echo ''.gpversion.' | ';
			echo '
';
			echo '';
			echo '| '.$langmessage['New_version'].' | ';
			echo ''.$this->core_package['version'].' | ';
			echo '
';
			echo '
';
		}else{
			echo $langmessage['UP_TO_DATE'];
			echo '
'.$langmessage['Your_version'];
			echo '  '.gpversion;
			echo '
';
			echo '
';
			echo \gp\tool::link('',$langmessage['return_to_your_site']);
			echo '
';
			$this->RemoveUpdateMessage();
		}
		echo '
 ';
	}
	function UpdateCMS(){
		global $langmessage;
		if( !$this->core_package ){
			echo $langmessage['OOPS'].' (Core Package Not Set)';
			return false;
		}
		if( isset($_POST['step']) ){
			$this->curr_step = (int)$_POST['step'];
		}
		//already up to date?
		if( ($this->curr_step < 4) && version_compare(gpversion,$this->core_package['version'],'>=') ){
			msg($langmessage['UP_TO_DATE']);
			return false;
		}
		//filesystem
		$filesystem_method = $this->DetectFileSystem();
		if( !$filesystem_method ){
			msg('Update Aborted: Could not establish a file writing method compatible with your server.');
			return false;
		}
		$this->Steps();
		echo '';
		return true;
	}
	/**
	 * Display the update steps and current
	 *
	 */
	protected function Steps(){
		global $langmessage;
		$this->steps[1] = $langmessage['step:prepare'];
		$this->steps[2] = $langmessage['step:download'];
		$this->steps[3] = $langmessage['step:unpack'];
		$this->steps[4] = $langmessage['step:clean'];
		echo ''.$langmessage['update_steps'].'
';
		echo '';
		$curr_step_label = '';
		foreach($this->steps as $temp_step => $message ){
			if( $this->curr_step == $temp_step ){
				echo '- '.$message.'
 ';
				$curr_step_label = $message;
			}elseif( $temp_step < $this->curr_step ){
				echo '- '.$message.'
 ';
			}else{
				echo '- '.$message.'
 ';
			}
		}
		echo '
';
		echo ''.$curr_step_label.'
';
	}
	/**
	 * Run the current step
	 *
	 */
	protected function RunStep(){
		ob_start();
		switch($this->curr_step){
			case 4:
				$this->done = $this->CleanUp();
			break;
			case 3:
				$this->passed = $this->UnpackAndReplace();
				$this->OldFolders();
			break;
			case 2:
				$this->passed = $this->DownloadSource();
			break;
			case 1:
				$this->passed = $this->GetServerInfo();
			break;
		}
		return ob_get_clean();
	}
	/**
	 * Determine how we'll be writing the new code to the server (ftp or direct)
	 *
	 */
	function DetectFileSystem(){
		global $dataDir;
		//already determined
		if( isset($_POST['filesystem_method']) ){
			$this->FileSystem = \gp\tool\FileSystem::set_method($_POST['filesystem_method']);
			if( $this->FileSystem ){
				return $_POST['filesystem_method'];
			}
		}
		$this->curr_step = 1; //make sure we don't attempt anything beyond step 1
		$context[$dataDir]					= 'file';	// Need to be able to write to the dataDir
		$context[$dataDir . '/include']		= 'file';	// Need to be able to rename or delete the include directory
		$context[$dataDir . '/themes']		= 'dir';	// These may have user content in them and should not be completely replaced
		$context[$dataDir . '/addons']		= 'dir';
		$this->FileSystem					= \gp\tool\FileSystem::init($context);
		if( !$this->FileSystem ){
			return false;
		}
		return $this->FileSystem->method;
	}
	/**
	 * Remove folders and files that are no longer needed
	 *
	 */
	function CleanUp(){
		global $langmessage;
		//delete old folders
		if( isset($_POST['old_folder']) && is_array($_POST['old_folder']) ){
			$this->CleanUpFolders($_POST['old_folder']);
		}
		//failed install message
		if( isset($_POST['failed_install']) ){
			$this->msg($langmessage['settings_restored']);
			echo '';
			echo \gp\tool::link('',$langmessage['return_to_your_site']);
			echo '     ';
			echo ''.$langmessage['try_again'].'';
			echo '
';
			return true;
		}
		//delete zip file
		if( !empty($this->core_package['file']) && file_exists($this->core_package['file']) ){
			unlink($this->core_package['file']);
		}
		$this->msg($langmessage['settings_restored']);
		$this->msg($langmessage['software_updated']);
		echo '';
		echo \gp\tool::link('','» '.$langmessage['return_to_your_site']);
		echo '
';
		return true;
	}
	/**
	 * Delete folders
	 *
	 */
	function CleanUpFolders($folders){
		global $langmessage;
		if( !$this->FileSystem->connect() ){
			$this->msg($langmessage['OOPS'].' (not connected)');
			return false;
		}
		$not_deleted		= array();
		$this->FileSystem->CleanUpFolders($folders, $not_deleted);
		if( count($not_deleted) > 0 ){
			$this->msg($langmessage['delete_incomplete'].': '.implode(', ',$not_deleted));
			return false;
		}
		return true;
	}
	/**
	 * Remove configuration setting that indicates the CMS is being updated
	 *
	 */
	function RemoveUpdateMessage(){
		global $config, $langmessage;
		if( !isset($config['updating_message']) ){
			return true;
		}
		unset($config['updating_message']);
		if( !\gp\admin\Tools::SaveConfig(true) ){
			return false;
		}
		return true;
	}
	/**
	 * Replace the /include, /themes and /addons folders
	 * Start by creating the new folders with the new content
	 * Then replace the existing directories with the new directories
	 *
	 */
	function UnpackAndReplace(){
		global $langmessage, $config, $dataDir;
		if( !$this->FileSystem->connect() ){
			$this->msg($langmessage['OOPS'].': (not connected)');
			return false;
		}
		try{
			if( !$this->UnpackAndSort($this->core_package['file']) ){
				return false;
			}
		}catch( \Exception $e){
			$this->msg($langmessage['error_unpacking'].' (no root)');
			return false;
		}
		$this->msg('Files Sorted');
		$config['updating_message'] = $langmessage['sorry_currently_updating'];
		if( !\gp\admin\Tools::SaveConfig() ){
			$this->msg($langmessage['error_updating_settings']);
			return false;
		}
		$replaced = $this->FileSystem->ReplaceDirs( $this->replace_dirs, $this->extra_dirs );
		if( $replaced !== true ){
			$this->msg($langmessage['error_unpacking'].' '.$replaced);
			$this->RemoveUpdateMessage();
			return false;
		}
		$this->msg($langmessage['copied_new_files']);
		$this->RemoveUpdateMessage();
		return true;
	}
	/**
	 * Show which files will be deleted in the cleanup
	 *
	 */
	function OldFolders(){
		global $langmessage, $dataDir;
		$dirs = array_merge( array_values($this->replace_dirs), array_values($this->extra_dirs));
		$dirs = array_unique( $dirs );
		if( count($dirs) == 0 ){
			return;
		}
		$filesystem_base = $this->FileSystem->get_base_dir();
		ob_start();
		echo $langmessage['old_folders_created'];
		echo '';
		$this->msg(ob_get_clean());
	}
	/**
	 * Unpack the archive and save the files in temporary folders
	 * @return bool
	 */
	function UnpackAndSort($file){
		global $langmessage;
		$archive		= new \gp\tool\Archive($file);
		$archive_root	= $archive->GetRoot();
		if( is_null($archive_root) ){
			$this->msg($langmessage['error_unpacking'].' (no root)');
			return false;
		}
		$archive_root_len	= strlen($archive_root);
		$archive_files		= $archive->ListFiles();
		$this->msg($langmessage['package_unpacked']);
		foreach($archive_files as $file){
			if( strpos($file['name'],$archive_root) === false ){
				continue;
			}
			$rel_filename	= substr($file['name'],$archive_root_len);
			$name_parts		= explode('/',trim($rel_filename,'/'));
			$dir			= array_shift($name_parts);
			$replace_dir	= false;
			switch($dir){
				case 'include':
					$replace_dir	= 'include';
					$rel_filename	= implode('/',$name_parts);
				break;
				case 'themes':
				case 'addons':
					if( count($name_parts) == 0 ){
						continue 2;
					}
					$replace_dir	= $dir.'/'.array_shift($name_parts);
					$rel_filename	= implode('/',$name_parts);
				break;
			}
			if( $replace_dir === false ){
				continue;
			}
			$content	= $archive->getFromName($file['name']);
			if( empty($content) ){
				continue;
			}
			$replace_dir = trim($replace_dir,'/');
			if( !isset( $this->replace_dirs[$replace_dir] ) ){
				$this->replace_dirs[$replace_dir] = \gp\tool\FileSystem::TempFile( $replace_dir );
			}
			$file_rel	= $this->replace_dirs[$replace_dir].'/'.$rel_filename;
			if( !$this->PutFile( $file_rel, $content ) ){
				return false;
			}
		}
		return true;
	}
	/**
	 * Use FileSystem to save the file contents so permissions are consistent
	 *
	 */
	private function PutFile( $dest_rel, $content ){
		global $langmessage;
		$full = $this->FileSystem->get_base_dir().'/'.trim($dest_rel,'/');
		if( !$this->FileSystem->put_contents($full,$content) ){
			trigger_error('Could not create file: '.$full);
			$this->msg($langmessage['error_unpacking'].' (2)');
			return true;
		}
		return true;
	}
	/**
	 * Download the source code
	 *
	 */
	function DownloadSource(){
		global $langmessage, $dataDir;
		$this->msg('Downloading version '.$this->core_package['version'].' from '.CMS_READABLE_DOMAIN.'.');
		/* for testing
		 * $download = 'http://gpeasy.loc/x_gpEasy.zip';
		 */
		$download 		= addon_browse_path.'/Special_gpEasy?cmd=download';
		$contents		= \gp\tool\RemoteGet::Get_Successful($download);
		if( !$contents || empty($contents) ){
			$this->msg($langmessage['download_failed'].'(1)');
			return false;
		}
		$this->msg($langmessage['package_downloaded']);
		$md5 = md5($contents);
		if( $md5 != $this->core_package['md5'] ){
			$this->msg($langmessage['download_failed_md5'].'
Downloaded Checksum ('.$md5.') != Expected Checksum ('.$this->core_package['md5'].')');
			return false;
		}
		$this->msg($langmessage['download_verified']);
		//save contents
		$temp_file	= $dataDir.\gp\tool\FileSystem::TempFile('/data/_temp/update','.zip');
		if( !\gp\tool\Files::Save($temp_file,$contents) ){
			$this->msg($langmessage['download_failed'].' (2)');
			return false;
		}
		$this->core_package['file'] = $temp_file;
		return true;
	}
	/**
	 * Make sure we actuallly have the ability to write to the server using
	 *
	 */
	function GetServerInfo(){
		global $langmessage;
		if( $this->FileSystem->connect() ){
			$this->DoRemoteCheck2(); //make sure we have the latest information
			$this->msg($langmessage['ready_for_upgrade']);
			return true;
		}
		if( isset($_POST['connect_values_submitted']) ){
			msg($this->FileSystem->connect_msg);
		}
		//not connected, show form
		echo '';
		return false;
	}
	/**
	 * Add an update message
	 *
	 */
	function msg($msg){
		$this->update_msgs[] = $msg;
	}
	function OutputMessages(){
		if( !$this->update_msgs ){
			return;
		}
		echo '';
		foreach($this->update_msgs as $msg){
			echo '- '.$msg.'
 ';
		}
		echo '
';
	}
}