Переглянути джерело

Work in progress on the 2.0 setup/ the "install from scratch" is complete.

git-svn-id: http://svn.code.sf.net/p/itop/code/trunk@2219 a333f486-631f-4898-b8df-5754b55c2be0
dflaven 13 роки тому
батько
коміт
15a6299910

+ 53 - 0
setup/parameters.class.inc.php

@@ -37,6 +37,59 @@ class PHPParameters extends Parameters
 			$this->aData = $ITOP_PARAMS;
 			$this->aData = $ITOP_PARAMS;
 		}
 		}
 	}
 	}
+	
+	public function ToXML(DOMNode $oRoot, $data = null, $sNodeName = null)
+	{
+		if ($data === null)
+		{
+			$data = $this->aData;
+		}
+				
+		if (is_array($data))
+		{
+			if ($oRoot instanceof DOMDocument)
+			{
+				$oNode = $oRoot->createElement($sNodeName);
+			}
+			else
+			{
+				$oNode = $oRoot->ownerDocument->createElement($sNodeName);
+			}
+			$oRoot->appendChild($oNode);
+
+			$aKeys = array_keys($data);
+			$bNumericKeys = true;
+			foreach($aKeys as $idx => $subkey)
+			{
+				if(((int)$subkey) !== $subkey)
+				{
+					$bNumericKeys = false;
+					break;
+				}
+			}
+			if ($bNumericKeys)
+			{
+				$oNode->setAttribute("type", "array");
+				foreach($data as $key => $value)
+				{
+					$this->ToXML($oNode, $value , 'item');
+				}
+			}
+			else
+			{
+				foreach($data as $key => $value)
+				{
+					$this->ToXML($oNode, $value , $key);
+				}
+			}
+		}
+		else
+		{
+			$oNode = $oRoot->ownerDocument->createElement($sNodeName, $data);
+			$oRoot->appendChild($oNode);
+		}
+		return $oNode;
+	}
 }
 }
 
 
 class XMLParameters extends Parameters
 class XMLParameters extends Parameters

+ 45 - 2
setup/setuputils.class.inc.php

@@ -667,8 +667,29 @@ function ValidateField(sFieldId, bUsed)
 		{
 		{
 			if (sValue.match(/^[A-Za-z][A-Za-z0-9_]*$/))
 			if (sValue.match(/^[A-Za-z][A-Za-z0-9_]*$/))
 			{
 			{
-				$("#v_"+sFieldId).html("");
-				return true;
+				var bCollision = false;
+				if (sFieldId == 'db_new_name')
+				{
+					// check that the "new name" does not correspond to an existing database
+					var sNewName = $('#db_new_name').val();
+					$('#db_name option').each( function() {
+						if ($(this).attr('value') == sNewName)
+						{
+							bCollision = true;
+						}
+					});
+				}
+				
+				if (bCollision)
+				{
+					$("#v_"+sFieldId).html('<img src="../images/validation_error.png" title="A database with the same name already exists"/>');
+					return false;
+				}
+				else
+				{
+					$("#v_"+sFieldId).html("");
+					return true;
+				}
 			}
 			}
 			else
 			else
 			{
 			{
@@ -883,4 +904,26 @@ EOF
 		
 		
 		return $sHtml;
 		return $sHtml;
 	}
 	}
+	
+	public static function AnalyzeInstallation($oWizard)
+	{
+		require_once(APPROOT.'/setup/moduleinstaller.class.inc.php');
+		$oConfig = new Config();
+		
+		$aParamValues = array(
+			'db_server' => $oWizard->GetParameter('db_server', ''),
+			'db_user' => $oWizard->GetParameter('db_user', ''),
+			'db_pwd' => $oWizard->GetParameter('db_server', ''),
+			'db_name' => $oWizard->GetParameter('db_name', ''),
+			'db_prefix' => $oWizard->GetParameter('db_prefix', ''),
+			'source_dir' => APPROOT.'datamodel',
+		);
+		$oConfig->UpdateFromParams($aParamValues, 'datamodel');
+
+		$oProductionEnv = new RunTimeEnvironment();
+		$oConfig = new Config();
+		$aAvailableModules = $oProductionEnv->AnalyzeInstallation($oConfig, 'datamodel');
+
+		return $aAvailableModules;
+	}	
 }
 }

+ 384 - 55
setup/wizardsteps.class.inc.php

@@ -230,17 +230,33 @@ class WizStepInstallOrUpgrade extends WizardStep
 		$oPage->add('<tr><td>Location on the disk:</td><td><input id="source_dir" type="text" name="source_dir" value="'.htmlentities($sSourceDir, ENT_QUOTES, 'UTF-8').'" size="25"/></td></tr>');
 		$oPage->add('<tr><td>Location on the disk:</td><td><input id="source_dir" type="text" name="source_dir" value="'.htmlentities($sSourceDir, ENT_QUOTES, 'UTF-8').'" size="25"/></td></tr>');
 		SetupUtils::DisplayDBParameters($oPage, false, $sDBServer, $sDBUser, $sDBPwd, $sDBName, $sDBPrefix);
 		SetupUtils::DisplayDBParameters($oPage, false, $sDBServer, $sDBUser, $sDBPwd, $sDBName, $sDBPrefix);
 
 
