mirror of
https://github.com/Combodo/iTop.git
synced 2026-05-16 13:58:45 +02:00
Setup sequencer side A
This commit is contained in:
@@ -10,11 +10,11 @@ class SetupAudit extends AbstractSetupAudit
|
||||
private string $sEnvBefore;
|
||||
private string $sEnvAfter;
|
||||
|
||||
public function __construct(string $sEnvBefore)
|
||||
public function __construct(string $sEnvBefore, ?string $sEnvAfter = null)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->sEnvBefore = $sEnvBefore;
|
||||
$this->sEnvAfter = "$sEnvBefore-build";
|
||||
$this->sEnvAfter = $sEnvAfter ?? "$sEnvBefore-build";
|
||||
}
|
||||
|
||||
public function ComputeClasses(): void
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
*/
|
||||
|
||||
use Combodo\iTop\PhpParser\Evaluation\PhpExpressionEvaluator;
|
||||
use Combodo\iTop\Setup\FeatureRemoval\SetupAudit;
|
||||
use Combodo\iTop\Setup\ModuleDiscovery\ModuleFileReader;
|
||||
use Combodo\iTop\Setup\ModuleDiscovery\ModuleFileReaderException;
|
||||
|
||||
@@ -308,7 +309,7 @@ class RunTimeEnvironment
|
||||
$sModule = $oModule->GetName();
|
||||
$bIsExtra = $this->GetExtensionMap()->ModuleIsChosenAsPartOfAnExtension($sModule, iTopExtension::SOURCE_REMOTE);
|
||||
if (array_key_exists($sModule, $aAvailableModules)) {
|
||||
if (($aAvailableModules[$sModule]['installed_version'] != '') || $bIsExtra && !$oModule->IsAutoSelect()) { //Extra modules are always unless they are 'AutoSelect'
|
||||
if (($aAvailableModules[$sModule]['installed_version'] != '') || $bIsExtra && !$oModule->IsAutoSelect()) { //Extra modules are always unless they are 'AutoSelect'
|
||||
$aRet[$oModule->GetName()] = $oModule;
|
||||
}
|
||||
}
|
||||
@@ -327,9 +328,10 @@ class RunTimeEnvironment
|
||||
$bSelected = $oPhpExpressionEvaluator->ParseAndEvaluateBooleanExpression($oModule->GetAutoSelect());
|
||||
if ($bSelected) {
|
||||
$aRet[$oModule->GetName()] = $oModule; // store the Id of the selected module
|
||||
$bModuleAdded = true;
|
||||
$bModuleAdded = true;
|
||||
}
|
||||
} catch (ModuleFileReaderException $e) {
|
||||
}
|
||||
catch (ModuleFileReaderException $e) {
|
||||
//do nothing. logged already
|
||||
}
|
||||
}
|
||||
@@ -345,56 +347,6 @@ class RunTimeEnvironment
|
||||
return $aRet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the data model by imitating the given environment
|
||||
* The list of modules to be installed in the build environment is:
|
||||
* - the list of modules present in the "source_dir" (defined by the source environment) which are marked as "installed" in the source environment's database
|
||||
* - plus the list of modules present in the "extra" directory of the build environment: data/<build_environment>-modules/
|
||||
*
|
||||
* @param string $sSourceEnv The name of the source environment to 'imitate'
|
||||
* @param bool $bUseSymLinks Whether to create symbolic links instead of copies
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function CompileFrom($sSourceEnv, $bUseSymLinks = null)
|
||||
{
|
||||
$oSourceConfig = new Config(utils::GetConfigFilePath($sSourceEnv));
|
||||
$sSourceDir = $oSourceConfig->Get('source_dir');
|
||||
|
||||
$sSourceDirFull = APPROOT.$sSourceDir;
|
||||
// Do load the required modules
|
||||
//
|
||||
$oFactory = new ModelFactory($sSourceDirFull);
|
||||
$aModulesToCompile = $this->GetMFModulesToCompile($sSourceEnv, $sSourceDir);
|
||||
$oModule = null;
|
||||
foreach ($aModulesToCompile as $oModule) {
|
||||
if ($oModule instanceof MFDeltaModule) {
|
||||
// Just before loading the delta, let's save an image of the datamodel
|
||||
// in case there is no delta the operation will be done after the end of the loop
|
||||
$oFactory->SaveToFile(utils::GetDataPath().'datamodel-'.$this->sBuildEnv.'.xml');
|
||||
}
|
||||
$oFactory->LoadModule($oModule);
|
||||
}
|
||||
|
||||
if (!is_null($oModule) && ($oModule instanceof MFDeltaModule)) {
|
||||
// A delta was loaded, let's save a second copy of the datamodel
|
||||
$oFactory->SaveToFile(utils::GetDataPath().'datamodel-'.$this->sBuildEnv.'-with-delta.xml');
|
||||
} else {
|
||||
// No delta was loaded, let's save the datamodel now
|
||||
$oFactory->SaveToFile(utils::GetDataPath().'datamodel-'.$this->sBuildEnv.'.xml');
|
||||
}
|
||||
|
||||
$sBuildDir = APPROOT.'env-'.$this->sBuildEnv;
|
||||
self::MakeDirSafe($sBuildDir);
|
||||
$bSkipTempDir = ($this->sFinalEnv != $this->sBuildEnv); // No need for a temporary directory if sBuildEnv is already a temporary directory
|
||||
$oMFCompiler = new MFCompiler($oFactory, $this->sFinalEnv);
|
||||
$oMFCompiler->Compile($sBuildDir, null, $bUseSymLinks, $bSkipTempDir);
|
||||
|
||||
MetaModel::ResetAllCaches($this->sBuildEnv);
|
||||
|
||||
return array_keys($aModulesToCompile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to create the database structure
|
||||
*
|
||||
@@ -509,7 +461,7 @@ class RunTimeEnvironment
|
||||
}
|
||||
}
|
||||
foreach ($aPredefinedObjects as $iRefId => $aObjValues) {
|
||||
if (! array_key_exists($iRefId, $aDBIds)) {
|
||||
if (!array_key_exists($iRefId, $aDBIds)) {
|
||||
$oNewObj = MetaModel::NewObject($sClass);
|
||||
$oNewObj->SetKey($iRefId);
|
||||
foreach ($aObjValues as $sAttCode => $value) {
|
||||
@@ -650,7 +602,8 @@ class RunTimeEnvironment
|
||||
{
|
||||
try {
|
||||
$aSelectInstall = ModuleInstallationRepository::GetInstance()->ReadFromDB($oConfig);
|
||||
} catch (MySQLException $e) {
|
||||
}
|
||||
catch (MySQLException $e) {
|
||||
// No database or erroneous information
|
||||
$this->log_error('Can not connect to the database: host: '.$oConfig->Get('db_host').', user:'.$oConfig->Get('db_user').', pwd:'.$oConfig->Get('db_pwd').', db name:'.$oConfig->Get('db_name'));
|
||||
$this->log_error('Exception '.$e->getMessage());
|
||||
@@ -708,14 +661,17 @@ class RunTimeEnvironment
|
||||
{
|
||||
SetupLog::Error($sText);
|
||||
}
|
||||
|
||||
protected function log_warning($sText)
|
||||
{
|
||||
SetupLog::Warning($sText);
|
||||
}
|
||||
|
||||
protected function log_info($sText)
|
||||
{
|
||||
SetupLog::Info($sText);
|
||||
}
|
||||
|
||||
protected function log_ok($sText)
|
||||
{
|
||||
SetupLog::Ok($sText);
|
||||
@@ -747,10 +703,16 @@ class RunTimeEnvironment
|
||||
if ($oLatestDM == null) {
|
||||
return '0.0.0';
|
||||
}
|
||||
|
||||
return $oLatestDM->Get('version');
|
||||
}
|
||||
|
||||
public function Commit()
|
||||
/**
|
||||
* Move the build env to the final env if all the steps went ok
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function Commit(): void
|
||||
{
|
||||
if ($this->sFinalEnv != $this->sBuildEnv) {
|
||||
if (file_exists(utils::GetDataPath().$this->sBuildEnv.'.delta.xml')) {
|
||||
@@ -808,12 +770,13 @@ class RunTimeEnvironment
|
||||
/**
|
||||
* Overwrite or create the destination file
|
||||
*
|
||||
* @param $sSource
|
||||
* @param $sDest
|
||||
* @param string $sSource
|
||||
* @param string $sDest
|
||||
* @param bool $bSourceMustExist
|
||||
* @throws Exception
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function CommitFile($sSource, $sDest, $bSourceMustExist = true)
|
||||
protected function CommitFile(string $sSource, string $sDest, bool $bSourceMustExist = true): void
|
||||
{
|
||||
if (file_exists($sSource)) {
|
||||
SetupUtils::builddir(dirname($sDest));
|
||||
@@ -847,6 +810,7 @@ class RunTimeEnvironment
|
||||
* @param $sDest
|
||||
* @param boolean $bSourceMustExist
|
||||
* @param boolean $bRemoveSource If true $sSource will be removed, otherwise $sSource will just be emptied
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function CommitDir($sSource, $sDest, $bSourceMustExist = true, $bRemoveSource = true)
|
||||
@@ -875,9 +839,11 @@ class RunTimeEnvironment
|
||||
|
||||
/**
|
||||
* Call the given handler method for all selected modules having an installation handler
|
||||
*
|
||||
* @param array[] $aAvailableModules
|
||||
* @param string $sHandlerName
|
||||
* @param string[]|null $aSelectedModules
|
||||
* @param string[]|null $aSelectedModules
|
||||
*
|
||||
* @throws CoreException
|
||||
*/
|
||||
public function CallInstallerHandlers($aAvailableModules, $sHandlerName, $aSelectedModules = null)
|
||||
@@ -915,15 +881,16 @@ class RunTimeEnvironment
|
||||
if (is_callable($aCallSpec)) {
|
||||
try {
|
||||
call_user_func_array($aCallSpec, $aArgs);
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$sModuleId = $aModuleInfo[ModuleFileReader::MODULE_INFO_ID] ?? "";
|
||||
$sErrorMessage = "Module $sModuleId : error when calling module installer class $sModuleInstallerClass for $sHandlerName handler";
|
||||
$aExceptionContextData = [
|
||||
'ModulelId' => $sModuleId,
|
||||
'ModuleInstallerClass' => $sModuleInstallerClass,
|
||||
'ModulelId' => $sModuleId,
|
||||
'ModuleInstallerClass' => $sModuleInstallerClass,
|
||||
'ModuleInstallerHandler' => $sHandlerName,
|
||||
'ExceptionClass' => get_class($e),
|
||||
'ExceptionMessage' => $e->getMessage(),
|
||||
'ExceptionClass' => get_class($e),
|
||||
'ExceptionMessage' => $e->getMessage(),
|
||||
];
|
||||
throw new CoreException($sErrorMessage, $aExceptionContextData, '', $e);
|
||||
}
|
||||
@@ -932,9 +899,10 @@ class RunTimeEnvironment
|
||||
|
||||
/**
|
||||
* Load data from XML files for the selected modules (structural data and/or sample data)
|
||||
*
|
||||
* @param array[] $aAvailableModules All available modules and their definition
|
||||
* @param bool $bSampleData Wether or not to load sample data
|
||||
* @param null|string[] $aSelectedModules List of selected modules
|
||||
* @param null|string[] $aSelectedModules List of selected modules
|
||||
*/
|
||||
public function LoadData($aAvailableModules, $bSampleData, $aSelectedModules = null)
|
||||
{
|
||||
@@ -1011,9 +979,11 @@ class RunTimeEnvironment
|
||||
|
||||
/**
|
||||
* Merge two arrays of file names, adding the relative path to the files provided in the array to merge
|
||||
*
|
||||
* @param string[] $aSourceArray
|
||||
* @param string $sBaseDir
|
||||
* @param string[] $aFilesToMerge
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
protected static function MergeWithRelativeDir($aSourceArray, $sBaseDir, $aFilesToMerge)
|
||||
@@ -1022,14 +992,16 @@ class RunTimeEnvironment
|
||||
foreach ($aFilesToMerge as $sFile) {
|
||||
$aToMerge[] = $sBaseDir.'/'.$sFile;
|
||||
}
|
||||
|
||||
return array_merge($aSourceArray, $aToMerge);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the MetaModel for some common pitfall (class name too long, classes requiring too many joins...)
|
||||
* The check takes about 900 ms for 200 classes
|
||||
* @throws Exception
|
||||
*
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public function CheckMetaModel()
|
||||
{
|
||||
@@ -1043,7 +1015,7 @@ class RunTimeEnvironment
|
||||
|
||||
$oSearch = new DBObjectSearch($sClass);
|
||||
$oSearch->SetShowObsoleteData(false);
|
||||
$oSQLQuery = $oSearch->GetSQLQueryStructure(null, false);
|
||||
$oSQLQuery = $oSearch->GetSQLQueryStructure([], false);
|
||||
$sViewName = MetaModel::DBGetView($sClass);
|
||||
if (strlen($sViewName) > 64) {
|
||||
throw new Exception("Class name too long for class: '$sClass'. The name of the corresponding view ($sViewName) would exceed MySQL's limit for the name of a table (64 characters).");
|
||||
@@ -1062,4 +1034,226 @@ class RunTimeEnvironment
|
||||
|
||||
return sprintf("Checked %d classes in %.1f ms. No error found.\n", $iCount, $fDuration * 1000.0);
|
||||
}
|
||||
} // End of class
|
||||
|
||||
public function DataToCleanupAudit()
|
||||
{
|
||||
$oSetupAudit = new SetupAudit('production', $this->sBuildEnv);
|
||||
|
||||
//Make sure the MetaModel is started before analysing for issues
|
||||
$sConfFile = utils::GetConfigFilePath($this->sBuildEnv);
|
||||
MetaModel::Startup($sConfFile, false, false, false, $this->sBuildEnv);
|
||||
$oSetupAudit->GetIssues(true);
|
||||
$iCount = $oSetupAudit->GetDataToCleanupCount();
|
||||
|
||||
if ($iCount > 0) {
|
||||
throw new Exception("$iCount elements require data adjustments or cleanup in the backoffice prior to upgrading iTop", DataAuditSequencer::DATA_AUDIT_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
public function CopySetupFiles(): void
|
||||
{
|
||||
$sSourceEnv = 'production';
|
||||
$sDestinationEnv = $this->sBuildEnv;
|
||||
|
||||
if ($sDestinationEnv != $sSourceEnv) {
|
||||
SetupUtils::CopyFile(utils::GetDataPath().$sSourceEnv.'.delta.xml', utils::GetDataPath().$sDestinationEnv.'.delta.xml');
|
||||
SetupUtils::copydir(utils::GetDataPath().$sSourceEnv.'-modules/', utils::GetDataPath().$sDestinationEnv.'-modules/');
|
||||
|
||||
// Copy the config file
|
||||
//
|
||||
$sFinalConfig = APPCONF.$sDestinationEnv.'/config-itop.php';
|
||||
if (is_file($sFinalConfig)) {
|
||||
chmod($sFinalConfig, 0770); // In case it exists: RWX for owner and group, nothing for others
|
||||
}
|
||||
SetupUtils::copydir(APPCONF.$sSourceEnv, APPCONF.$sDestinationEnv);
|
||||
if (is_file($sFinalConfig)) {
|
||||
chmod($sFinalConfig, 0440); // Read-only for owner and group, nothing for others
|
||||
}
|
||||
|
||||
MetaModel::ResetAllCaches($sDestinationEnv);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the data model by imitating the given environment
|
||||
* The list of modules to be installed in the build environment is:
|
||||
* - the list of modules present in the "source_dir" (defined by the source environment) which are marked as "installed" in the source environment's database
|
||||
* - plus the list of modules present in the "extra" directory of the build environment: data/<build_environment>-modules/
|
||||
*
|
||||
* @param string $sSourceEnv The name of the source environment to 'imitate'
|
||||
* @param null $bUseSymLinks Whether to create symbolic links instead of copies
|
||||
*
|
||||
* @return string[]
|
||||
* @throws \ConfigException
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function CompileFrom($sSourceEnv, $bUseSymLinks = null)
|
||||
{
|
||||
$oSourceConfig = new Config(utils::GetConfigFilePath($sSourceEnv));
|
||||
$sSourceDir = $oSourceConfig->Get('source_dir');
|
||||
|
||||
$sSourceDirFull = APPROOT.$sSourceDir;
|
||||
// Do load the required modules
|
||||
//
|
||||
$oFactory = new ModelFactory($sSourceDirFull);
|
||||
$aModulesToCompile = $this->GetMFModulesToCompile($sSourceEnv, $sSourceDir);
|
||||
$oModule = null;
|
||||
foreach ($aModulesToCompile as $oModule) {
|
||||
if ($oModule instanceof MFDeltaModule) {
|
||||
// Just before loading the delta, let's save an image of the datamodel
|
||||
// in case there is no delta the operation will be done after the end of the loop
|
||||
$oFactory->SaveToFile(utils::GetDataPath().'datamodel-'.$this->sBuildEnv.'.xml');
|
||||
}
|
||||
$oFactory->LoadModule($oModule);
|
||||
}
|
||||
|
||||
if (!is_null($oModule) && ($oModule instanceof MFDeltaModule)) {
|
||||
// A delta was loaded, let's save a second copy of the datamodel
|
||||
$oFactory->SaveToFile(utils::GetDataPath().'datamodel-'.$this->sBuildEnv.'-with-delta.xml');
|
||||
} else {
|
||||
// No delta was loaded, let's save the datamodel now
|
||||
$oFactory->SaveToFile(utils::GetDataPath().'datamodel-'.$this->sBuildEnv.'.xml');
|
||||
}
|
||||
|
||||
$sBuildDir = APPROOT.'env-'.$this->sBuildEnv;
|
||||
self::MakeDirSafe($sBuildDir);
|
||||
$bSkipTempDir = ($this->sFinalEnv != $this->sBuildEnv); // No need for a temporary directory if sBuildEnv is already a temporary directory
|
||||
$oMFCompiler = new MFCompiler($oFactory, $this->sFinalEnv);
|
||||
$oMFCompiler->Compile($sBuildDir, null, $bUseSymLinks, $bSkipTempDir);
|
||||
|
||||
MetaModel::ResetAllCaches($this->sBuildEnv);
|
||||
|
||||
return array_keys($aModulesToCompile);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param array $aRemovedExtensionCodes
|
||||
* @param array $aSelectedModules
|
||||
* @param string $sSourceDir
|
||||
* @param string $sExtensionDir
|
||||
* @param boolean $bUseSymbolicLinks
|
||||
*
|
||||
* @return void
|
||||
* @throws \ConfigException
|
||||
* @throws \CoreException
|
||||
*
|
||||
*/
|
||||
public function DoCompile(array $aRemovedExtensionCodes, array $aSelectedModules, string $sSourceDir, string $sExtensionDir, bool $bUseSymbolicLinks = false): void
|
||||
{
|
||||
SetupLog::Info('Compiling data model.');
|
||||
|
||||
$sEnvironment = $this->sBuildEnv;
|
||||
$sBuildPath = $this->GetBuildDir();
|
||||
|
||||
$sSourcePath = APPROOT.$sSourceDir;
|
||||
$aDirsToScan = [$sSourcePath];
|
||||
$sExtensionsPath = APPROOT.$sExtensionDir;
|
||||
if (is_dir($sExtensionsPath)) {
|
||||
// if the extensions dir exists, scan it for additional modules as well
|
||||
$aDirsToScan[] = $sExtensionsPath;
|
||||
}
|
||||
$sExtraPath = APPROOT.'/data/'.$sEnvironment.'-modules/';
|
||||
if (is_dir($sExtraPath)) {
|
||||
// if the extra dir exists, scan it for additional modules as well
|
||||
$aDirsToScan[] = $sExtraPath;
|
||||
}
|
||||
|
||||
if (!is_dir($sSourcePath)) {
|
||||
throw new Exception("Failed to find the source directory '$sSourcePath', please check the rights of the web server");
|
||||
}
|
||||
|
||||
if (!is_dir($sBuildPath)) {
|
||||
if (!mkdir($sBuildPath)) {
|
||||
throw new Exception("Failed to create directory '$sBuildPath', please check the rights of the web server");
|
||||
} else {
|
||||
// adjust the rights if and only if the directory was just created
|
||||
// owner:rwx user/group:rx
|
||||
chmod($sBuildPath, 0755);
|
||||
}
|
||||
} elseif ($this->IsInItop($sBuildPath)) {
|
||||
// If the directory is under the root folder - as expected - let's clean-it before compiling
|
||||
SetupUtils::tidydir($sBuildPath);
|
||||
}
|
||||
|
||||
$oExtensionsMap = new iTopExtensionsMap('production', $aDirsToScan);
|
||||
// Removed modules are stored as static for FindModules()
|
||||
$oExtensionsMap->DeclareExtensionAsRemoved($aRemovedExtensionCodes);
|
||||
|
||||
$oFactory = new ModelFactory($aDirsToScan);
|
||||
|
||||
$oDictModule = new MFDictModule('dictionaries', 'iTop Dictionaries', APPROOT.'dictionaries');
|
||||
$oFactory->LoadModule($oDictModule);
|
||||
|
||||
$sDeltaFile = APPROOT.'core/datamodel.core.xml';
|
||||
if (file_exists($sDeltaFile)) {
|
||||
$oCoreModule = new MFCoreModule('core', 'Core Module', $sDeltaFile);
|
||||
$oFactory->LoadModule($oCoreModule);
|
||||
}
|
||||
$sDeltaFile = APPROOT.'application/datamodel.application.xml';
|
||||
if (file_exists($sDeltaFile)) {
|
||||
$oApplicationModule = new MFCoreModule('application', 'Application Module', $sDeltaFile);
|
||||
$oFactory->LoadModule($oApplicationModule);
|
||||
}
|
||||
|
||||
$aModules = $oFactory->FindModules();
|
||||
|
||||
foreach ($aModules as $oModule) {
|
||||
$sModule = $oModule->GetName();
|
||||
if (in_array($sModule, $aSelectedModules)) {
|
||||
$oFactory->LoadModule($oModule);
|
||||
}
|
||||
}
|
||||
|
||||
// Dump the "reference" model, just before loading any actual delta
|
||||
$oFactory->SaveToFile(utils::GetDataPath().'datamodel-'.$sEnvironment.'.xml');
|
||||
|
||||
$sDeltaFile = utils::GetDataPath().$sEnvironment.'.delta.xml';
|
||||
if (file_exists($sDeltaFile)) {
|
||||
$oDelta = new MFDeltaModule($sDeltaFile);
|
||||
$oFactory->LoadModule($oDelta);
|
||||
$oFactory->SaveToFile(utils::GetDataPath().'datamodel-'.$sEnvironment.'-with-delta.xml');
|
||||
}
|
||||
|
||||
$oMFCompiler = new MFCompiler($oFactory, $sEnvironment);
|
||||
$oMFCompiler->Compile($sBuildPath, null, $bUseSymbolicLinks, false, false);
|
||||
SetupLog::Info("Data model successfully compiled to '$sBuildPath'.");
|
||||
|
||||
$sCacheDir = APPROOT.'/data/cache-'.$sEnvironment.'/';
|
||||
SetupUtils::builddir($sCacheDir);
|
||||
SetupUtils::tidydir($sCacheDir);
|
||||
|
||||
|
||||
// Set an "Instance UUID" identifying this machine based on a file located in the data directory
|
||||
$sInstanceUUIDFile = utils::GetDataPath().'instance.txt';
|
||||
SetupUtils::builddir(utils::GetDataPath());
|
||||
if (!file_exists($sInstanceUUIDFile)) {
|
||||
$sInstanceUUID = utils::CreateUUID('filesystem');
|
||||
file_put_contents($sInstanceUUIDFile, $sInstanceUUID);
|
||||
}
|
||||
}
|
||||
|
||||
protected function IsInItop(string $sPath): bool
|
||||
{
|
||||
$sFileRealPath = realpath($sPath);
|
||||
if ($sFileRealPath === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$sRealBasePath = realpath(APPROOT); // avoid problems when having '/' on Windows for example
|
||||
if (!self::StartsWith($sFileRealPath, $sRealBasePath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected static function StartsWith(string $sHaystack, string $sNeedle): bool
|
||||
{
|
||||
if (strlen($sNeedle) > strlen($sHaystack)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return substr_compare($sHaystack, $sNeedle, 0, strlen($sNeedle)) === 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,14 +45,22 @@ class ApplicationInstallSequencer extends StepSequencer
|
||||
|
||||
protected Config $oConfig;
|
||||
|
||||
protected RunTimeEnvironment $oRunTimeEnvironment;
|
||||
|
||||
/**
|
||||
* @param \Parameters $oParams
|
||||
*
|
||||
* @throws \ConfigException
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function __construct(Parameters $oParams)
|
||||
public function __construct(Parameters $oParams, ?RunTimeEnvironment $oRunTimeEnvironment = null)
|
||||
{
|
||||
if (is_null($oRunTimeEnvironment)) {
|
||||
$sEnvironment = $oParams->Get('target_env', 'production');
|
||||
$oRunTimeEnvironment = new RunTimeEnvironment($sEnvironment, false);
|
||||
}
|
||||
$this->oRunTimeEnvironment = $oRunTimeEnvironment;
|
||||
|
||||
$this->oParams = $oParams;
|
||||
|
||||
$aParamValues = $oParams->GetParamForConfigArray();
|
||||
@@ -99,26 +107,7 @@ class ApplicationInstallSequencer extends StepSequencer
|
||||
return $oConfig;
|
||||
}
|
||||
|
||||
protected function DoLogParameters($sPrefix = 'install-', $sOperation = 'Installation')
|
||||
{
|
||||
// Log the parameters...
|
||||
$oDoc = new DOMDocument('1.0', 'UTF-8');
|
||||
$oDoc->preserveWhiteSpace = false;
|
||||
$oDoc->formatOutput = true;
|
||||
$this->oParams->ToXML($oDoc, null, 'installation');
|
||||
$sXML = $oDoc->saveXML();
|
||||
$sSafeXml = preg_replace("|<pwd>([^<]*)</pwd>|", "<pwd>**removed**</pwd>", $sXML);
|
||||
SetupLog::Info("======= ".$sOperation." starts =======\nParameters:\n$sSafeXml\n");
|
||||
|
||||
// Save the response file as a stand-alone file as well
|
||||
$sFileName = $sPrefix.date('Y-m-d');
|
||||
$index = 0;
|
||||
while (file_exists(APPROOT.'log/'.$sFileName.'.xml')) {
|
||||
$index++;
|
||||
$sFileName = $sPrefix.date('Y-m-d').'-'.$index;
|
||||
}
|
||||
file_put_contents(APPROOT.'log/'.$sFileName.'.xml', $sSafeXml);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the next step of the installation and reports about the progress
|
||||
@@ -137,6 +126,14 @@ class ApplicationInstallSequencer extends StepSequencer
|
||||
$this->EnterReadOnlyMode();
|
||||
switch ($sStep) {
|
||||
case '':
|
||||
if (in_array('log-parameters', $this->oParams->Get('optional_steps', []))) {
|
||||
return $this->GetNextStep('log-parameters', 'Log parameters', 0);
|
||||
}
|
||||
return $this->GetNextStep('copy', 'Copying data model files', 5);
|
||||
|
||||
case 'log-parameters':
|
||||
$this->DoLogParameters('data-audit-', 'Data Audit');
|
||||
return $this->GetNextStep('copy', 'Copying data model files', 5);
|
||||
|
||||
$this->DoLogParameters();
|
||||
|
||||
@@ -374,26 +371,6 @@ class ApplicationInstallSequencer extends StepSequencer
|
||||
SetupUtils::ExitReadOnlyMode();
|
||||
}
|
||||
|
||||
protected function DoCopy($aCopies)
|
||||
{
|
||||
$aReports = [];
|
||||
foreach ($aCopies as $aCopy) {
|
||||
$sSource = $aCopy['source'];
|
||||
$sDestination = APPROOT.$aCopy['destination'];
|
||||
|
||||
SetupUtils::builddir($sDestination);
|
||||
SetupUtils::tidydir($sDestination);
|
||||
SetupUtils::copydir($sSource, $sDestination);
|
||||
$aReports[] = "'{$aCopy['source']}' to '{$aCopy['destination']}' (OK)";
|
||||
}
|
||||
if (count($aReports) > 0) {
|
||||
$sReport = "Copies: ".count($aReports).': '.implode('; ', $aReports);
|
||||
} else {
|
||||
$sReport = "No file copy";
|
||||
}
|
||||
return $sReport;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sBackupFileFormat
|
||||
* @param string $sSourceConfigFile
|
||||
@@ -416,160 +393,6 @@ class ApplicationInstallSequencer extends StepSequencer
|
||||
$oBackup->CreateCompressedBackup($sTargetFile, $sSourceConfigFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $aRemovedExtensionCodes
|
||||
* @param array $aSelectedModules
|
||||
* @param string $sSourceDir
|
||||
* @param string $sExtensionDir
|
||||
* @param boolean $bUseSymbolicLinks
|
||||
*
|
||||
* @return void
|
||||
* @throws \ConfigException
|
||||
* @throws \CoreException
|
||||
*
|
||||
* @since 3.1.0 N°2013 added the aParamValues param
|
||||
*/
|
||||
protected function DoCompile($aRemovedExtensionCodes, $aSelectedModules, $sSourceDir, $sExtensionDir, $bUseSymbolicLinks = null, $bEnterMaintenanceMode = true)
|
||||
{
|
||||
/**
|
||||
* @since 3.2.0 move the ContextTag init at the very beginning of the method
|
||||
* @noinspection PhpUnusedLocalVariableInspection
|
||||
*/
|
||||
$oContextTag = new ContextTag(ContextTag::TAG_SETUP);
|
||||
|
||||
SetupLog::Info("Compiling data model.");
|
||||
|
||||
require_once(APPROOT.'setup/modulediscovery.class.inc.php');
|
||||
require_once(APPROOT.'setup/modelfactory.class.inc.php');
|
||||
require_once(APPROOT.'setup/compiler.class.inc.php');
|
||||
|
||||
$aParamValues = $this->oParams->GetParamForConfigArray();
|
||||
$sEnvironment = $this->GetTargetEnv();
|
||||
$sTargetDir = $this->GetTargetDir();
|
||||
|
||||
if (empty($sSourceDir) || empty($sTargetDir)) {
|
||||
throw new Exception("missing parameter source_dir and/or target_dir");
|
||||
}
|
||||
|
||||
$sSourcePath = APPROOT.$sSourceDir;
|
||||
$aDirsToScan = [$sSourcePath];
|
||||
$sExtensionsPath = APPROOT.$sExtensionDir;
|
||||
if (is_dir($sExtensionsPath)) {
|
||||
// if the extensions dir exists, scan it for additional modules as well
|
||||
$aDirsToScan[] = $sExtensionsPath;
|
||||
}
|
||||
$sExtraPath = APPROOT.'/data/'.$sEnvironment.'-modules/';
|
||||
if (is_dir($sExtraPath)) {
|
||||
// if the extra dir exists, scan it for additional modules as well
|
||||
$aDirsToScan[] = $sExtraPath;
|
||||
}
|
||||
$sTargetPath = APPROOT.$sTargetDir;
|
||||
|
||||
if (!is_dir($sSourcePath)) {
|
||||
throw new Exception("Failed to find the source directory '$sSourcePath', please check the rights of the web server");
|
||||
}
|
||||
|
||||
$bIsAlreadyInMaintenanceMode = SetupUtils::IsInMaintenanceMode();
|
||||
|
||||
if (($sEnvironment == 'production') && !$bIsAlreadyInMaintenanceMode) {
|
||||
$sConfigFilePath = utils::GetConfigFilePath($sEnvironment);
|
||||
if (is_file($sConfigFilePath)) {
|
||||
$oConfig = new Config($sConfigFilePath);
|
||||
$oConfig->UpdateFromParams($aParamValues);
|
||||
if ($bEnterMaintenanceMode) {
|
||||
SetupUtils::EnterMaintenanceMode($oConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (!is_dir($sTargetPath)) {
|
||||
if (!mkdir($sTargetPath)) {
|
||||
throw new Exception("Failed to create directory '$sTargetPath', please check the rights of the web server");
|
||||
} else {
|
||||
// adjust the rights if and only if the directory was just created
|
||||
// owner:rwx user/group:rx
|
||||
chmod($sTargetPath, 0755);
|
||||
}
|
||||
} elseif (substr($sTargetPath, 0, strlen(APPROOT)) == APPROOT) {
|
||||
// If the directory is under the root folder - as expected - let's clean-it before compiling
|
||||
SetupUtils::tidydir($sTargetPath);
|
||||
}
|
||||
|
||||
$oExtensionsMap = new iTopExtensionsMap('production', $aDirsToScan);
|
||||
$oExtensionsMap->DeclareExtensionAsRemoved($aRemovedExtensionCodes);
|
||||
|
||||
$oFactory = new ModelFactory($aDirsToScan);
|
||||
|
||||
$oDictModule = new MFDictModule('dictionaries', 'iTop Dictionaries', APPROOT.'dictionaries');
|
||||
$oFactory->LoadModule($oDictModule);
|
||||
|
||||
$sDeltaFile = APPROOT.'core/datamodel.core.xml';
|
||||
if (file_exists($sDeltaFile)) {
|
||||
$oCoreModule = new MFCoreModule('core', 'Core Module', $sDeltaFile);
|
||||
$oFactory->LoadModule($oCoreModule);
|
||||
}
|
||||
$sDeltaFile = APPROOT.'application/datamodel.application.xml';
|
||||
if (file_exists($sDeltaFile)) {
|
||||
$oApplicationModule = new MFCoreModule('application', 'Application Module', $sDeltaFile);
|
||||
$oFactory->LoadModule($oApplicationModule);
|
||||
}
|
||||
|
||||
$aModules = $oFactory->FindModules();
|
||||
|
||||
foreach ($aModules as $oModule) {
|
||||
$sModule = $oModule->GetName();
|
||||
if (in_array($sModule, $aSelectedModules)) {
|
||||
$oFactory->LoadModule($oModule);
|
||||
}
|
||||
}
|
||||
// Dump the "reference" model, just before loading any actual delta
|
||||
$oFactory->SaveToFile(utils::GetDataPath().'datamodel-'.$sEnvironment.'.xml');
|
||||
|
||||
$sDeltaFile = utils::GetDataPath().$sEnvironment.'.delta.xml';
|
||||
if (file_exists($sDeltaFile)) {
|
||||
$oDelta = new MFDeltaModule($sDeltaFile);
|
||||
$oFactory->LoadModule($oDelta);
|
||||
$oFactory->SaveToFile(utils::GetDataPath().'datamodel-'.$sEnvironment.'-with-delta.xml');
|
||||
}
|
||||
|
||||
$oMFCompiler = new MFCompiler($oFactory, $sEnvironment);
|
||||
$oMFCompiler->Compile($sTargetPath, null, $bUseSymbolicLinks, false, $bEnterMaintenanceMode);
|
||||
//$aCompilerLog = $oMFCompiler->GetLog();
|
||||
//SetupLog::Info(implode("\n", $aCompilerLog));
|
||||
SetupLog::Info("Data model successfully compiled to '$sTargetPath'.");
|
||||
|
||||
$sCacheDir = APPROOT.'/data/cache-'.$sEnvironment.'/';
|
||||
SetupUtils::builddir($sCacheDir);
|
||||
SetupUtils::tidydir($sCacheDir);
|
||||
} catch (Exception $e) {
|
||||
if (($sEnvironment == 'production') && !$bIsAlreadyInMaintenanceMode) {
|
||||
SetupUtils::ExitMaintenanceMode();
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
|
||||
// Special case to patch a ugly patch in itop-config-mgmt
|
||||
$sFileToPatch = $sTargetPath.'/itop-config-mgmt-1.0.0/model.itop-config-mgmt.php';
|
||||
if (file_exists($sFileToPatch)) {
|
||||
$sContent = file_get_contents($sFileToPatch);
|
||||
|
||||
$sContent = str_replace("require_once(APPROOT.'modules/itop-welcome-itil/model.itop-welcome-itil.php');", "//\n// The line below is no longer needed in iTop 2.0 -- patched by the setup program\n// require_once(APPROOT.'modules/itop-welcome-itil/model.itop-welcome-itil.php');", $sContent);
|
||||
|
||||
file_put_contents($sFileToPatch, $sContent);
|
||||
}
|
||||
|
||||
// Set an "Instance UUID" identifying this machine based on a file located in the data directory
|
||||
$sInstanceUUIDFile = utils::GetDataPath().'instance.txt';
|
||||
SetupUtils::builddir(utils::GetDataPath());
|
||||
if (!file_exists($sInstanceUUIDFile)) {
|
||||
$sIntanceUUID = utils::CreateUUID('filesystem');
|
||||
file_put_contents($sInstanceUUIDFile, $sIntanceUUID);
|
||||
}
|
||||
if (($sEnvironment == 'production') && !$bIsAlreadyInMaintenanceMode && $bEnterMaintenanceMode) {
|
||||
SetupUtils::ExitMaintenanceMode();
|
||||
}
|
||||
}
|
||||
|
||||
protected function GetModelInfoPath(string $sEnv): string
|
||||
{
|
||||
return APPROOT."data/beforecompilation_".$sEnv."_modelinfo.json";
|
||||
|
||||
@@ -25,9 +25,6 @@ require_once APPROOT.'setup/feature_removal/SetupAudit.php';
|
||||
require_once(APPROOT.'setup/sequencers/StepSequencer.php');
|
||||
require_once(APPROOT.'setup/sequencers/ApplicationInstallSequencer.php');
|
||||
|
||||
use Combodo\iTop\Setup\FeatureRemoval\DryRemovalRuntimeEnvironment;
|
||||
use Combodo\iTop\Setup\FeatureRemoval\SetupAudit;
|
||||
|
||||
class DataAuditSequencer extends ApplicationInstallSequencer
|
||||
{
|
||||
public const DATA_AUDIT_FAILED = 100;
|
||||
@@ -35,12 +32,14 @@ class DataAuditSequencer extends ApplicationInstallSequencer
|
||||
protected function GetTempEnv()
|
||||
{
|
||||
$sTargetEnv = $this->GetTargetEnv();
|
||||
|
||||
return $sTargetEnv.'-build';
|
||||
}
|
||||
|
||||
protected function GetTargetDir()
|
||||
{
|
||||
$sTargetEnv = $this->GetTempEnv();
|
||||
|
||||
return 'env-'.$sTargetEnv;
|
||||
}
|
||||
|
||||
@@ -56,102 +55,56 @@ class DataAuditSequencer extends ApplicationInstallSequencer
|
||||
public function ExecuteStep($sStep = '', $sInstallComment = null)
|
||||
{
|
||||
try {
|
||||
/**
|
||||
* @since 3.2.0 move the ContextTag init at the very beginning of the method
|
||||
* @noinspection PhpUnusedLocalVariableInspection
|
||||
*/
|
||||
$oContextTag = new ContextTag(ContextTag::TAG_SETUP);
|
||||
$fStart = microtime(true);
|
||||
SetupLog::Info("##### STEP {$sStep} start");
|
||||
switch ($sStep) {
|
||||
case '':
|
||||
$this->DoLogParameters('data-audit-', 'Data Audit');
|
||||
return $this->GetNextStep('copy', 'Copying data model files', 5);
|
||||
|
||||
$aResult = [
|
||||
'status' => self::OK,
|
||||
'message' => '',
|
||||
'percentage-completed' => 20,
|
||||
'next-step' => 'compile',
|
||||
'next-step-label' => 'Compiling the data model',
|
||||
];
|
||||
|
||||
break;
|
||||
case 'copy':
|
||||
$this->oRunTimeEnvironment->CopySetupFiles();
|
||||
return $this->GetNextStep('compile', 'Compiling the data model', 20, 'Copying...');
|
||||
|
||||
case 'compile':
|
||||
$aSelectedModules = $this->oParams->Get('selected_modules');
|
||||
$sSourceDir = $this->oParams->Get('source_dir', 'datamodels/latest');
|
||||
$sExtensionDir = $this->oParams->Get('extensions_dir', 'extensions');
|
||||
$aMiscOptions = $this->oParams->Get('options', []);
|
||||
$aRemovedExtensionCodes = $this->oParams->Get('removed_extensions', []);
|
||||
|
||||
$this->DoCompile(
|
||||
$bUseSymbolicLinks = $aMiscOptions['symlinks'] ?? false;
|
||||
$sMessage = $bUseSymbolicLinks ? '' : 'Using symbolic links instead of copying data model files (for developers only!)';
|
||||
$this->oRunTimeEnvironment->DoCompile(
|
||||
$aRemovedExtensionCodes,
|
||||
$aSelectedModules,
|
||||
$sSourceDir,
|
||||
$sExtensionDir,
|
||||
false,
|
||||
false
|
||||
$bUseSymbolicLinks
|
||||
);
|
||||
return $this->GetNextStep('setup-audit', 'Checking data consistency with the new data model', 70, $sMessage);
|
||||
|
||||
$aResult = [
|
||||
'status' => self::OK,
|
||||
'message' => '',
|
||||
'next-step' => 'write-config',
|
||||
'next-step-label' => 'Writing audit config',
|
||||
'percentage-completed' => 40,
|
||||
];
|
||||
break;
|
||||
case 'write-config':
|
||||
$this->DoWriteConfig();
|
||||
$aResult = [
|
||||
'status' => self::OK,
|
||||
'message' => '',
|
||||
'next-step' => 'setup-audit',
|
||||
'next-step-label' => 'Checking data consistency with the new data model',
|
||||
'percentage-completed' => 60,
|
||||
];
|
||||
break;
|
||||
case 'setup-audit':
|
||||
$this->DoSetupAudit();
|
||||
$aResult = [
|
||||
'status' => self::OK,
|
||||
'message' => '',
|
||||
'next-step' => 'cleanup',
|
||||
'next-step-label' => 'Temporary folders cleanup',
|
||||
'percentage-completed' => 80,
|
||||
];
|
||||
break;
|
||||
case 'cleanup' :
|
||||
$this->DoCleanup();
|
||||
$aResult = [
|
||||
'status' => self::OK,
|
||||
'message' => '',
|
||||
'next-step' => '',
|
||||
'next-step-label' => 'Completed',
|
||||
'percentage-completed' => 100,
|
||||
];
|
||||
break;
|
||||
default:
|
||||
$aResult = [
|
||||
'status' => self::ERROR,
|
||||
'message' => '',
|
||||
'next-step' => '',
|
||||
'next-step-label' => "Unknown setup step '$sStep'.",
|
||||
'percentage-completed' => 100,
|
||||
];
|
||||
break;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$aResult = [
|
||||
'status' => self::ERROR,
|
||||
'message' => $e->getMessage(),
|
||||
'next-step' => '',
|
||||
'next-step-label' => '',
|
||||
'percentage-completed' => 100,
|
||||
'error_code' => $e->getCode(),
|
||||
];
|
||||
$this->oRunTimeEnvironment->DataToCleanupAudit();
|
||||
return $this->GetNextStep('', 'Completed', 100);
|
||||
|
||||
default:
|
||||
return $this->GetNextStep('', "Unknown setup step '$sStep'.", 100, '', self::ERROR);
|
||||
}
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$this->ReportException($e);
|
||||
} finally {
|
||||
$aResult = $this->GetNextStep('', '', 100, $e->getMessage(), self::ERROR);
|
||||
$aResult['error_code'] = $e->getCode();
|
||||
return $aResult;
|
||||
}
|
||||
finally {
|
||||
$fDuration = round(microtime(true) - $fStart, 2);
|
||||
SetupLog::Info("##### STEP {$sStep} duration: {$fDuration}s");
|
||||
}
|
||||
|
||||
return $aResult;
|
||||
}
|
||||
|
||||
protected function DoWriteConfig()
|
||||
@@ -167,57 +120,7 @@ class DataAuditSequencer extends ApplicationInstallSequencer
|
||||
|
||||
return $oConfig->WriteToFile($sTempConfigFileName);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function DoSetupAudit()
|
||||
{
|
||||
/**
|
||||
* @since 3.2.0 move the ContextTag init at the very beginning of the method
|
||||
* @noinspection PhpUnusedLocalVariableInspection
|
||||
*/
|
||||
$oContextTag = new ContextTag(ContextTag::TAG_SETUP);
|
||||
|
||||
$sPreviousEnvironment = $this->GetTargetEnv();
|
||||
|
||||
$oSetupAudit = new SetupAudit($sPreviousEnvironment);
|
||||
|
||||
//Make sure the MetaModel is started before analysing for issues
|
||||
$sConfFile = utils::GetConfigFilePath($sPreviousEnvironment);
|
||||
MetaModel::Startup($sConfFile, false /* $bModelOnly */, false /* $bAllowCache */, false /* $bTraceSourceFiles */, $sPreviousEnvironment);
|
||||
$oSetupAudit->GetIssues(true);
|
||||
$iCount = $oSetupAudit->GetDataToCleanupCount();
|
||||
|
||||
if ($iCount > 0) {
|
||||
throw new Exception("$iCount elements require data adjustments or cleanup in the backoffice prior to upgrading iTop", static::DATA_AUDIT_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
protected function DoCleanup()
|
||||
{
|
||||
$sEnv = $this->GetTempEnv();
|
||||
|
||||
//keep this folder empty
|
||||
SetupUtils::tidydir(APPROOT."/env-$sEnv");
|
||||
|
||||
$aFolders = [
|
||||
APPROOT."/data/$sEnv-modules",
|
||||
APPROOT."/data/cache-$sEnv",
|
||||
APPROOT."/conf/$sEnv",
|
||||
];
|
||||
foreach ($aFolders as $sFolder) {
|
||||
SetupUtils::tidydir($sFolder);
|
||||
SetupUtils::rmdir_safe($sFolder);
|
||||
}
|
||||
|
||||
$sFiles = [
|
||||
APPROOT."/data/datamodel-$sEnv.xml",
|
||||
APPROOT."/data/$sEnv.delta.prev.xml",
|
||||
];
|
||||
foreach ($sFiles as $sFile) {
|
||||
if (is_file($sFile)) {
|
||||
@unlink($sFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,5 +102,37 @@ abstract class StepSequencer
|
||||
return ($iOverallStatus == self::OK);
|
||||
}
|
||||
|
||||
protected function GetNextStep(string $sNextStep, string $sNextStepLabel, int $iPercentComplete, string $sMessage = '', int $iStatus = self::OK): array
|
||||
{
|
||||
return [
|
||||
'status' => $iStatus,
|
||||
'message' => $sMessage,
|
||||
'next-step' => $sNextStep,
|
||||
'next-step-label' => $sNextStepLabel,
|
||||
'percentage-completed' => $iPercentComplete,
|
||||
];
|
||||
}
|
||||
|
||||
protected function DoLogParameters($sPrefix = 'install-', $sOperation = 'Installation')
|
||||
{
|
||||
// Log the parameters...
|
||||
$oDoc = new DOMDocument('1.0', 'UTF-8');
|
||||
$oDoc->preserveWhiteSpace = false;
|
||||
$oDoc->formatOutput = true;
|
||||
$this->oParams->ToXML($oDoc, null, 'installation');
|
||||
$sXML = $oDoc->saveXML();
|
||||
$sSafeXml = preg_replace('|<pwd>([^<]*)</pwd>|', '<pwd>**removed**</pwd>', $sXML);
|
||||
SetupLog::Info('======= '.$sOperation." starts =======\nParameters:\n$sSafeXml\n");
|
||||
|
||||
// Save the response file as a stand-alone file as well
|
||||
$sFileName = $sPrefix.date('Y-m-d');
|
||||
$index = 0;
|
||||
while (file_exists(APPROOT.'log/'.$sFileName.'.xml')) {
|
||||
$index++;
|
||||
$sFileName = $sPrefix.date('Y-m-d').'-'.$index;
|
||||
}
|
||||
file_put_contents(APPROOT.'log/'.$sFileName.'.xml', $sSafeXml);
|
||||
}
|
||||
|
||||
abstract public function ExecuteStep($sStep = '', $sComment = null);
|
||||
}
|
||||
|
||||
@@ -817,6 +817,11 @@ class SetupUtils
|
||||
}
|
||||
}
|
||||
|
||||
public static function CopyFile(string $sSource, string $sDest, bool $bUseSymbolicLinks = false): bool
|
||||
{
|
||||
return self::copydir($sSource, $sDest, $bUseSymbolicLinks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to copy a directory to a target directory, skipping .SVN files (for developer's comfort!)
|
||||
* Returns true if successful
|
||||
@@ -826,11 +831,13 @@ class SetupUtils
|
||||
* @return bool
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function copydir($sSource, $sDest, $bUseSymbolicLinks = false)
|
||||
public static function copydir(string $sSource, string $sDest, bool $bUseSymbolicLinks = false): bool
|
||||
{
|
||||
if (is_dir($sSource)) {
|
||||
if (!is_dir($sDest)) {
|
||||
mkdir($sDest, 0777 /* Default */, true);
|
||||
} else {
|
||||
SetupUtils::tidydir($sDest);
|
||||
}
|
||||
$aFiles = scandir($sSource);
|
||||
if (sizeof($aFiles) > 0) {
|
||||
@@ -839,24 +846,13 @@ class SetupUtils
|
||||
// Skip
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_dir($sSource.'/'.$sFile)) {
|
||||
// Recurse
|
||||
self::copydir($sSource.'/'.$sFile, $sDest.'/'.$sFile, $bUseSymbolicLinks);
|
||||
} else {
|
||||
if ($bUseSymbolicLinks) {
|
||||
if (function_exists('symlink')) {
|
||||
if (file_exists($sDest.'/'.$sFile)) {
|
||||
unlink($sDest.'/'.$sFile);
|
||||
}
|
||||
symlink($sSource.'/'.$sFile, $sDest.'/'.$sFile);
|
||||
} else {
|
||||
throw(new Exception("Error, cannot *copy* '$sSource/$sFile' to '$sDest/$sFile' using symbolic links, 'symlink' is not supported on this system."));
|
||||
}
|
||||
symlink($sSource.'/'.$sFile, $sDest.'/'.$sFile);
|
||||
} else {
|
||||
if (is_link($sDest.'/'.$sFile)) {
|
||||
unlink($sDest.'/'.$sFile);
|
||||
}
|
||||
copy($sSource.'/'.$sFile, $sDest.'/'.$sFile);
|
||||
}
|
||||
}
|
||||
@@ -865,11 +861,7 @@ class SetupUtils
|
||||
return true;
|
||||
} elseif (is_file($sSource)) {
|
||||
if ($bUseSymbolicLinks) {
|
||||
if (function_exists('symlink')) {
|
||||
return symlink($sSource, $sDest);
|
||||
} else {
|
||||
throw(new Exception("Error, cannot *copy* '$sSource' to '$sDest' using symbolic links, 'symlink' is not supported on this system."));
|
||||
}
|
||||
return symlink($sSource, $sDest);
|
||||
} else {
|
||||
return copy($sSource, $sDest);
|
||||
}
|
||||
|
||||
@@ -67,6 +67,10 @@ abstract class AbstractWizStepInstall extends WizardStep
|
||||
'copies' => $aCopies,
|
||||
// 'backup' => see below
|
||||
],
|
||||
'optional_steps' => [
|
||||
''
|
||||
// 'backup' => see below
|
||||
],
|
||||
'source_dir' => str_replace(APPROOT, '', $sSourceDir),
|
||||
'datamodel_version' => $this->oWizard->GetParameter('datamodel_version'), //TODO: let the installer compute this automatically...
|
||||
'previous_configuration_file' => $sPreviousConfigurationFile,
|
||||
|
||||
Reference in New Issue
Block a user