<?php
/*
Officity - Web application platform - Version 6.0 - 2011-07-05

François Dispaux, Boris Verdeyen, Thomas Hermant,
Jérémie Roy, Grégory Meurice, Abdelila Harbi, 
Marc Mignonsin, Jonathan Sanchez, Julien Gonzalez, Pierre Fouchez

Sushee and Officity is © Copyright 2011 Nectil SA.

`/var/www/installer/public_html/officity-source/apps/officity/factory/publish/publish-ops.php` is part of Officity.

Sushee is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Officity and Sushee are distributed in the hope that they will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Sushee. If not, see <http://www.gnu.org/licenses/>.
*/
    require_once(dirname(__FILE__).'/include.php');

	/* *** */
    
	class PublishOperation extends AbstractOperation
	{
		protected $release;
		
		function __construct($output_type)
		{
			parent::__construct($output_type);
			$this->release = new Officity_ReleaseElement();		
		}

		function __destruct()
		{
			
		}
		
		protected function _execute($method_name)
		{
			if (!is_object($this->release))
			{
				$class_name = get_class($this);
				$this->abort('Operation on a non-object element in "'.$class_name.'->'.$method_name.'"!');
			}				
			$this->JSON_response->set('module', 'officity:release');

			return $this->$method_name();
		}
		
		/* *** */
		
		// PUBLISH APPLICATIONS
		public function _publishApps()
		{	
			/* 1st: checks */
			
		    // retrieve apps IDs
		    $namespaces_IDs = $apps_IDs = array();
			foreach ($_POST as $key => $value)
		    {
		    	list($check_prefix, $ns_or_app_prefix, $ID) = explode('-', $key, 3);
		    	if ($check_prefix == 'check')
		    	{
		    		if ($ns_or_app_prefix == 'namespace') $namespaces_IDs []= $ID;
		    		if ($ns_or_app_prefix == 'app') $apps_IDs []= $ID;
		    	}
		    }		    

    		// publish system?
			$check_system = $this->fetchURLParam('check-system', '');
			$publish_system = !empty($check_system);
			
		    if (empty($namespaces_IDs) && ($publish_system == false))
		    {
		    	$this->abort('Please make a selection!');
		    }
		    
		    // retrieve apps folders in both environments and check for existence
			$origin_env_type = $this->fetchURLParam('originEnv');
			$target_env_type = $this->fetchURLParam('targetEnv');
			$origin_env_folder = $target_env_folder = $ver_env_folder = '';
			$this->getEnvironmentsFolders($origin_env_type, $origin_env_folder, $target_env_type, $target_env_folder, $ver_env_folder);

			// retrieve namespaces/applications folders and check for existence
		    if (!empty($namespaces_IDs))
		    {
				$nql = new Sushee_Shell(false);
				$nql->addCommand('
					<SEARCH>
						<NAMESPACE>
							<INFO>
								<ID operator="IN">'.implode(',', $namespaces_IDs).'</ID>
							</INFO>
						</NAMESPACE>
						<RETURN depth="2">
							<INFO>
								<NAMESPACE />
							</INFO>
							<DEPENDENCIES>
								<DEPENDENCY type="officity:namespaceApps">
									<INFO>
										<URL />
									</INFO>
								</DEPENDENCY>
							</DEPENDENCIES>
						</RETURN>
					</SEARCH>
				');
				$nql->execute();

				// namespaces shared folders
		    	$ns_data = array();
		    	foreach ($namespaces_IDs as $namespace_ID)
		    	{
            		$ns_node = $nql->getElement('/RESPONSE/RESULTS/NAMESPACE[@ID='.$namespace_ID.']');
        	        if (empty($ns_node))
				    {
				    	$this->abort('Cannot retrieve editor data (ID='.$namespace_ID.')!');
				    }				    
				    $ns_folder = $ns_node->valueOf('INFO/NAMESPACE');
        		    if (empty($ns_folder))
				    {
				    	$this->abort('Cannot retrieve editor folder (ID='.$namespace_ID.')!');
				    }
				    
			    	$ns_data[$namespace_ID]['origin_path'] = '/'.$origin_env_folder.'/'.$ns_folder;
			    	$ns_data[$namespace_ID]['origin_shared_path'] = $ns_data[$namespace_ID]['origin_path'].'/shared';
			    	$ns_data[$namespace_ID]['target_path'] = '/'.$target_env_folder.'/'.$ns_folder;
			    	$ns_data[$namespace_ID]['target_shared_path'] = $ns_data[$namespace_ID]['target_path'].'/shared';
			    	
			    	// origin shared folder exists?
    				$ns_data[$namespace_ID]['origin_shared_folder'] = new KernelFolder($ns_data[$namespace_ID]['origin_shared_path']);
					if (!$ns_data[$namespace_ID]['origin_shared_folder']->exists())
					{
						$this->abort('Shared folder '.$ns_data[$namespace_ID]['origin_shared_path'].' does not exist!');
					}
		    	}
		    	
		    	// apps folders
		    	foreach ($apps_IDs as $app_ID)
		    	{	
		    		$app_node = $nql->getElement('/RESPONSE/RESULTS/NAMESPACE//APP[@ID='.$app_ID.']');
        	        if (empty($app_node))
				    {
				    	$this->abort('Cannot retrieve application data (ID='.$app_ID.')!');
				    }				    
		    		$app_folder = $app_node->valueOf('INFO/URL');
        		    if (empty($app_folder))
				    {
				    	$this->abort('Cannot retrieve application folder (ID='.$app_ID.')!');
				    }		

				    // log
				    $email_notification_msg .= $app_folder.PHP_EOL;
				    
				    // apps data
				    $ns_ID = $app_node->valueOf('ancestor::NAMESPACE/@ID');
		    		$ns_data[$ns_ID]['apps'][$app_ID]['origin_path'] = $ns_data[$ns_ID]['origin_path'].'/'.$app_folder;
		    		$ns_data[$ns_ID]['apps'][$app_ID]['target_path'] = $ns_data[$ns_ID]['target_path'].'/'.$app_folder;
		    		
		    		// origin app folder exists?
        			$ns_data[$ns_ID]['apps'][$app_ID]['origin_folder'] = new KernelFolder($ns_data[$ns_ID]['apps'][$app_ID]['origin_path']);
					if (!$ns_data[$ns_ID]['apps'][$app_ID]['origin_folder']->exists())
					{
						$this->abort('Application folder '.$ns_data[$ns_ID]['apps'][$app_ID]['origin_path'].' does not exist!');
					}	 
		    	}
			    
			    if (empty($ns_data))
			    {
			    	$this->abort('Cannot retrieve editors data!');
			    }
		    }
		    
		    //var_dump($ns_data);
		    //$this->abort('To be continued...');
		    
		    // log
		    $email_notification_msg = 'Folders published:'.PHP_EOL;
		    foreach ($ns_data as $ns_ID => $ns)
		    {
		    	$email_notification_msg .= PHP_EOL.$ns['origin_shared_path'].PHP_EOL;
		    	foreach ($ns['apps'] as $app_ID => $app)
		    	{
		    		$email_notification_msg .= '   '.$app['origin_path'].PHP_EOL;
		    	}
		    }
			
		    $system_data = array('publish' => $publish_system);
			if ($publish_system == true)
			{
				// origin : system path
				$system_data['origin_path'] = '/'.$origin_env_folder.'/system';
				$system_data['origin_folder'] = new KernelFolder($system_data['origin_path']);
				if (!$system_data['origin_folder']->exists())
				{
					$this->abort('System folder '.$system_data['origin_path'].' does not exist!');
				}
				$system_data['target_path'] = '/'.$target_env_folder.'/system';
				$system_data['target_folder'] = new KernelFolder($system_data['target_path']);
				
				/*var_dump($system_data);
				$this->abort('To be continued...');*/
				$email_notification_msg .= PHP_EOL.'System'.PHP_EOL.'   '.$system_data['origin_path'];
			}
			
			$folders_ops_results_msg = '';
			
		    /* 2nd: folders backup then creation */
			
			// backup target env
			$backup_file = $this->backupTargetEnv($target_env_folder, $ver_env_folder);
			
			// delete target folders
			$this->deleteTargetFolders($ns_data, $system_data, $target_env_folder);
			
			// copy folders to target env
			$this->copyOriginFolders($ns_data, $system_data, $target_env_folder);
						
			/* 3rd: release element creation */

		    $this->release->setInfo('TYPE', $origin_env_type.'-'.$target_env_type);
		    $this->release->setInfo('NOTE', $this->fetchURLParam('release-comment', ''));
		    $this->release->setInfo('BACKUPFILE', $backup_file);
		    
		    foreach ($apps_IDs as $app_ID)
		    {
		    	$this->release->setDependencyByName('officity:releaseApps', 'APP', $app_ID, 'append');
		    }
		    
	        $create_result = $this->release->create();   
	        if ($create_result === true)
	        {
	        	$this->JSON_response->set('ID', $this->release->getID());
	        }
	        else
	        {
	        	$this->JSON_response->set('NQL', $this->release->getNQLCommand()); // log NQL
	        	$this->abort('Operation failed: '.$this->release->getLastError());
	        }
	        
	        // email notification
			$nql->addCommand('
				<CREATE>
					<SERVERMAIL>
						<RECIPIENT>feedback@nectil.com</RECIPIENT>
						<SUBJECT>A new Officity version has been released!</SUBJECT>
						<BODY>'.$this->release->getInfo('NOTE').PHP_EOL.PHP_EOL.$email_notification_msg.'</BODY>
					</SERVERMAIL>
				</CREATE>
			');
			$nql->execute();
	        
	        return true;
		}
		
		protected function copyOriginFolders($ns_data, $system_data, $env_folder_name)
		{
			foreach ($ns_data as $ns_ID => $ns)
			{
				$target_folder = new KernelFolder($ns['target_path']);
				if (!$ns['origin_shared_folder']->copy($target_folder))
				{
					$this->abort('Error copying folder '.$ns['origin_shared_path'].' to '.$ns['target_path'].'!');
				}
				
				foreach ($ns['apps'] as $app_ID => $app)
				{
					if (!$app['origin_folder']->copy($target_folder))
					{
						$this->abort('Error copying folder '.$app['origin_path'].' to '.$ns['target_path'].'!');
					}
				}
			}
			
			if ($system_data['publish'] == true)
			{
				// copy system
				$env_target_path = '/'.$env_folder_name;
				$env_target_folder = new KernelFolder($env_target_path);
				if (!$system_data['origin_folder']->copy($env_target_folder))
				{
					$this->abort('Error copying system folder '.$system_data['origin_path'].' to '.$env_target_path.'!');
				}
				
				// replace config folder with its backup (see deleteTargetFolders)
				$config_folder = $system_data['target_folder']->getChild('config');
				if ($config_folder->exists())
				{
					$config_folder->delete();
					if ($config_folder->exists())
					{
						$this->abort('Error copying system folder to '.$env_target_path.': cannot delete config folder to replace it with its backup! Publish operation aborted. Please check manually before retrying.');
					}
					
					$config_bak_path = $env_target_path.'/bak-sys-config';
					$config_bak_folder = new KernelFolder($config_bak_path);
					if (!$config_bak_folder->exists())
					{
						$this->abort('Error copying system folder to '.$env_target_path.': cannot retrieve config backup folder '.$config_bak_path.'! Publish operation aborted. Please check manually before retrying.');
					}
					
					if (!$config_bak_folder->rename('config'))
					{
						$this->abort('Error copying system folder to '.$env_target_path.': cannot rename config folder '.$config_bak_path.' to config! Publish operation aborted. Please check manually before retrying.');
					}
					
					if (!$config_bak_folder->copy($system_data['target_folder']))
					{
						$this->abort('Error copying system folder to '.$env_target_path.': cannot copy config backup folder '.$config_bak_path.' to '.$system_data['target_path'].'! Publish operation aborted. Please check manually before retrying.');
					}

					$config_bak_folder->delete();
					if ($config_bak_folder->exists())
					{
						$this->abort('Error copying system folder to '.$env_target_path.': cannot delete config folder with its backup! Publish operation aborted. Please check manually before retrying.');
					}
				}
			}
		}
		
		protected function deleteTargetFolders($ns_data, $system_data, $env_folder_name)
		{
			foreach ($ns_data as $ns_ID => $ns)
			{
				$shared_folder = new KernelFolder($ns['target_shared_path']);
				if ($shared_folder->exists())
				{
					$shared_folder->delete();
					if ($shared_folder->exists())
					{
						$this->abort('Error deleting shared folder '.$ns['target_shared_path'].'! Publish operation aborted. Please check manually before retrying.');
					}
				}
				
				foreach ($ns['apps'] as $app_ID => $app)
				{
					$app_folder = new KernelFolder($app['target_path']);
					if ($app_folder->exists())
					{
						$app_folder->delete();
						if ($app_folder->exists())
						{
							$this->abort('Error deleting application folder '.$app['target_path'].'! Publish operation aborted. Please check manually before retrying.');
						}
					}
				}
			}
			
			if ($system_data['publish'] == true)
			{
				if ($system_data['target_folder']->exists())
				{
					// backup config folder
					$config_target_path = $system_data['target_path'].'/config';
					$new_config_name = 'bak-sys-config';
					$env_target_path = '/'.$env_folder_name;
					
					$config_folder = new KernelFolder($config_target_path);
					if (!$config_folder->exists())
					{
						$config_folder = new KernelFolder($env_target_path.'/bak-sys-config');
						if (!$config_folder->exists())
						{
							$this->abort('Error deleting system folder '.$system_data['target_path'].': cannot retrieve config folder or its backup! Publish operation aborted. Please check manually before retrying.');
						}
					}						
					if (!$config_folder->rename($new_config_name))
					{
						$this->abort('Error deleting system folder: cannot rename config folder '.$config_target_path.' to '.$new_config_name.'! Publish operation aborted. Please check manually before retrying.');
					}
					
					$env_target_folder = new KernelFolder($env_target_path);
					if (!$config_folder->copy($env_target_folder))
					{
						$this->abort('Error deleting system folder: cannot copy config folder '.$config_target_path.' to '.$env_target_path.'/'.$new_config_name.'! Publish operation aborted. Please check manually before retrying.');
					}					
					
					// delete
					$system_data['target_folder']->delete();
					if ($system_data['target_folder']->exists())
					{
						$this->abort('Error deleting system folder '.$system_data['target_path'].'! Publish operation aborted. Please check manually before retrying.');
					}
				}
			}
		}
		
		protected function backupTargetEnv($folder_name, $ver_folder_name)
		{
			$folder = new KernelFolder('/'.$folder_name);
			if (!$folder->exists())
			{
				$this->abort('Target environment backup failed: folder '.$folder_name.' does not exist!');
			}
			
			$ver_folder = new KernelFolder($ver_folder_name);
			if (!$folder->exists())
			{
				$this->abort('Target environment backup failed: versions folder '.$ver_folder_name.' does not exist!');
			}
			
			// copy then compress
			if (!$folder->copy($ver_folder))
			{
				$this->abort('Error copying folder '.$folder_name.' to '.$ver_folder_name.'!');
			}
			
			$backup_folder = $ver_folder->getChild($folder_name);
			$backup_folder_name = 'R-'.date('Ymd-His-').$folder_name;					
			if (!$backup_folder->rename($backup_folder_name))
			{
				$this->abort('Error renaming folder '.$folder_name.' to '.$backup_folder_name.'!');
			}
			
			if (!$backup_folder->compress())
			{
				$this->abort('Error compressing backup folder '.$backup_folder_name.'!');
			}
			
			// cleanup
			$backup_folder->delete();
			if ($backup_folder->exists())
			{
				$this->abort('Error deleting backup folder '.$backup_folder_name.'!');
			}
			
			return $backup_folder_name;
		}
		
		protected function getEnvironmentsFolders($origin_env_type, &$origin_env_folder, $target_env_type, &$target_env_folder, &$ver_env_folder)
		{
			$nql = new Sushee_Shell(false);
			$nql->addCommand('
				<SEARCH>
					<OFFICITY:ENVIRONMENT>
						<INFO>
							<PUBLISHED operator="=">1</PUBLISHED>
						</INFO>
					</OFFICITY:ENVIRONMENT>
					<RETURN>
						<INFO>
							<FOLDER />
							<TYPE />
						</INFO>
					</RETURN>
				</SEARCH>
			');
			$nql->execute();
			
			$origin_env_folder = $nql->valueOf('/RESPONSE/RESULTS/OFFICITY:ENVIRONMENT[INFO/TYPE="'.$origin_env_type.'"]/INFO/FOLDER');
			if (empty($origin_env_folder))
			{
				$this->abort('Cannot retrieve origin environment folder ('.$origin_env_type.')!');
			}
			
			$target_env_folder = $nql->valueOf('/RESPONSE/RESULTS/OFFICITY:ENVIRONMENT[INFO/TYPE="'.$target_env_type.'"]/INFO/FOLDER');
			if (empty($target_env_folder))
			{
				$this->abort('Cannot retrieve target environment folder ('.$target_env_type.')!');
			}
			
			$ver_env_folder = $nql->valueOf('/RESPONSE/RESULTS/OFFICITY:ENVIRONMENT[INFO/TYPE="versions"]/INFO/FOLDER');
			if (empty($ver_env_folder))
			{
				$this->abort('Cannot retrieve versions environment folder!');
			}
		}
	}
	
    /* *** */
	
	try
	{
		$op = new PublishOperation();
		if ($op->execute(URLParam::fetch('op'), URLParam::fetch('output', 'json')) === false)
		{
			$op->abort();
		}
		else
		{
			$op->success();
		}
	}
	catch (OfficityException $e)
	{
		$e->getJSONResponse()->outputError();
	}
?>