-		$sChecked = ($bDBBackup == 'install') ? ' checked ' : '';
-		$oPage->add('<tr><td style="text-align:right"><input id="db_backup" type="checkbox" name="db_backup"'.$sChecked.' value="1"/></td><td>Backup the '.ITOP_APPLICATION.' database before upgrading</td></tr>');
-		$oPage->add('<tr><td>Save the backup to:</td><td><input id="db_backup_path" type="text" name="db_backup_path" value="'.htmlentities($sDBBackupPath, ENT_QUOTES, 'UTF-8').'" size="25"/></td></tr>');
+		$aBackupChecks = SetupUtils::CheckBackupPrerequisites($sDBBackupPath);
+		$bCanBackup = true;
+		$sMySQLDumpMessage = '';
+		foreach($aBackupChecks as $oCheck)
+		{
+			if ($oCheck->iSeverity == CheckResult::ERROR)
+			{
+				$bCanBackup = false;
+				$sMySQLDumpMessage .= '<img src="../images/error.png"/>&nbsp;<b> Warning:</b> '.$oCheck->sLabel;
+			}
+			else
+			{
+				$sMySQLDumpMessage .= '<img src="../images/validation_ok.png"/> '.$oCheck->sLabel.' ';
+			}
+		}
+		$sChecked = ($bCanBackup && ($bDBBackup == 'install')) ? ' checked ' : '';
+		$sDisabled = $bCanBackup ? '' : ' disabled ';
+		$oPage->add('<tr><td colspan="2"><input id="db_backup" type="checkbox" name="db_backup"'.$sChecked.$sDisabled.' value="1"/>&nbsp;Backup the '.ITOP_APPLICATION.' database before upgrading</td></tr>');
+		$oPage->add('<tr><td colspan="2">Save the backup to: <input id="db_backup_path" type="text" name="db_backup_path" '.$sDisabled.'value="'.htmlentities($sDBBackupPath, ENT_QUOTES, 'UTF-8').'" size="25"/></td></tr>');
 		$fFreeSpace = SetupUtils::CheckDiskSpace($sDBBackupPath);
 		$fFreeSpace = SetupUtils::CheckDiskSpace($sDBBackupPath);
 		$sMessage = '';
 		$sMessage = '';
 		if ($fFreeSpace !== false)
 		if ($fFreeSpace !== false)
 		{
 		{
-			$sMessage = SetupUtils::HumanReadableSize($fFreeSpace).' free in '.dirname($sDBBackupPath);
+			$sMessage .= SetupUtils::HumanReadableSize($fFreeSpace).' free in '.dirname($sDBBackupPath);
 		}
 		}
-		$oPage->add('<tr><td>&nbsp;</td>');
-		$oPage->add('<td><span id="backup_info" style="font-size:small;color:#696969;">'.$sMessage.'</span></td></tr>');
+		$oPage->add('<tr><td colspan="2">');
+		$oPage->add($sMySQLDumpMessage.'<br/><span id="backup_info" style="font-size:small;color:#696969;">'.$sMessage.'</span></td></tr>');
 		$oPage->add('</table>');
 		$oPage->add('</table>');
 		//$oPage->add('</fieldset>');
 		//$oPage->add('</fieldset>');
 		$oPage->add_ready_script(
 		$oPage->add_ready_script(
@@ -615,8 +631,8 @@ class WizStepAdminAccount extends WizardStep
 		$oPage->add('<fieldset>');
 		$oPage->add('<fieldset>');
 		$oPage->add('<legend>Administrator Account</legend>');
 		$oPage->add('<legend>Administrator Account</legend>');
 		$oPage->add('<table>');
 		$oPage->add('<table>');
-		$oPage->add('<tr><td>Login: </td><td><input id="admin_user" name="admin_user" type="text" size="25" maxlength="64" value="'.htmlentities($sAdminUser, ENT_QUOTES, 'UTF-8').'"></td><tr>');
-		$oPage->add('<tr><td>Password: </td><td><input id="admin_pwd" name="admin_pwd" type="password" size="25" maxlength="64" value="'.htmlentities($sAdminPwd, ENT_QUOTES, 'UTF-8').'"></td><tr>');
+		$oPage->add('<tr><td>Login: </td><td><input id="admin_user" name="admin_user" type="text" size="25" maxlength="64" value="'.htmlentities($sAdminUser, ENT_QUOTES, 'UTF-8').'"><span id="v_admin_user"/></td><tr>');
+		$oPage->add('<tr><td>Password: </td><td><input id="admin_pwd" name="admin_pwd" type="password" size="25" maxlength="64" value="'.htmlentities($sAdminPwd, ENT_QUOTES, 'UTF-8').'"><span id="v_admin_pwd"/></td><tr>');
 		$oPage->add('<tr><td>Confirm password: </td><td><input id="confirm_pwd" name="confirm_pwd" type="password" size="25" maxlength="64" value="'.htmlentities($sConfirmPwd, ENT_QUOTES, 'UTF-8').'"></td><tr>');
 		$oPage->add('<tr><td>Confirm password: </td><td><input id="confirm_pwd" name="confirm_pwd" type="password" size="25" maxlength="64" value="'.htmlentities($sConfirmPwd, ENT_QUOTES, 'UTF-8').'"></td><tr>');
 		$sSourceDir = APPROOT.'dictionaries/';
 		$sSourceDir = APPROOT.'dictionaries/';
 		$aLanguages = SetupUtils::GetAvailableLanguages($sSourceDir);
 		$aLanguages = SetupUtils::GetAvailableLanguages($sSourceDir);
@@ -625,8 +641,48 @@ class WizStepAdminAccount extends WizardStep
 		$oPage->add('</td></tr>');
 		$oPage->add('</td></tr>');
 		$oPage->add('</table>');
 		$oPage->add('</table>');
 		$oPage->add('</fieldset>');
 		$oPage->add('</fieldset>');
+		$oPage->add_ready_script(
+<<<EOF
+		$('#admin_user').bind('change keyup', function() { WizardUpdateButtons(); } );
+		$('#admin_pwd').bind('change keyup', function() { WizardUpdateButtons(); } );
+		$('#confirm_pwd').bind('change keyup', function() { WizardUpdateButtons(); } );
+EOF
+		);
 	}
 	}
-}
+	
+	/**
+	 * Tells whether the "Next" button should be enabled interactively
+	 * @return string A piece of javascript code returning either true or false
+	 */
+	public function JSCanMoveForward()
+	{
+		return
+<<<EOF
+	bRet = ($('#admin_user').val() != '');
+	if (!bRet)
+	{
+		$("#v_admin_user").html('<img src="../images/validation_error.png" title="This field cannot be empty"/>');
+	}
+	else
+	{
+		$("#v_admin_user").html('');
+	}
+	
+	bPasswordsMatch = ($('#admin_pwd').val() == $('#confirm_pwd').val());
+	if (!bPasswordsMatch)
+	{
+		$('#v_admin_pwd').html('<img src="../images/validation_error.png" title="Retyped password do not match"/>');
+	}
+	else
+	{
+		$('#v_admin_pwd').html('');
+	}
+	bRet = bPasswordsMatch && bRet;
+	
+	return bRet;
+EOF
+		;
+	}}
 
 
 /**
 /**
  * Miscellaneous Parameters (URL, Sample Data)
  * Miscellaneous Parameters (URL, Sample Data)
@@ -670,7 +726,8 @@ class WizStepMiscParams extends WizardStep
 		$oPage->add('<fieldset>');
 		$oPage->add('<fieldset>');
 		$oPage->add('<legend>Application URL</legend>');
 		$oPage->add('<legend>Application URL</legend>');
 		$oPage->add('<table>');
 		$oPage->add('<table>');
-		$oPage->add('<tr><td>URL: </td><td><input id="application_url" name="application_url" type="text" size="35" maxlength="1024" value="'.htmlentities($sApplicationURL, ENT_QUOTES, 'UTF-8').'"></td><tr>');
+		$oPage->add('<tr><td>URL: </td><td><input id="application_url" name="application_url" type="text" size="35" maxlength="1024" value="'.htmlentities($sApplicationURL, ENT_QUOTES, 'UTF-8').'"><span id="v_application_url"/></td><tr>');
+		$oPage->add('<tr><td colspan="2">Change the value above if the end-users will be accessing the application by another path due to a specific configuration of the web server.</td><tr>');
 		$oPage->add('</table>');
 		$oPage->add('</table>');
 		$oPage->add('</fieldset>');
 		$oPage->add('</fieldset>');
 		$oPage->add('<fieldset>');
 		$oPage->add('<fieldset>');
@@ -682,6 +739,33 @@ class WizStepMiscParams extends WizardStep
 		$oPage->p('<input id="sample_data_no" name="sample_data" type="radio" value="no"'.$sChecked.'><label for="sample_data_no">&nbsp;I am installing a <b>production</b> instance, create an empty database to start from.');
 		$oPage->p('<input id="sample_data_no" name="sample_data" type="radio" value="no"'.$sChecked.'><label for="sample_data_no">&nbsp;I am installing a <b>production</b> instance, create an empty database to start from.');
 		$oPage->add('</table>');
 		$oPage->add('</table>');
 		$oPage->add('</fieldset>');
 		$oPage->add('</fieldset>');
+		$oPage->add_ready_script(
+<<<EOF
+		$('#application_url').bind('change keyup', function() { WizardUpdateButtons(); } );
+EOF
+		);
+	}
+	
+	/**
+	 * Tells whether the "Next" button should be enabled interactively
+	 * @return string A piece of javascript code returning either true or false
+	 */
+	public function JSCanMoveForward()
+	{
+		return
+<<<EOF
+	bRet = ($('#application_url').val() != '');
+	if (!bRet)
+	{
+		$("#v_application_url").html('<img src="../images/validation_error.png" title="This field cannot be empty"/>');
+	}
+	else
+	{
+		$("#v_application_url").html('');
+	}
+	return bRet;
+EOF
+		;
 	}
 	}
 }
 }
 
 
@@ -704,7 +788,7 @@ class WizStepModulesChoice extends WizardStep
 	
 	
 	public function ProcessParams($bMoveForward = true)
 	public function ProcessParams($bMoveForward = true)
 	{
 	{
-		// Accumualtes the selected modules:
+		// Accumulates the selected modules:
 		$index = $this->GetStepIndex();
 		$index = $this->GetStepIndex();
 		
 		
 		// use json_encode:decode to store a hash array: step_id => array(input_name => selected_input_id)
 		// use json_encode:decode to store a hash array: step_id => array(input_name => selected_input_id)
@@ -727,12 +811,15 @@ class WizStepModulesChoice extends WizardStep
 			{
 			{
 				// Exiting this step of the wizard, let's convert the selection into a list of modules
 				// Exiting this step of the wizard, let's convert the selection into a list of modules
 				$aModules = array();
 				$aModules = array();
+				$sDisplayChoices = '<ul>';
 				for($i = 0; $i <= $index; $i++)
 				for($i = 0; $i <= $index; $i++)
 				{
 				{
 					$aStepInfo = $this->GetStepInfo($i);
 					$aStepInfo = $this->GetStepInfo($i);
-					$this->GetSelectedModules($aStepInfo, $aSelectedChoices[$i], $aModules);
+					$sDisplayChoices .= $this->GetSelectedModules($aStepInfo, $aSelectedChoices[$i], $aModules);
 				}
 				}
+				$sDisplayChoices .= '</ul>';
 				$this->oWizard->SetParameter('selected_modules', json_encode(array_keys($aModules)));
 				$this->oWizard->SetParameter('selected_modules', json_encode(array_keys($aModules)));
+				$this->oWizard->SetParameter('display_choices', $sDisplayChoices);
 				return array('class' => 'WizStepSummary', 'state' => '');
 				return array('class' => 'WizStepSummary', 'state' => '');
 			}
 			}
 			
 			
@@ -749,6 +836,7 @@ class WizStepModulesChoice extends WizardStep
 		$aStepInfo = $this->GetStepInfo();
 		$aStepInfo = $this->GetStepInfo();
 		$oPage->add_style("div.choice { margin: 0.5em;}");
 		$oPage->add_style("div.choice { margin: 0.5em;}");
 		$oPage->add_style("div.description { margin-left: 2em; }");
 		$oPage->add_style("div.description { margin-left: 2em; }");
+		$oPage->add_style(".choice-disabled { color: #999; }");
 		$oPage->add('<table><tr>');
 		$oPage->add('<table><tr>');
 		$sBannerPath = isset($aStepInfo['banner']) ? $aStepInfo['banner'] : '';
 		$sBannerPath = isset($aStepInfo['banner']) ? $aStepInfo['banner'] : '';
 		if (!empty($sBannerPath))
 		if (!empty($sBannerPath))
@@ -788,6 +876,62 @@ class WizStepModulesChoice extends WizardStep
 		$oPage->add('<div style="max-height:250px;overflow:auto;border:#ccc 1px solid;">');
 		$oPage->add('<div style="max-height:250px;overflow:auto;border:#ccc 1px solid;">');
 		$this->DisplayOptions($oPage, $aStepInfo, $aSelectedComponents);
 		$this->DisplayOptions($oPage, $aStepInfo, $aSelectedComponents);
 		$oPage->add('</div>');
 		$oPage->add('</div>');
+		
+		$oPage->add_script(
+<<<EOF
+function CheckChoice(sChoiceId)
+{
+	console.log('Ici !!');
+	var oElement = $('#'+sChoiceId);
+	var bChecked = (oElement.attr('checked') == 'checked');
+	var sId = sChoiceId.replace('choice', '');
+	if ((oElement.attr('type') == 'radio') && bChecked)
+	{
+		// Only the radio that is clicked is notified, let's warn the other radio buttons
+		sName = oElement.attr('name');
+		$('input[name="'+sName+'"]').each(function() {
+			var sRadioId = $(this).attr('id');
+			if (sRadioId != sChoiceId)
+			{
+				CheckChoice(sRadioId);
+			}
+		});
+	}
+	
+	$('#sub_choices'+sId).each(function() {
+		if (!bChecked)
+		{
+			$(this).addClass('choice-disabled');
+		}
+		else
+		{
+			$(this).removeClass('choice-disabled');
+		}
+		
+		$('input', this).each(function() {
+			if (bChecked)
+			{
+				if ($(this).attr('data-disabled') != 'disabled')
+				{
+					// Only non-mandatory fields can be enabled
+					$(this).removeAttr('disabled');
+				}
+			}
+			else
+			{
+				$(this).attr('disabled', 'disabled');
+			}
+		});
+	});
+}
+EOF
+		);
+		$oPage->add_ready_script(
+<<<EOF
+		$('.wiz-choice').bind('change', function() { CheckChoice($(this).attr('id')); } );
+		$('.wiz-choice').trigger('change');
+EOF
+		);
 	}
 	}
 	
 	
 	protected function GetDefaults($aInfo, &$aDefaults, $sParentId = '')
 	protected function GetDefaults($aInfo, &$aDefaults, $sParentId = '')
@@ -836,12 +980,12 @@ class WizStepModulesChoice extends WizardStep
 	 * @param string $sParentId Used for recursion
 	 * @param string $sParentId Used for recursion
 	 * @return void
 	 * @return void
 	 */
 	 */
-	protected function GetSelectedModules($aInfo, $aSelectedChoices, &$aModules, $sParentId = '')
+	protected function GetSelectedModules($aInfo, $aSelectedChoices, &$aModules, $sParentId = '', $sDisplayChoices = '')
 	{
 	{
 		if ($sParentId == '')
 		if ($sParentId == '')
 		{
 		{
 			// Check once (before recursing) that the hidden modules are selected
 			// Check once (before recursing) that the hidden modules are selected
-			foreach($this->AnalyzeInstallation() as $sModuleId => $aModule)
+			foreach(SetupUtils::AnalyzeInstallation($this->oWizard) as $sModuleId => $aModule)
 			{
 			{
 				if ($sModuleId != ROOT_MODULE)
 				if ($sModuleId != ROOT_MODULE)
 				{
 				{
@@ -860,6 +1004,7 @@ class WizStepModulesChoice extends WizardStep
 			if ( (isset($aChoice['mandatory']) && $aChoice['mandatory']) || 
 			if ( (isset($aChoice['mandatory']) && $aChoice['mandatory']) || 
 				 (isset($aSelectedChoices[$sChoiceId]) && ($aSelectedChoices[$sChoiceId] == $sChoiceId)) )
 				 (isset($aSelectedChoices[$sChoiceId]) && ($aSelectedChoices[$sChoiceId] == $sChoiceId)) )
 			{
 			{
+				$sDisplayChoices .= '<li>'.$aChoice['title'].'</li>';
 				if (isset($aChoice['modules']))
 				if (isset($aChoice['modules']))
 				{
 				{
 					foreach($aChoice['modules'] as $sModuleId)
 					foreach($aChoice['modules'] as $sModuleId)
@@ -870,8 +1015,11 @@ class WizStepModulesChoice extends WizardStep
 				// Recurse only for selected choices
 				// Recurse only for selected choices
 				if (isset($aChoice['sub_options']))
 				if (isset($aChoice['sub_options']))
 				{
 				{
-					$this->GetSelectedModules($aChoice['sub_options'], $aSelectedChoices, $aModules, $sChoiceId);
+					$sDisplayChoices .= '<ul>';
+					$sDisplayChoices = $this->GetSelectedModules($aChoice['sub_options'], $aSelectedChoices, $aModules, $sChoiceId, $sDisplayChoices);
+					$sDisplayChoices .= '</ul>';
 				}
 				}
+				$sDisplayChoices .= '</li>';
 			}
 			}
 			$index++;
 			$index++;
 		}
 		}
@@ -888,6 +1036,7 @@ class WizStepModulesChoice extends WizardStep
 			if ( (isset($aChoice['mandatory']) && $aChoice['mandatory']) || 
 			if ( (isset($aChoice['mandatory']) && $aChoice['mandatory']) || 
 				 (isset($aSelectedChoices[$sChoiceName]) && ($aSelectedChoices[$sChoiceName] == $sChoiceId)) )
 				 (isset($aSelectedChoices[$sChoiceName]) && ($aSelectedChoices[$sChoiceName] == $sChoiceId)) )
 			{
 			{
+				$sDisplayChoices .= '<li>'.$aChoice['title'].'</li>';
 				if (isset($aChoice['modules']))
 				if (isset($aChoice['modules']))
 				{
 				{
 					foreach($aChoice['modules'] as $sModuleId)
 					foreach($aChoice['modules'] as $sModuleId)
@@ -898,11 +1047,15 @@ class WizStepModulesChoice extends WizardStep
 				// Recurse only for selected choices
 				// Recurse only for selected choices
 				if (isset($aChoice['sub_options']))
 				if (isset($aChoice['sub_options']))
 				{
 				{
-					$this->GetSelectedModules($aChoice['sub_options'], $aSelectedChoices, $aModules, $sChoiceId);
+					$sDisplayChoices .= '<ul>';
+					$sDisplayChoices = $this->GetSelectedModules($aChoice['sub_options'], $aSelectedChoices, $aModules, $sChoiceId, $sDisplayChoices);
+					$sDisplayChoices .= '</ul>';
 				}
 				}
+				$sDisplayChoices .= '</li>';
 			}
 			}
 			$index++;
 			$index++;
 		}
 		}
+		return $sDisplayChoices;
 	}
 	}
 	
 	
 	protected function GetStepIndex()
 	protected function GetStepIndex()
@@ -942,7 +1095,7 @@ class WizStepModulesChoice extends WizardStep
 		else if ($idx == 0)
 		else if ($idx == 0)
 		{
 		{
 			// No wizard configuration provided, build a standard one:
 			// No wizard configuration provided, build a standard one:
-			$aAvailableModules = $this->AnalyzeInstallation();
+			$aAvailableModules = SetupUtils::AnalyzeInstallation($this->oWizard);
 			$aStepInfo = array(
 			$aStepInfo = array(
 				'title' => 'Modules Selection',
 				'title' => 'Modules Selection',
 				'description' => '<h2>Select the modules to install. You can launch the installation again to install new modules, but you cannot remove already installed modules.</h2>',
 				'description' => '<h2>Select the modules to install. You can launch the installation again to install new modules, but you cannot remove already installed modules.</h2>',
@@ -989,28 +1142,6 @@ class WizStepModulesChoice extends WizardStep
 		}
 		}
 		return $aStepInfo;
 		return $aStepInfo;
 	}
 	}
-	
-	protected function AnalyzeInstallation()
-	{
-		require_once(APPROOT.'/setup/moduleinstaller.class.inc.php');
-		$oConfig = new Config();
-		
-		$aParamValues = array(
-			'db_server' => $this->oWizard->GetParameter('db_server', ''),
-			'db_user' => $this->oWizard->GetParameter('db_user', ''),
-			'db_pwd' => $this->oWizard->GetParameter('db_server', ''),
-			'db_name' => $this->oWizard->GetParameter('db_name', ''),
-			'db_prefix' => $this->oWizard->GetParameter('db_prefix', ''),
-			'source_dir' => APPROOT.'datamodel',
-		);
-		$oConfig->UpdateFromParams($aParamValues, 'datamodel');
-
-		$oProductionEnv = new RunTimeEnvironment();
-		$oConfig = new Config();
-		$aAvailableModules = $oProductionEnv->AnalyzeInstallation($oConfig, 'datamodel');
-
-		return $aAvailableModules;
-	}
 		
 		
 	protected function DisplayOptions($oPage, $aStepInfo, $aSelectedComponents, $sParentId = '')
 	protected function DisplayOptions($oPage, $aStepInfo, $aSelectedComponents, $sParentId = '')
 	{
 	{
@@ -1024,15 +1155,15 @@ class WizStepModulesChoice extends WizardStep
 			$sChoiceId = $sParentId.'_'.$index;
 			$sChoiceId = $sParentId.'_'.$index;
 			if (isset($aChoice['mandatory']) && $aChoice['mandatory'])
 			if (isset($aChoice['mandatory']) && $aChoice['mandatory'])
 			{
 			{
-				$oPage->add('<div class="choice"><input id="choice'.$sChoiceId.'" checked disabled type="checkbox"'.$sAttributes.'/><input type="hidden" name="choice['.$sChoiceId.']" value="'.$sChoiceId.'">&nbsp;');
+				$oPage->add('<div class="choice"><input id="choice'.$sChoiceId.'" checked disabled data-disabled="disabled" type="checkbox"'.$sAttributes.'/><input type="hidden" name="choice['.$sChoiceId.']" value="'.$sChoiceId.'">&nbsp;');
 			}
 			}
 			else if (isset($aSelectedComponents[$sChoiceId]) && ($aSelectedComponents[$sChoiceId] == $sChoiceId))
 			else if (isset($aSelectedComponents[$sChoiceId]) && ($aSelectedComponents[$sChoiceId] == $sChoiceId))
 			{
 			{
-				$oPage->add('<div class="choice"><input id="choice'.$sChoiceId.'" name="choice['.$sChoiceId.']" type="checkbox" checked value="'.$sChoiceId.'"/>&nbsp;');
+				$oPage->add('<div class="choice"><input class="wiz-choice" id="choice'.$sChoiceId.'" name="choice['.$sChoiceId.']" type="checkbox" checked value="'.$sChoiceId.'"/>&nbsp;');
 			}
 			}
 			else
 			else
 			{
 			{
-				$oPage->add('<div class="choice"><input id="choice'.$sChoiceId.'" name="choice['.$sChoiceId.']" type="checkbox" value="'.$sChoiceId.'"/>&nbsp;');
+				$oPage->add('<div class="choice"><input class="wiz-choice" id="choice'.$sChoiceId.'" name="choice['.$sChoiceId.']" type="checkbox" value="'.$sChoiceId.'"/>&nbsp;');
 			}
 			}
 			$this->DisplayChoice($oPage, $aChoice, $aSelectedComponents, $sChoiceId);
 			$this->DisplayChoice($oPage, $aChoice, $aSelectedComponents, $sChoiceId);
 			$oPage->add('</div>');
 			$oPage->add('</div>');
@@ -1052,7 +1183,7 @@ class WizStepModulesChoice extends WizardStep
 			{
 			{
 				$sAttributes = ' checked ';
 				$sAttributes = ' checked ';
 			}
 			}
-			$oPage->add('<div class="choice"><input id="choice'.$sChoiceId.'" name="choice['.$sChoiceName.']" type="radio"'.$sAttributes.' value="'.$sChoiceId.'"/>&nbsp;');
+			$oPage->add('<div class="choice"><input class="wiz-choice" id="choice'.$sChoiceId.'" name="choice['.$sChoiceName.']" type="radio"'.$sAttributes.' value="'.$sChoiceId.'"/>&nbsp;');
 			$this->DisplayChoice($oPage, $aChoice, $aSelectedComponents, $sChoiceId);
 			$this->DisplayChoice($oPage, $aChoice, $aSelectedComponents, $sChoiceId);
 			$oPage->add('</div>');
 			$oPage->add('</div>');
 			$index++;
 			$index++;
@@ -1063,12 +1194,12 @@ class WizStepModulesChoice extends WizardStep
 	{
 	{
 		$oPage->add('<label for="choice'.$sChoiceId.'"><b>'.htmlentities($aChoice['title'], ENT_QUOTES, 'UTF-8').'</b></label>');
 		$oPage->add('<label for="choice'.$sChoiceId.'"><b>'.htmlentities($aChoice['title'], ENT_QUOTES, 'UTF-8').'</b></label>');
 		$sDescription = isset($aChoice['description']) ? htmlentities($aChoice['description'], ENT_QUOTES, 'UTF-8') : '';
 		$sDescription = isset($aChoice['description']) ? htmlentities($aChoice['description'], ENT_QUOTES, 'UTF-8') : '';
-		$oPage->add('<div class="description">'.$sDescription);
+		$oPage->add('<div class="description">'.$sDescription.'<span id="sub_choices'.$sChoiceId.'">');
 		if (isset($aChoice['sub_options']))
 		if (isset($aChoice['sub_options']))
 		{
 		{
 			$this->DisplayOptions($oPage, $aChoice['sub_options'], $aSelectedComponents, $sChoiceId);
 			$this->DisplayOptions($oPage, $aChoice['sub_options'], $aSelectedComponents, $sChoiceId);
 		}
 		}
-		$oPage->add('</div>');
+		$oPage->add('</span></div>');
 	}
 	}
 	
 	
 	protected function GetSourceFilePath()
 	protected function GetSourceFilePath()
@@ -1085,7 +1216,16 @@ class WizStepSummary extends WizardStep
 {
 {
 	public function GetTitle()
 	public function GetTitle()
 	{
 	{
-		return 'Installation summary';
+		$sMode = $this->oWizard->GetParameter('mode', 'install');
+		if ($sMode == 'install')
+		{
+			return 'Ready to install';
+			
+		}
+		else
+		{
+			return 'Ready to upgrade';
+		}
 	}
 	}
 	
 	
 	public function GetPossibleSteps()
 	public function GetPossibleSteps()
@@ -1109,8 +1249,121 @@ class WizStepSummary extends WizardStep
 	
 	
 	public function Display(WebPage $oPage)
 	public function Display(WebPage $oPage)
 	{
 	{
+		$oPage->add_style(
+<<<EOF
+#params_summary {
+	height: 200px;
+	overflow: auto;
+}
+#params_summary div {
+	width:100;
+	margin-top:0;
+	padding-top: 0.5em;
+	padding-left: 0;
+}
+#params_summary div ul {
+	margin-left:0;
+	padding-left: 20px;
+}
+#params_summary div.closed ul {
+	display:none;
+}
+#params_summary div li {
+	list-style: none;
+	width: 100;
+	margin-left:0;
+	padding-left: 0em;
+}
+.title {
+	padding-left: 20px;
+	font-weight: bold;
+	cursor: pointer;
+	background: url(../images/minus.gif) 2px 2px no-repeat;
+}
+#params_summary div.closed .title {
+	background: url(../images/plus.gif) 2px 2px no-repeat;
+}
+#progress_content {
+	height: 200px;
+	overflow: auto;
+	text-align: center;
+}
+#installation_progress {
+	display: none;
+}
+EOF
+		);
+		$sMode = $this->oWizard->GetParameter('mode', 'install');
+		
+		$sPreinstallationPhase = '';
+		
+		$sDestination = ITOP_APPLICATION.(($sMode == 'install') ? ' version '.ITOP_VERSION.' is about to be installed ' : ' is about to be upgraded ');
+		$sDBDescription = ' <b>existing</b> database <b>'.$this->oWizard->GetParameter('db_name').'</b>';
+		if (($sMode == 'install') && ($this->oWizard->GetParameter('create_db') == 'yes'))
+		{
+			$sDBDescription = ' <b>new</b> database <b>'.$this->oWizard->GetParameter('db_new_name').'</b>';
+		}
+		$sDestination .= 'into the '.$sDBDescription.' on the server <b>'.$this->oWizard->GetParameter('db_server').'</b>.';
+		$oPage->add('<h2>'.$sDestination.'</h2>');
+		
+		$oPage->add('<fieldset id="summary"><legend>Installation Parameters</legend>');
+		$oPage->add('<div id="params_summary">');
+		$oPage->add('<div class="closed"><span class="title">Database Parameters</span><ul>');
+		$oPage->add('<li>Server Name: '.$this->oWizard->GetParameter('db_server').'</li>');
+		$oPage->add('<li>DB User Name: '.$this->oWizard->GetParameter('db_user').'</li>');
+		$oPage->add('<li>DB user password: '.$this->oWizard->GetParameter('db_pwd').'</li>');
+		if (($sMode == 'install') && ($this->oWizard->GetParameter('create_db') == 'yes'))
+		{
+			$oPage->add('<li>Database Name: '.$this->oWizard->GetParameter('db_new_name').' (will be created)</li>');
+		}
+		else
+		{
+			$oPage->add('<li>Database Name: '.$this->oWizard->GetParameter('db_name').'</li>');
+		}
+		if ($this->oWizard->GetParameter('db_prefix') != '')
+		{
+			$oPage->add('<li>Prefix for the '.ITOP_APPLICATION.' tables: '.$this->oWizard->GetParameter('db_prefix').'</li>');
+		}
+		else
+		{
+			$oPage->add('<li>Prefix for the '.ITOP_APPLICATION.' tables: none</li>');
+		}
+		$oPage->add('</ul></div>');
+		
+		$oPage->add('<div><span class="title">Data Model Configuration</span>');
+		$oPage->add($this->oWizard->GetParameter('display_choices'));
+		$oPage->add('</div>');
+		
+		$oPage->add('<div class="closed"><span class="title">Other Parameters</span><ul>');
+		$oPage->add('<li>Default language: '.$this->oWizard->GetParameter('default_language').'</li>');
+		$oPage->add('<li>URL to access the application: '.$this->oWizard->GetParameter('application_url').'</li>');
+		if ($this->oWizard->GetParameter('sample_data') == 'yes')
+		{
+			$oPage->add('<li>Sample data will be loaded into the database.</li>');
+		}
+		$oPage->add('</ul></div>');
+		
+		$oPage->add('<div class="closed"><span class="title">Admininistrator Account</span><ul>');
+		$oPage->add('<li>Login: '.$this->oWizard->GetParameter('admin_user').'</li>');
+		$oPage->add('<li>Password: '.$this->oWizard->GetParameter('admin_pwd').'</li>');
+		$oPage->add('<li>Language: '.$this->oWizard->GetParameter('admin_language').'</li>');
+		$oPage->add('</ul></div>');
+		
+		$aMiscOptions = json_decode($this->oWizard->GetParameter('misc_options', '[]'), true /* bAssoc */);
+		if (count($aMiscOptions) > 0)
+		{
+			$oPage->add('<div class="closed"><span class="title">Miscellaneous Options</span><ul>');
+			foreach($aMiscOptions as $sKey => $sValue)
+			{
+				$oPage->add('<li>'.$sKey.': '.$sValue.'</li>');
+			}
+			$oPage->add('</ul></div>');
+			
+		}
+		
+		$aSelectedModules = json_decode($this->oWizard->GetParameter('selected_modules'), true);
+/*		
 		$oPage->add('<ul>Selected modules:');
 		$oPage->add('<ul>Selected modules:');
-		$aSelectedModules = json_decode($this->oWizard->GetParameter('selected_modules'));
 		sort($aSelectedModules);
 		sort($aSelectedModules);
 		foreach($aSelectedModules as $sModuleId)
 		foreach($aSelectedModules as $sModuleId)
 		{
 		{
@@ -1118,17 +1371,22 @@ class WizStepSummary extends WizardStep
 			
 			
 		}
 		}
 		$oPage->add('</ul>');
 		$oPage->add('</ul>');
-		$oPage->add('<div id="percentage"> Ready to Start...<br/></div>');
+*/		
 		$oPage->add_ready_script(
 		$oPage->add_ready_script(
 <<<EOF
 <<<EOF
-	$("#btn_next").bind("click.install", function(event){ $(this).attr("disabled", "disabled"); event.preventDefault(); ExecuteStep(""); });
+	$("#params_summary div").addClass('closed');
+	$("#params_summary .title").click(function() { $(this).parent().toggleClass('closed'); } );
+	$("#btn_next").bind("click.install", function(event) {
+			$('#summary').hide();
+			$('#installation_progress').show();
+			$(this).attr("disabled", "disabled"); event.preventDefault(); ExecuteStep("");
+	});
 	$("#wiz_form").data("installation_status", "not started")
 	$("#wiz_form").data("installation_status", "not started")
 EOF
 EOF
 		);
 		);
 		
 		
 		// Prepare the parameters to execute the installation asynchronously
 		// Prepare the parameters to execute the installation asynchronously
 		
 		
-		$sMode = $this->oWizard->GetParameter('mode', 'install');
 		$sBackupDestination = '';
 		$sBackupDestination = '';
 		$sConfigurationFile = '';
 		$sConfigurationFile = '';
 		$sDBName = $this->oWizard->GetParameter('db_name');
 		$sDBName = $this->oWizard->GetParameter('db_name');
@@ -1186,7 +1444,7 @@ EOF
 		  'language' => $this->oWizard->GetParameter('default_language'),
 		  'language' => $this->oWizard->GetParameter('default_language'),
 		  'selected_modules' =>  $aSelectedModules,
 		  'selected_modules' =>  $aSelectedModules,
 		  'sample_data' => ($this->oWizard->GetParameter('sample_data', '') == 'yes') ? true : false ,
 		  'sample_data' => ($this->oWizard->GetParameter('sample_data', '') == 'yes') ? true : false ,
-		  'options' => json_decode($this->oWizard->GetParameter('misc_options')),
+		  'options' => json_decode($this->oWizard->GetParameter('misc_options', '[]'), true),
 		);
 		);
 
 
 		if ($sBackupDestination != '')
 		if ($sBackupDestination != '')
@@ -1196,8 +1454,33 @@ EOF
 			      'configuration_file' => $sConfigurationFile,
 			      'configuration_file' => $sConfigurationFile,
 			);
 			);
 		}
 		}
-		
 		$sJSONData = json_encode($aInstallParams);
 		$sJSONData = json_encode($aInstallParams);
+		if (isset($aMiscOptions['generate_config']))
+		{
+			$oDoc = new DOMDocument('1.0', 'UTF-8');
+			$oDoc->preserveWhiteSpace = false;
+			$oDoc->formatOutput = true;
+			$oParams = new PHPParameters();
+			$oParams->LoadFromHash($aInstallParams);
+			$oParams->ToXML($oDoc, null, 'installation');
+			$sXML = $oDoc->saveXML();
+			
+			$oPage->add('<div class="closed"><span class="title">XML Config file</span><ul><pre>');
+			$oPage->add(htmlentities($sXML, ENT_QUOTES, 'UTF-8'));
+			$oPage->add('</pre></ul></div>');
+		}
+		
+	
+		$oPage->add('</div>'); // params_summary
+		$oPage->add('</fieldset>');
+		
+		$oPage->add('<fieldset id="installation_progress"><legend>Progress of the installation</legend>');
+		$oPage->add('<div id="progress_content">');
+		$oPage->add_linked_script('../setup/jquery.progression.js');
+		$oPage->add('<p class="center"><span id="setup_msg">Ready to start...</span></p><div style="display:block;margin-left: auto; margin-right:auto;" id="progress">0%</div>');		
+		$oPage->add('</div>'); // progress_content
+		$oPage->add('</fieldset>');
+		
 		$oPage->add('<input type="hidden" id="installer_parameters" value="'.htmlentities($sJSONData, ENT_QUOTES, 'UTF-8').'"/>');
 		$oPage->add('<input type="hidden" id="installer_parameters" value="'.htmlentities($sJSONData, ENT_QUOTES, 'UTF-8').'"/>');
 	}
 	}
 	
 	
@@ -1212,11 +1495,15 @@ EOF
 		if (($aRes['status'] != ApplicationInstaller::ERROR) && ($aRes['next-step'] != ''))
 		if (($aRes['status'] != ApplicationInstaller::ERROR) && ($aRes['next-step'] != ''))
 		{
 		{
 			// Tell the web page to move the progress bar and to launch the next step
 			// Tell the web page to move the progress bar and to launch the next step
+			$sMessage = addslashes(htmlentities($aRes['next-step-label'], ENT_QUOTES, 'UTF-8'));
 			$oPage->add_ready_script(
 			$oPage->add_ready_script(
 <<<EOF
 <<<EOF
 	$("#wiz_form").data("installation_status", "running");
 	$("#wiz_form").data("installation_status", "running");
 	WizardUpdateButtons();
 	WizardUpdateButtons();
-	$("#percentage").html('{$aRes['percentage-completed']} % completed<br/>{$aRes['next-step-label']}');
+	$('#setup_msg').html('$sMessage');
+	$('#progress').progression( {Current:{$aRes['percentage-completed']}, Maximum: 100, aBackgroundImg: GetAbsoluteUrlAppRoot()+'setup/orange-progress.gif', aTextColor: '#000000'} );
+	
+	//$("#percentage").html('{$aRes['percentage-completed']} % completed<br/>{$aRes['next-step-label']}');
 	ExecuteStep('{$aRes['next-step']}');
 	ExecuteStep('{$aRes['next-step']}');
 EOF
 EOF
 		);
 		);
@@ -1227,6 +1514,7 @@ EOF
 			$oPage->add_ready_script(
 			$oPage->add_ready_script(
 <<<EOF
 <<<EOF
 	$("#wiz_form").data("installation_status", "completed");
 	$("#wiz_form").data("installation_status", "completed");
+	$('#progress').progression( {Current:100, Maximum: 100, aBackgroundImg: GetAbsoluteUrlAppRoot()+'setup/orange-progress.gif', aTextColor: '#000000'} );
 	WizardUpdateButtons();
 	WizardUpdateButtons();
 	$("#btn_next").unbind("click.install");
 	$("#btn_next").unbind("click.install");
 	$("#btn_next").click();
 	$("#btn_next").click();
@@ -1235,11 +1523,12 @@ EOF
 		}
 		}
 		else
 		else
 		{
 		{
+			$sMessage = addslashes(htmlentities($aRes['message'], ENT_QUOTES, 'UTF-8'));
 			$oPage->add_ready_script(
 			$oPage->add_ready_script(
 <<<EOF
 <<<EOF
 	$("#wiz_form").data("installation_status", "error");
 	$("#wiz_form").data("installation_status", "error");
 	WizardUpdateButtons();
 	WizardUpdateButtons();
-	$("#percentage").html('<b>Error</b><br/>{$aRes['message']}');
+	$('#setup_msg').html('$sMessage');
 EOF
 EOF
 			);
 			);
 		}
 		}
@@ -1287,8 +1576,48 @@ class WizStepDone extends WizardStep
 	
 	
 	public function Display(WebPage $oPage)
 	public function Display(WebPage $oPage)
 	{
 	{
-		$oPage->p('Installation Completed.');
+		// Check if there are some manual steps required:
+		$aManualSteps = array();
+		$aAvailableModules = SetupUtils::AnalyzeInstallation($this->oWizard);
+
+		$sRootUrl = utils::GetAbsoluteUrlAppRoot();
+		$aSelectedModules = json_decode($this->oWizard->GetParameter('selected_modules'), true);
+		foreach($aSelectedModules as $sModuleId)
+		{
+			if (!empty($aAvailableModules[$sModuleId]['doc.manual_setup']))
+			{
+				$aManualSteps[$aAvailableModules[$sModuleId]['label']] = $sRootUrl.$aAvailableModules[$sModuleId]['doc.manual_setup'];
+			}
+		}
+		if (count($aManualSteps) > 0)
+		{
+			$oPage->add("<h2>Manual operations required</h2>");
+			$oPage->p("In order to complete the installation, the following manual operations are required:");
+			foreach($aManualSteps as $sModuleLabel => $sUrl)
+			{
+				$oPage->p("<a href=\"$sUrl\" target=\"_blank\">Manual instructions for $sModuleLabel</a>");
+			}
+			$oPage->add("<h2>Congratulations for installing iTop</h2>");
+		}
+		else
+		{
+			$oPage->add("<h2>Congratulations for installing iTop</h2>");
+			$oPage->ok("The installation completed successfully.");
+		}
+		// Form goes here.. No back button since the job is done !
+		$oPage->add('<table style="width:600px;border:0;padding:0;"><tr>');
+		$oPage->add("<td><a style=\"background:transparent;padding:0;\" title=\"Free: Register your iTop version.\" href=\"http://www.combodo.com/register?product=iTop&version=".urlencode(ITOP_VERSION." revision ".ITOP_REVISION)."\" target=\"_blank\"><img style=\"border:0\" src=\"../images/setup-register.gif\"/></td></a>");
+		$oPage->add("<td><a style=\"background:transparent;padding:0;\" title=\"Get Professional Support from Combodo\" href=\"http://www.combodo.com/itopsupport\" target=\"_blank\"><img style=\"border:0\" src=\"../images/setup-support.gif\"/></td></a>");
+		$oPage->add("<td><a style=\"background:transparent;padding:0;\" title=\"Get Professional Training from Combodo\" href=\"http://www.combodo.com/itoptraining\" target=\"_blank\"><img style=\"border:0\" src=\"../images/setup-training.gif\"/></td></a>");
+		$oPage->add('</tr></table>');
+		$sForm = '<form method="post" action="'.$this->oWizard->GetParameter('application_url').'/pages/UI.php">';
+		$sForm .= '<input type="hidden" name="auth_user" value="'.htmlentities($this->oWizard->GetParameter('admin_user'), ENT_QUOTES, 'UTF-8').'">';
+		$sForm .= '<input type="hidden" name="auth_pwd" value="'.htmlentities($this->oWizard->GetParameter('admin_pwd'), ENT_QUOTES, 'UTF-8').'">';
+		$sForm .= "<p style=\"text-align:center;width:100%\"><button id=\"enter_itop\" type=\"submit\">Enter iTop</button></p>";
+		$sForm .= '</form>';
 		$oPage->add('<img style="border:0" src="http://www.combodo.com/stats/?p='.urlencode(ITOP_APPLICATION).'&v='.urlencode(ITOP_VERSION).'"/>');
 		$oPage->add('<img style="border:0" src="http://www.combodo.com/stats/?p='.urlencode(ITOP_APPLICATION).'&v='.urlencode(ITOP_VERSION).'"/>');
+		$sForm = addslashes($sForm);
+		$oPage->add_ready_script("$('#wiz_form').after('$sForm');");
 	}
 	}
 	
 	
 	public function CanMoveForward()
 	public function CanMoveForward()