Refactor Core Update (+3 squashed commit)

Squashed commit:

[e1cbfe93f] Refactor Core Update

[41ec2adf7] Refactor Core Update

[ca6cefca3] Refactor Core Update
This commit is contained in:
Eric
2019-12-19 09:53:09 +01:00
parent 69551378c2
commit 7f3efe59ab
18 changed files with 490 additions and 368 deletions

View File

@@ -17,7 +17,6 @@
<module>itop-welcome-itil</module>
<module>itop-tickets</module>
<module>itop-files-information</module>
<module>itop-twig-base</module>
<module>combodo-db-tools</module>
<module>itop-core-update</module>
<module>itop-hub-connector</module>

View File

@@ -8,8 +8,20 @@ namespace Combodo\iTop\CoreUpdate;
use Combodo\iTop\CoreUpdate\Controller\AjaxController;
use ContextTag;
use MetaModel;
use utils;
if (!defined('MODULESROOT'))
{
define('MODULESROOT', APPROOT.'env-production/');
}
require_once(MODULESROOT.'itop-core-update/src/Service/RunTimeEnvironmentCoreUpdater.php');
require_once(MODULESROOT.'itop-core-update/src/Service/CoreUpdater.php');
require_once(MODULESROOT.'itop-core-update/src/Controller/AjaxController.php');
MetaModel::LoadConfig(utils::GetConfig());
require_once(APPROOT.'application/startup.inc.php');
new ContextTag('Setup');
$oUpdateController = new AjaxController();

View File

@@ -20,7 +20,6 @@ SetupWebPage::AddModule(
// Setup
//
'dependencies' => array(
'itop-twig-base/1.0.0',
'itop-files-information/1.0.0',
'combodo-db-tools/1.0.8',
),

View File

@@ -8,11 +8,11 @@
namespace Combodo\iTop\CoreUpdate\Controller;
use Combodo\iTop\Application\TwigBase\Controller\Controller;
use Combodo\iTop\CoreUpdate\Service\CoreUpdater;
use Combodo\iTop\DBTools\Service\DBToolsUtils;
use Combodo\iTop\FilesInformation\Service\FileNotExistException;
use Combodo\iTop\FilesInformation\Service\FilesInformation;
use Combodo\iTop\TwigBase\Controller\Controller;
use Dict;
use Exception;
use IssueLog;
@@ -22,61 +22,63 @@ use utils;
class AjaxController extends Controller
{
const LOCAL_DIR = __DIR__;
public function __construct()
{
parent::__construct();
$this->InitFromModule();
}
public function OperationCanUpdateCore()
{
$aParams = array();
public function OperationCanUpdateCore()
{
$aParams = array();
try
{
$bCanUpdateCore = FilesInformation::CanUpdateCore($sMessage);
$aParams['bStatus'] = $bCanUpdateCore;
if ($bCanUpdateCore)
{
$aParams['sMessage'] = Dict::S('iTopUpdate:UI:CanCoreUpdate:Yes');
}
else
{
$aParams['sMessage'] = Dict::Format('iTopUpdate:UI:CanCoreUpdate:No', $sMessage);
}
}
catch (FileNotExistException $e)
{
$aParams['bStatus'] = false;
$aParams['sMessage'] = Dict::Format('iTopUpdate:UI:CanCoreUpdate:ErrorFileNotExist', $e->getMessage());
}
catch(Exception $e)
{
$aParams['bStatus'] = false;
$aParams['sMessage'] = Dict::Format('iTopUpdate:UI:CanCoreUpdate:Error', $e->getMessage());
}
try
{
$bCanUpdateCore = FilesInformation::CanUpdateCore($sMessage);
$aParams['bStatus'] = $bCanUpdateCore;
if ($bCanUpdateCore)
{
$aParams['sMessage'] = Dict::S('iTopUpdate:UI:CanCoreUpdate:Yes');
}
else
{
$aParams['sMessage'] = Dict::Format('iTopUpdate:UI:CanCoreUpdate:No', $sMessage);
}
} catch (FileNotExistException $e)
{
$aParams['bStatus'] = false;
$aParams['sMessage'] = Dict::Format('iTopUpdate:UI:CanCoreUpdate:ErrorFileNotExist', $e->getMessage());
} catch (Exception $e)
{
$aParams['bStatus'] = false;
$aParams['sMessage'] = Dict::Format('iTopUpdate:UI:CanCoreUpdate:Error', $e->getMessage());
}
$this->DisplayJSONPage($aParams);
}
$this->DisplayJSONPage($aParams);
}
public function OperationGetItopDiskSpace()
{
$aParams = array();
$aParams['iItopDiskSpace'] = FilesInformation::GetItopDiskSpace();
$aParams['sItopDiskSpace'] = utils::BytesToFriendlyFormat($aParams['iItopDiskSpace']);
$this->DisplayJSONPage($aParams);
}
public function OperationGetItopDiskSpace()
{
$aParams = array();
$aParams['iItopDiskSpace'] = FilesInformation::GetItopDiskSpace();
$aParams['sItopDiskSpace'] = utils::BytesToFriendlyFormat($aParams['iItopDiskSpace']);
$this->DisplayJSONPage($aParams);
}
public function OperationGetDBDiskSpace()
{
$aParams = array();
$aParams['iDBDiskSpace'] = DBToolsUtils::GetDatabaseSize();
$aParams['sDBDiskSpace'] = utils::BytesToFriendlyFormat($aParams['iDBDiskSpace']);
$this->DisplayJSONPage($aParams);
}
public function OperationGetDBDiskSpace()
{
$aParams = array();
$aParams['iDBDiskSpace'] = DBToolsUtils::GetDatabaseSize();
$aParams['sDBDiskSpace'] = utils::BytesToFriendlyFormat($aParams['iDBDiskSpace']);
$this->DisplayJSONPage($aParams);
}
public function OperationGetCurrentVersion()
{
$aParams = array();
$aParams['sVersion'] = Dict::Format('UI:iTopVersion:Long', ITOP_APPLICATION, ITOP_VERSION, ITOP_REVISION, ITOP_BUILD_DATE);
$this->DisplayJSONPage($aParams);
}
public function OperationGetCurrentVersion()
{
$aParams = array();
$aParams['sVersion'] = Dict::Format('UI:iTopVersion:Long', ITOP_APPLICATION, ITOP_VERSION, ITOP_REVISION, ITOP_BUILD_DATE);
$this->DisplayJSONPage($aParams);
}
public function OperationEnterMaintenance()
{
@@ -85,8 +87,7 @@ class AjaxController extends Controller
{
SetupUtils::EnterReadOnlyMode(MetaModel::GetConfig());
$iResponseCode = 200;
}
catch (Exception $e)
} catch (Exception $e)
{
IssueLog::Error("EnterMaintenance: ".$e->getMessage());
$aParams['sError'] = $e->getMessage();
@@ -102,8 +103,7 @@ class AjaxController extends Controller
{
SetupUtils::ExitReadOnlyMode();
$iResponseCode = 200;
}
catch (Exception $e)
} catch (Exception $e)
{
IssueLog::Error("ExitMaintenance: ".$e->getMessage());
$aParams['sError'] = $e->getMessage();
@@ -112,22 +112,21 @@ class AjaxController extends Controller
$this->DisplayJSONPage($aParams, $iResponseCode);
}
public function OperationBackup()
{
$aParams = array();
try
{
CoreUpdater::Backup();
$iResponseCode = 200;
}
catch (Exception $e)
{
IssueLog::Error("Backup: ".$e->getMessage());
$aParams['sError'] = $e->getMessage();
$iResponseCode = 500;
}
$this->DisplayJSONPage($aParams, $iResponseCode);
}
public function OperationBackup()
{
$aParams = array();
try
{
CoreUpdater::Backup();
$iResponseCode = 200;
} catch (Exception $e)
{
IssueLog::Error("Backup: ".$e->getMessage());
$aParams['sError'] = $e->getMessage();
$iResponseCode = 500;
}
$this->DisplayJSONPage($aParams, $iResponseCode);
}
public function OperationFilesArchive()
{
@@ -136,8 +135,7 @@ class AjaxController extends Controller
{
CoreUpdater::CreateItopArchive();
$iResponseCode = 200;
}
catch (Exception $e)
} catch (Exception $e)
{
IssueLog::Error("FilesArchive: ".$e->getMessage());
$aParams['sError'] = $e->getMessage();
@@ -146,39 +144,71 @@ class AjaxController extends Controller
$this->DisplayJSONPage($aParams, $iResponseCode);
}
public function OperationCopyFiles()
{
$aParams = array();
try
{
CoreUpdater::CopyCoreFiles();
$iResponseCode = 200;
}
catch (Exception $e)
{
IssueLog::Error("CopyFiles: ".$e->getMessage());
$aParams['sError'] = $e->getMessage();
$iResponseCode = 500;
}
public function OperationCopyFiles()
{
$aParams = array();
try
{
CoreUpdater::CopyCoreFiles();
$iResponseCode = 200;
} catch (Exception $e)
{
IssueLog::Error("CopyFiles: ".$e->getMessage());
$aParams['sError'] = $e->getMessage();
$iResponseCode = 500;
}
$this->DisplayJSONPage($aParams, $iResponseCode);
}
$this->DisplayJSONPage($aParams, $iResponseCode);
}
public function OperationCompile()
{
$aParams = array();
try
{
CoreUpdater::Compile();
$iResponseCode = 200;
}
catch (Exception $e)
{
IssueLog::Error("Compile: ".$e->getMessage());
$aParams['sError'] = $e->getMessage();
$iResponseCode = 500;
}
public function OperationCheckCompile()
{
$aParams = array();
try
{
CoreUpdater::CheckCompile();
$iResponseCode = 200;
} catch (Exception $e)
{
IssueLog::Error("Compile: ".$e->getMessage());
$aParams['sError'] = $e->getMessage();
$iResponseCode = 500;
}
$this->DisplayJSONPage($aParams, $iResponseCode);
}
$this->DisplayJSONPage($aParams, $iResponseCode);
}
public function OperationCompile()
{
$aParams = array();
try
{
CoreUpdater::Compile();
$iResponseCode = 200;
} catch (Exception $e)
{
IssueLog::Error("Compile: ".$e->getMessage());
$aParams['sError'] = $e->getMessage();
$iResponseCode = 500;
}
$this->DisplayJSONPage($aParams, $iResponseCode);
}
public function OperationUpdateDatabase()
{
$aParams = array();
try
{
CoreUpdater::UpdateDatabase();
$iResponseCode = 200;
} catch (Exception $e)
{
IssueLog::Error("Compile: ".$e->getMessage());
$aParams['sError'] = $e->getMessage();
$iResponseCode = 500;
}
$this->DisplayJSONPage($aParams, $iResponseCode);
}
}

View File

@@ -6,9 +6,9 @@
namespace Combodo\iTop\CoreUpdate\Controller;
use Combodo\iTop\Application\TwigBase\Controller\Controller;
use Combodo\iTop\CoreUpdate\Service\CoreUpdater;
use Combodo\iTop\DBTools\Service\DBToolsUtils;
use Combodo\iTop\TwigBase\Controller\Controller;
use Dict;
use Exception;
use SetupUtils;
@@ -16,7 +16,11 @@ use utils;
class UpdateController extends Controller
{
const LOCAL_DIR = __DIR__;
public function __construct()
{
parent::__construct();
$this->InitFromModule();
}
public function OperationSelectUpdateFile()
{

View File

@@ -1,6 +1,7 @@
<?php
/**
* iTop
*
* @copyright Copyright (C) 2010,2019 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*
@@ -26,14 +27,14 @@ require_once APPROOT.'setup/runtimeenv.class.inc.php';
final class CoreUpdater
{
const DOWNLOAD_DIR = APPROOT.'data/downloaded-core/';
const DOWNLOAD_DIR = APPROOT.'data/downloaded-core/';
const UPDATE_DIR = APPROOT.'data/core-update/';
/**
* @param bool $bDoBackup
*
* @throws \Exception
*/
/**
* @param bool $bDoBackup
*
* @throws \Exception
*/
public static function CopyCoreFiles()
{
set_time_limit(600);
@@ -69,19 +70,39 @@ final class CoreUpdater
FilesIntegrity::CheckInstallationIntegrity(APPROOT);
IssueLog::Info('itop-core-update: Files integrity OK');
}
catch (Exception $e)
} catch (Exception $e)
{
IssueLog::error($e->getMessage());
IssueLog::Info('itop-core-update: ended');
throw $e;
}
finally
} finally
{
self::RRmdir(self::UPDATE_DIR);
}
}
/**
* @throws \Exception
*/
public static function CheckCompile()
{
try
{
// Compile code in env-production-build
IssueLog::Info('itop-core-update: Check compilation');
$sTargetEnv = 'production';
$oRuntimeEnv = new RunTimeEnvironmentCoreUpdater($sTargetEnv, false);
$oRuntimeEnv->CheckDirectories($sTargetEnv);
$oRuntimeEnv->CompileFrom('production');
SetupUtils::tidydir(APPROOT."env-{$sTargetEnv}-build");
} catch (Exception $e)
{
IssueLog::error($e->getMessage());
throw $e;
}
}
/**
* @throws \Exception
*/
@@ -91,18 +112,44 @@ final class CoreUpdater
{
// Compile code
IssueLog::Info('itop-core-update: Start compilation');
IssueLog::Info('itop-core-update: Version Dev');
$sTargetEnv = 'production';
$oRuntimeEnv = new RunTimeEnvironmentCoreUpdater($sTargetEnv);
$oRuntimeEnv->CheckDirectories($sTargetEnv);
$oRuntimeEnv->CompileFrom('production');
IssueLog::Info('itop-core-update: Compilation done');
} catch (Exception $e)
{
IssueLog::error($e->getMessage());
throw $e;
}
}
/**
* @throws \Exception
*/
public static function UpdateDatabase()
{
try
{
// Compile code
IssueLog::Info('itop-core-update: Update database');
$sTargetEnv = 'production';
$oRuntimeEnv = new RunTimeEnvironmentCoreUpdater($sTargetEnv);
$oRuntimeEnv->CheckDirectories($sTargetEnv);
$oConfig = $oRuntimeEnv->MakeConfigFile($sTargetEnv.' (built on '.date('Y-m-d').')');
$oConfig->Set('access_mode', ACCESS_FULL);
$oRuntimeEnv->WriteConfigFileSafe($oConfig);
$oRuntimeEnv->InitDataModel($oConfig, true);
$aAvailableModules = $oRuntimeEnv->AnalyzeInstallation($oConfig, $oRuntimeEnv->GetBuildDir());
$sModulesDirToKeep = $oRuntimeEnv->GetBuildDir();
$aDirsToScanForModules = array(
$sModulesDirToKeep,
APPROOT.'extensions'
);
$aAvailableModules = $oRuntimeEnv->AnalyzeInstallation($oConfig, $aDirsToScanForModules);
$aSelectedModules = array();
foreach ($aAvailableModules as $sModuleId => $aModule)
{
@@ -142,9 +189,10 @@ final class CoreUpdater
$oRuntimeEnv->RecordInstallation($oConfig, $sDataModelVersion, $aSelectedModules,
$aSelectedExtensionCodes, 'Done by the iTop Core Updater');
IssueLog::Info('itop-core-update: Compilation done');
}
catch (Exception $e)
$oRuntimeEnv->Commit();
IssueLog::Info('itop-core-update: Update database done');
} catch (Exception $e)
{
IssueLog::error($e->getMessage());
throw $e;
@@ -186,87 +234,87 @@ final class CoreUpdater
*/
public static function Backup()
{
$sBackupName = self::GetBackupName();
$sBackupFile = self::GetBackupFile();
if (file_exists($sBackupFile))
{
@unlink($sBackupFile);
}
$sBackupName = self::GetBackupName();
$sBackupFile = self::GetBackupFile();
if (file_exists($sBackupFile))
{
@unlink($sBackupFile);
}
self::DoBackup($sBackupName);
self::DoBackup($sBackupName);
}
/**
* @throws \Exception
*/
public static function CreateItopArchive()
{
set_time_limit(0);
$sItopArchiveFile = self::GetItopArchiveFile();
if (file_exists($sItopArchiveFile))
{
@unlink($sItopArchiveFile);
}
{
set_time_limit(0);
$sItopArchiveFile = self::GetItopArchiveFile();
if (file_exists($sItopArchiveFile))
{
@unlink($sItopArchiveFile);
}
$sTempFile = sys_get_temp_dir().'/'.basename($sItopArchiveFile);
if (file_exists($sTempFile))
{
@unlink($sTempFile);
}
$sTempFile = sys_get_temp_dir().'/'.basename($sItopArchiveFile);
if (file_exists($sTempFile))
{
@unlink($sTempFile);
}
$aPathInfo = pathInfo(realpath(APPROOT));
$sParentPath = $aPathInfo['dirname'];
$sDirName = $aPathInfo['basename'];
$aPathInfo = pathInfo(realpath(APPROOT));
$sParentPath = $aPathInfo['dirname'];
$sDirName = $aPathInfo['basename'];
$oZipArchive = new ZipArchive();
$oZipArchive->open($sTempFile, ZIPARCHIVE::CREATE);
$oZipArchive->addEmptyDir($sDirName);
self::ZipFolder(realpath(APPROOT), $oZipArchive, strlen("$sParentPath/"));
$oZipArchive->close();
if (!file_exists($sTempFile))
{
IssueLog::Error("Failed to create itop archive $sTempFile");
}
$oZipArchive = new ZipArchive();
$oZipArchive->open($sTempFile, ZIPARCHIVE::CREATE);
$oZipArchive->addEmptyDir($sDirName);
self::ZipFolder(realpath(APPROOT), $oZipArchive, strlen("$sParentPath/"));
$oZipArchive->close();
if (@rename($sTempFile, $sItopArchiveFile))
{
IssueLog::Info("Archive $sItopArchiveFile Created");
}
else
{
IssueLog::Error("Failed to create archive $sItopArchiveFile");
}
}
if (!file_exists($sTempFile))
{
IssueLog::Error("Failed to create itop archive $sTempFile");
}
/**
*
* @param string $sTargetFile
* @throws Exception
*/
private static function DoBackup($sTargetFile)
{
// Make sure the target directory exists
$sBackupDir = dirname($sTargetFile);
SetupUtils::builddir($sBackupDir);
if (@rename($sTempFile, $sItopArchiveFile))
{
IssueLog::Info("Archive $sItopArchiveFile Created");
}
else
{
IssueLog::Error("Failed to create archive $sItopArchiveFile");
}
}
$oBackup = new DBBackup();
$oBackup->SetMySQLBinDir(MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'mysql_bindir', ''));
/**
*
* @param string $sTargetFile
*
* @throws Exception
*/
private static function DoBackup($sTargetFile)
{
// Make sure the target directory exists
$sBackupDir = dirname($sTargetFile);
SetupUtils::builddir($sBackupDir);
$oMutex = new iTopMutex('backup.'.utils::GetCurrentEnvironment());
$oMutex->Lock();
try
{
$oBackup->CreateCompressedBackup($sTargetFile);
IssueLog::Info('itop-core-update: Backup done: '.$sTargetFile);
}
catch (Exception $e)
{
$oMutex->Unlock();
throw $e;
}
$oMutex->Unlock();
}
$oBackup = new DBBackup();
$oBackup->SetMySQLBinDir(MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'mysql_bindir', ''));
$oMutex = new iTopMutex('backup.'.utils::GetCurrentEnvironment());
$oMutex->Lock();
try
{
$oBackup->CreateCompressedBackup($sTargetFile);
IssueLog::Info('itop-core-update: Backup done: '.$sTargetFile);
} catch (Exception $e)
{
$oMutex->Unlock();
throw $e;
}
$oMutex->Unlock();
}
/**
* @param $sSource
@@ -283,9 +331,9 @@ final class CoreUpdater
@mkdir($sDest, 0755);
}
$aFiles = scandir($sSource);
if(sizeof($aFiles) > 0 )
if (sizeof($aFiles) > 0)
{
foreach($aFiles as $sFile)
foreach ($aFiles as $sFile)
{
if ($sFile == '.' || $sFile == '..' || $sFile == '.svn' || $sFile == '.git')
{
@@ -315,7 +363,8 @@ final class CoreUpdater
}
}
public static function RRmdir($sDir) {
public static function RRmdir($sDir)
{
if (is_dir($sDir))
{
$oDir = @opendir($sDir);
@@ -368,9 +417,11 @@ final class CoreUpdater
* @param ZipArchive $oZipArchive
* @param int $iStrippedLength Number of text to be removed from the file path.
*/
private static function ZipFolder($sFolder, &$oZipArchive, $iStrippedLength) {
private static function ZipFolder($sFolder, &$oZipArchive, $iStrippedLength)
{
$oFolder = opendir($sFolder);
while (false !== ($sFile = readdir($oFolder))) {
while (false !== ($sFile = readdir($oFolder)))
{
if (($sFile == '.') || ($sFile == '..'))
{
continue;
@@ -385,9 +436,12 @@ final class CoreUpdater
// Remove prefix from file path before add to zip.
$sLocalPath = substr($sFilePath, $iStrippedLength);
if (is_file($sFilePath)) {
if (is_file($sFilePath))
{
$oZipArchive->addFile($sFilePath, $sLocalPath);
} elseif (is_dir($sFilePath)) {
}
elseif (is_dir($sFilePath))
{
// Add sub-directory.
$oZipArchive->addEmptyDir($sLocalPath);
self::ZipFolder($sFilePath, $oZipArchive, $iStrippedLength);
@@ -414,109 +468,106 @@ final class CoreUpdater
return $sItopArchiveFile;
}
/**
* @return string
*/
private static function GetBackupName()
{
$sBackupName = APPROOT.'data/backups/manual/backup-core-update';
return $sBackupName;
}
/**
* @return string
*/
private static function GetBackupName()
{
$sBackupName = APPROOT.'data/backups/manual/backup-core-update';
return $sBackupName;
}
/**
* @return string
*/
public static function GetBackupFile()
{
$sBackupFile = self::GetBackupName().'.tar.gz';
return $sBackupFile;
}
/**
* @return string
*/
public static function GetBackupFile()
{
$sBackupFile = self::GetBackupName().'.tar.gz';
return $sBackupFile;
}
/**
* @param $sArchiveFile
*
* @throws \Exception
*/
public static function ExtractDownloadedFile($sArchiveFile)
{
try
{
// Extract archive file
self::ExtractUpdateFile($sArchiveFile);
/**
* @param $sArchiveFile
*
* @throws \Exception
*/
public static function ExtractDownloadedFile($sArchiveFile)
{
try
{
// Extract archive file
self::ExtractUpdateFile($sArchiveFile);
IssueLog::Info('itop-core-update: Archive extracted, check files integrity');
IssueLog::Info('itop-core-update: Archive extracted, check files integrity');
// Check files integrity
FilesIntegrity::CheckInstallationIntegrity(self::UPDATE_DIR.'web/');
// Check files integrity
FilesIntegrity::CheckInstallationIntegrity(self::UPDATE_DIR.'web/');
IssueLog::Info('itop-core-update: Files integrity OK');
}
catch (Exception $e)
{
self::RRmdir(self::UPDATE_DIR);
throw $e;
}
finally
{
self::RRmdir(self::DOWNLOAD_DIR);
}
}
IssueLog::Info('itop-core-update: Files integrity OK');
} catch (Exception $e)
{
self::RRmdir(self::UPDATE_DIR);
throw $e;
} finally
{
self::RRmdir(self::DOWNLOAD_DIR);
}
}
/**
* @return string
* @throws \Exception
*/
public static function GetVersionToInstall()
{
try
{
$sConfigFile = self::UPDATE_DIR.'web/core/config.class.inc.php';
if (!is_file($sConfigFile))
{
throw new Exception(Dict::S(Dict::S('iTopUpdate:Error:BadFileContent')));
}
/**
* @return string
* @throws \Exception
*/
public static function GetVersionToInstall()
{
try
{
$sConfigFile = self::UPDATE_DIR.'web/core/config.class.inc.php';
if (!is_file($sConfigFile))
{
throw new Exception(Dict::S(Dict::S('iTopUpdate:Error:BadFileContent')));
}
$sContents = file_get_contents($sConfigFile);
preg_match_all("@define\('(?<name>ITOP_[^']*)', '(?<value>[^']*)'\);@", $sContents, $aMatches);
if (empty($aMatches))
{
throw new Exception(Dict::S(Dict::S('iTopUpdate:Error:BadFileContent')));
}
$aValues = array();
foreach ($aMatches['name'] as $index => $sName)
{
$aValues[$sName] = $aMatches['value'][$index];
}
$sContents = file_get_contents($sConfigFile);
preg_match_all("@define\('(?<name>ITOP_[^']*)', '(?<value>[^']*)'\);@", $sContents, $aMatches);
if (empty($aMatches))
{
throw new Exception(Dict::S(Dict::S('iTopUpdate:Error:BadFileContent')));
}
$aValues = array();
foreach ($aMatches['name'] as $index => $sName)
{
$aValues[$sName] = $aMatches['value'][$index];
}
if ($aValues['ITOP_APPLICATION'] != ITOP_APPLICATION)
{
throw new Exception(Dict::S('iTopUpdate:Error:BadItopProduct'));
}
if ($aValues['ITOP_APPLICATION'] != ITOP_APPLICATION)
{
throw new Exception(Dict::S('iTopUpdate:Error:BadItopProduct'));
}
// Extract updater file from the new version if available
if (is_file(APPROOT.'setup/appupgradecheck.php'))
{
// Remove previous specific updater
@unlink(APPROOT.'setup/appupgradecheck.php');
}
if (is_file(self::UPDATE_DIR.'web/setup/appupgradecheck.php'))
{
IssueLog::Info('itop-core-update: Use updater provided in the archive');
self::CopyFile(self::UPDATE_DIR.'web/setup/appupgradecheck.php', APPROOT.'setup/appupgradecheck.php');
@include_once(APPROOT.'setup/appupgradecheck.php');
}
if (function_exists('AppUpgradeCheckInstall'))
{
AppUpgradeCheckInstall();
}
// Extract updater file from the new version if available
if (is_file(APPROOT.'setup/appupgradecheck.php'))
{
// Remove previous specific updater
@unlink(APPROOT.'setup/appupgradecheck.php');
}
if (is_file(self::UPDATE_DIR.'web/setup/appupgradecheck.php'))
{
IssueLog::Info('itop-core-update: Use updater provided in the archive');
self::CopyFile(self::UPDATE_DIR.'web/setup/appupgradecheck.php', APPROOT.'setup/appupgradecheck.php');
@include_once(APPROOT.'setup/appupgradecheck.php');
}
if (function_exists('AppUpgradeCheckInstall'))
{
AppUpgradeCheckInstall();
}
return Dict::Format('UI:iTopVersion:Long', $aValues['ITOP_APPLICATION'], $aValues['ITOP_VERSION'], $aValues['ITOP_REVISION'], $aValues['ITOP_BUILD_DATE']);
}
catch (Exception $e)
{
self::RRmdir(self::UPDATE_DIR);
self::RRmdir(self::DOWNLOAD_DIR);
throw $e;
}
}
return Dict::Format('UI:iTopVersion:Long', $aValues['ITOP_APPLICATION'], $aValues['ITOP_VERSION'], $aValues['ITOP_REVISION'], $aValues['ITOP_BUILD_DATE']);
} catch (Exception $e)
{
self::RRmdir(self::UPDATE_DIR);
self::RRmdir(self::DOWNLOAD_DIR);
throw $e;
}
}
}

View File

@@ -12,9 +12,36 @@ require_once(APPROOT."setup/runtimeenv.class.inc.php");
use Config;
use Exception;
use RunTimeEnvironment;
use SetupUtils;
class RunTimeEnvironmentCoreUpdater extends RunTimeEnvironment
{
/**
* Constructor
*
* @param string $sEnvironment
* @param bool $bAutoCommit
*
* @throws \Exception
*/
public function __construct($sEnvironment = 'production', $bAutoCommit = true)
{
parent::__construct($sEnvironment, $bAutoCommit);
if ($sEnvironment != $this->sTargetEnv)
{
if (is_dir(APPROOT.'/env-'.$this->sTargetEnv))
{
SetupUtils::rrmdir(APPROOT.'/env-'.$this->sTargetEnv);
}
if (is_dir(APPROOT.'/data/'.$this->sTargetEnv.'-modules'))
{
SetupUtils::rrmdir(APPROOT.'/data/'.$this->sTargetEnv.'-modules');
}
SetupUtils::copydir(APPROOT.'/data/'.$sEnvironment.'-modules', APPROOT.'/data/'.$this->sTargetEnv.'-modules');
}
}
public function CheckDirectories($sTargetEnv)
{
$sTargetDir = APPROOT.'env-'.$sTargetEnv;

View File

@@ -57,7 +57,7 @@ function GetAjaxRequest(sOperation)
return oAjaxRequest;
}
{% set aSteps = ['EnterMaintenance', 'Backup', 'FilesArchive', 'CopyFiles', 'Compile', 'ExitMaintenance', 'UpdateDone'] %}
{% set aSteps = ['EnterMaintenance', 'Backup', 'FilesArchive', 'CopyFiles', 'CheckCompile', 'Compile', 'UpdateDatabase', 'ExitMaintenance', 'UpdateDone'] %}
aStepsName = [];
@@ -75,7 +75,7 @@ var sFilesArchiveStep;
sFilesArchiveStep = "FilesArchive";
{% endif %}
var aStepsAjaxOperation = ["EnterMaintenance", sBackupStep, sFilesArchiveStep, "CopyFiles", "Compile", "ExitMaintenance", null];
var aStepsAjaxOperation = ["EnterMaintenance", sBackupStep, sFilesArchiveStep, "CopyFiles", "CheckCompile", "Compile", "UpdateDatabase", "ExitMaintenance", null];
var iNextStep = 0;
function ExecNextStep() {

View File

@@ -1,7 +0,0 @@
# itop-twig-base
Provide Twig service to other modules.
BEWARE: This feature is subject to change! The API if not guaranteed future versions may break the compatibility.
namespace Combodo\iTop\TwigBase;

View File

@@ -1 +0,0 @@
<?php

View File

@@ -1,47 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2019 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
//
// iTop module definition file
//
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-twig-base/2.7.0',
array(
// Identification
//
'label' => 'iTop Twig Base',
'category' => 'business',
// Setup
//
'dependencies' => array(),
'mandatory' => false,
'visible' => false,
// Components
//
'datamodel' => array(
'model.itop-twig-base.php',
'src/Controller/Controller.php',
'src/Twig/Extension.php',
'src/Twig/TwigHelper.php',
),
'webservice' => array(),
'data.struct' => array(),
'data.sample' => array(),
// Documentation
//
'doc.manual_setup' => '', // hyperlink to manual setup documentation, if any
'doc.more_information' => '', // hyperlink to more information, if any
// Default settings
//
'settings' => array(),
)
);

View File

@@ -1,470 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2019 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\TwigBase\Controller;
use ajax_page;
use ApplicationMenu;
use Combodo\iTop\TwigBase\Twig\TwigHelper;
use Dict;
use Exception;
use IssueLog;
use iTopWebPage;
use LoginWebPage;
use MetaModel;
use ReflectionClass;
use SetupPage;
use SetupUtils;
use Twig_Error;
use utils;
use ZipArchive;
abstract class Controller
{
/** @var \Twig\Environment */
private $m_oTwig;
/** @var string */
private $m_sOperation;
/** @var string */
private $m_sModule;
/** @var iTopWebPage|\ajax_page */
private $m_oPage;
/** @var bool */
private $m_bCheckDemoMode = false;
/** @var bool */
private $m_bMustBeAdmin = false;
/** @var string */
private $m_sMenuId = null;
/** @var string */
private $m_sDefaultOperation = 'Default';
private $m_aLinkedScripts;
private $m_aLinkedStylesheets;
private $m_aAjaxTabs;
public function __construct()
{
$sModulePath = dirname(dirname($this->getDir()));
$this->m_sModule = basename($sModulePath);
$oTwig = TwigHelper::GetTwigEnvironment($sModulePath.'/view');
$this->m_oTwig = $oTwig;
$this->m_aLinkedScripts = array();
$this->m_aLinkedStylesheets = array();
$this->m_aAjaxTabs = array();
}
private function getDir()
{
return dirname((new ReflectionClass(static::class))->getFileName());
}
/**
* Entry point to handle requests
*
* @api
*/
public function HandleOperation()
{
try
{
$this->CheckAccess();
$this->m_sOperation = utils::ReadParam('operation', $this->m_sDefaultOperation);
$sMethodName = 'Operation'.$this->m_sOperation;
if (method_exists($this, $sMethodName))
{
$this->$sMethodName();
}
else
{
$this->DisplayPageNotFound();
}
}
catch (Exception $e)
{
require_once(APPROOT."/setup/setuppage.class.inc.php");
http_response_code(500);
$oP = new SetupPage(Dict::S('UI:PageTitle:FatalError'));
$oP->add("<h1>".Dict::S('UI:FatalErrorMessage')."</h1>\n");
$oP->add(get_class($e).' : '.htmlentities($e->GetMessage(), ENT_QUOTES, 'utf-8'));
$oP->output();
IssueLog::Error($e->getMessage());
}
}
/**
* Overridable "page not found" which is more an "operation not found"
*/
public function DisplayPageNotFound()
{
http_response_code(404);
die("Page not found");
}
/**
* @throws \Exception
*/
private function CheckAccess()
{
if ($this->m_bCheckDemoMode && MetaModel::GetConfig()->Get('demo_mode'))
{
throw new Exception("Sorry, iTop is in <b>demonstration mode</b>: this feature is disabled.");
}
LoginWebPage::DoLogin($this->m_bMustBeAdmin);
if (!empty($this->m_sMenuId))
{
ApplicationMenu::CheckMenuIdEnabled($this->m_sMenuId);
}
}
/**
* @return array
* @throws \Exception
*/
private function GetDefaultParameters()
{
$aParams = array();
$aParams['sIndexURL'] = utils::GetAbsoluteUrlModulePage($this->m_sModule, 'index.php');
return $aParams;
}
/**
* Disable this feature if in demo mode
*
* @api
*/
public function DisableInDemoMode()
{
$this->m_bCheckDemoMode = true;
}
/**
* Allow only admin users for this feature
*
* @api
*/
public function AllowOnlyAdmin()
{
$this->m_bMustBeAdmin = true;
}
/**
* Set the Id of the menu to check for user access rights
*
* @api
*
* @param string $sMenuId
*/
public function SetMenuId($sMenuId)
{
$this->m_sMenuId = $sMenuId;
}
/**
* Set the default operation when no 'operation' parameter is given on URL
*
* @api
*
* @param string $sDefaultOperation
*/
public function SetDefaultOperation($sDefaultOperation)
{
$this->m_sDefaultOperation = $sDefaultOperation;
}
/**
* Display an AJAX page (ajax_page)
*
* @api
*
* @param array $aParams Params used by the twig template
* @param null $sTemplateName Name of the twig template, ie MyTemplate for MyTemplate.html.twig
*
* @throws \Exception
*/
public function DisplayAjaxPage($aParams = array(), $sTemplateName = null)
{
$this->DisplayPage($aParams, $sTemplateName, 'ajax');
}
/**
* Display the twig page based on the name or the operation
*
* @api
*
* @param array $aParams Params used by the twig template
* @param string $sTemplateName Name of the twig template, ie MyTemplate for MyTemplate.html.twig
* @param string $sPageType ('html' or 'ajax')
*
* @throws \Exception
*/
public function DisplayPage($aParams = array(), $sTemplateName = null, $sPageType = 'html')
{
if (empty($sTemplateName))
{
$sTemplateName = $this->m_sOperation;
}
$aParams = array_merge($this->GetDefaultParameters(), $aParams);
$this->CreatePage($sPageType);
$this->AddToPage($this->RenderTemplate($aParams, $sTemplateName, 'html'));
$this->AddScriptToPage($this->RenderTemplate($aParams, $sTemplateName, 'js'));
$this->AddReadyScriptToPage($this->RenderTemplate($aParams, $sTemplateName, 'ready.js'));
if (!empty($this->m_aAjaxTabs))
{
$this->m_oPage->AddTabContainer('');
$this->m_oPage->SetCurrentTabContainer('');
}
foreach ($this->m_aAjaxTabs as $aTab)
{
$this->AddAjaxTabToPage($aTab['label'], $aTab['url'], $aTab['cache']);
}
foreach ($this->m_aLinkedScripts as $sLinkedScript)
{
$this->AddLinkedScriptToPage($sLinkedScript);
}
foreach ($this->m_aLinkedStylesheets as $sLinkedStylesheet)
{
$this->AddLinkedStylesheetToPage($sLinkedStylesheet);
}
$this->OutputPage();
}
/**
* Return a JSON response
*
* @api
*
* @param array $aParams Content of the response, will be converted to JSON
* @param int $iResponseCode HTTP response code
* @param array $aHeaders additional HTTP headers
*/
public function DisplayJSONPage($aParams = array(), $iResponseCode = 200, $aHeaders = array())
{
http_response_code($iResponseCode);
header('Content-Type: application/json');
foreach ($aHeaders as $sHeader)
{
header($sHeader);
}
echo json_encode($aParams);
}
/**
* Generate a page, zip it and propose the zipped file for download
*
* @api
*
* @param array $aParams Params used by the twig template
* @param null $sTemplateName Name of the twig template, ie MyTemplate for MyTemplate.html.twig
*/
public function DownloadZippedPage($aParams = array(), $sTemplateName = null)
{
if (empty($sTemplateName))
{
$sTemplateName = $this->m_sOperation;
}
$sReportFolder = str_replace("\\", '/', APPROOT.'log/');
$sReportFile = 'itop-system-information-report-'.date('Y-m-d-H-i-s');
$sHTMLReport = $sReportFolder.$sReportFile.'.html';
$sZIPReportFile = $sReportFile;
file_put_contents($sHTMLReport, $this->RenderTemplate($aParams, $sTemplateName, 'html'));
$this->ZipDownloadRemoveFile(array($sHTMLReport), $sZIPReportFile, true);
}
/**
* Create an archive and launch download, remove original file and archive when done
*
* @param string[] $aFiles
* @param string $sDownloadArchiveName file name to download, without the extension (.zip is automatically added)
* @param bool $bUnlinkFiles if true then will unlink each source file
*/
final protected function ZipDownloadRemoveFile($aFiles, $sDownloadArchiveName, $bUnlinkFiles = false)
{
$sArchiveFileFullPath = tempnam(SetupUtils::GetTmpDir(), 'itop_download-').'.zip';
$oArchive = new ZipArchive();
$oArchive->open($sArchiveFileFullPath, ZipArchive::CREATE);
foreach ($aFiles as $sFile)
{
$oArchive->addFile($sFile, basename($sFile));
}
$oArchive->close();
if ($bUnlinkFiles)
{
foreach ($aFiles as $sFile)
{
unlink($sFile);
}
}
$this->SendFileContent($sArchiveFileFullPath, $sDownloadArchiveName.'.zip', true, true);
}
final protected function SendFileContent($sFilePath, $sDownloadArchiveName = null, $bFileTransfer = true, $bRemoveFile = false, $aHeaders = array())
{
$sFileMimeType = utils::GetFileMimeType($sFilePath);
header('Content-Type: '.$sFileMimeType);
if ($bFileTransfer)
{
header('Content-Description: File Transfer');
header('Content-Disposition: inline; filename="'.$sDownloadArchiveName);
}
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
foreach ($aHeaders as $sKey => $sValue)
{
header($sKey.': '.$sValue);
}
header('Content-Length: '.filesize($sFilePath));
readfile($sFilePath);
if ($bRemoveFile)
{
unlink($sFilePath);
}
exit(0);
}
/**
* Add a linked script to the current Page
*
* @api
*
* @param string $sScript Script path to link
*/
public function AddLinkedScript($sScript)
{
$this->m_aLinkedScripts[] = $sScript;
}
/**
* Add an linked stylesheet to the current Page
*
* @api
*
* @param string $sStylesheet Stylesheet path to link
*/
public function AddLinkedStylesheet($sStylesheet)
{
$this->m_aLinkedStylesheets[] = $sStylesheet;
}
/**
* Add an AJAX tab to the current page
*
* @api
*
* @param string $sLabel Label of the tab
* @param string $sURL URL to call when the tab is activated
* @param bool $bCache If true, cache the result for the current web page
*/
public function AddAjaxTab($sLabel, $sURL, $bCache = true)
{
$this->m_aAjaxTabs[] = array('label' => $sLabel, 'url' => $sURL, 'cache' => $bCache);
}
private function RenderTemplate($aParams, $sName, $sTemplateFileExtension)
{
try
{
return $this->m_oTwig->render($sName.'.'.$sTemplateFileExtension.'.twig', $aParams);
}
catch (Twig_Error $e)
{
// Ignore errors
if (!utils::StartsWith($e->getMessage(), 'Unable to find template'))
{
IssueLog::Error($e->getMessage());
}
}
return '';
}
/**
* @param $sPageType
*
* @throws \Exception
*/
private function CreatePage($sPageType)
{
switch ($sPageType)
{
case 'html':
$this->m_oPage = new iTopWebPage($this->GetOperationTitle());
break;
case 'ajax':
$this->m_oPage = new ajax_page($this->GetOperationTitle());
break;
}
}
/**
* Get the title of the operation
*
* @return string
*/
public function GetOperationTitle()
{
return Dict::S($this->m_sModule.':UI:'.$this->m_sOperation);
}
/**
* @param $sContent
*
* @throws \Exception
*/
private function AddToPage($sContent)
{
$this->m_oPage->add($sContent);
}
private function AddReadyScriptToPage($sScript)
{
$this->m_oPage->add_ready_script($sScript);
}
private function AddScriptToPage($sScript)
{
$this->m_oPage->add_script($sScript);
}
private function AddLinkedScriptToPage($sLinkedScript)
{
$this->m_oPage->add_linked_script($sLinkedScript);
}
private function AddLinkedStylesheetToPage($sLinkedStylesheet)
{
$this->m_oPage->add_linked_stylesheet($sLinkedStylesheet);
}
private function AddAjaxTabToPage($sLabel, $sURL, $bCache)
{
$this->m_oPage->AddAjaxTab($sLabel, $sURL, $bCache);
}
/**
* @throws \Exception
*/
private function OutputPage()
{
$this->m_oPage->output();
}
}

View File

@@ -1,132 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2019 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\TwigBase\Twig;
use AttributeDateTime;
use Dict;
use Exception;
use MetaModel;
use Twig_Environment;
use Twig_SimpleFilter;
use Twig_SimpleFunction;
use utils;
class Extension
{
/**
* Registers Twig extensions such as filters or functions.
* It allows us to access some stuff directly in twig.
*
* @param \Twig_Environment $oTwigEnv
*/
public static function RegisterTwigExtensions(Twig_Environment &$oTwigEnv)
{
// Filter to translate a string via the Dict::S function
// Usage in twig: {{ 'String:ToTranslate'|dict_s }}
$oTwigEnv->addFilter(new Twig_SimpleFilter('dict_s',
function ($sStringCode, $sDefault = null, $bUserLanguageOnly = false) {
return Dict::S($sStringCode, $sDefault, $bUserLanguageOnly);
})
);
// Filter to format a string via the Dict::Format function
// Usage in twig: {{ 'String:ToTranslate'|dict_format() }}
$oTwigEnv->addFilter(new Twig_SimpleFilter('dict_format',
function ($sStringCode, $sParam01 = null, $sParam02 = null, $sParam03 = null, $sParam04 = null) {
return Dict::Format($sStringCode, $sParam01, $sParam02, $sParam03, $sParam04);
})
);
// Filter to format output
// example a DateTime is converted to user format
// Usage in twig: {{ 'String:ToFormat'|output_format }}
$oTwigEnv->addFilter(new Twig_SimpleFilter('date_format',
function ($sDate) {
try
{
if (preg_match('@^\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d$@', trim($sDate)))
{
return AttributeDateTime::GetFormat()->Format($sDate);
}
}
catch (Exception $e)
{
}
return $sDate;
})
);
// Filter to format output
// example a DateTime is converted to user format
// Usage in twig: {{ 'String:ToFormat'|output_format }}
$oTwigEnv->addFilter(new Twig_SimpleFilter('size_format',
function ($sSize) {
return utils::BytesToFriendlyFormat($sSize);
})
);
// Filter to enable base64 encode/decode
// Usage in twig: {{ 'String to encode'|base64_encode }}
$oTwigEnv->addFilter(new Twig_SimpleFilter('base64_encode', 'base64_encode'));
$oTwigEnv->addFilter(new Twig_SimpleFilter('base64_decode', 'base64_decode'));
// Filter to enable json decode (encode already exists)
// Usage in twig: {{ aSomeArray|json_decode }}
$oTwigEnv->addFilter(new Twig_SimpleFilter('json_decode', function ($sJsonString, $bAssoc = false) {
return json_decode($sJsonString, $bAssoc);
})
);
// Filter to add itopversion to an url
$oTwigEnv->addFilter(new Twig_SimpleFilter('add_itop_version', function ($sUrl) {
if (strpos($sUrl, '?') === false)
{
$sUrl = $sUrl."?itopversion=".ITOP_VERSION;
}
else
{
$sUrl = $sUrl."&itopversion=".ITOP_VERSION;
}
return $sUrl;
}));
// Filter to add a module's version to an url
$oTwigEnv->addFilter(new Twig_SimpleFilter('add_module_version', function ($sUrl, $sModuleName) {
$sModuleVersion = utils::GetCompiledModuleVersion($sModuleName);
if (strpos($sUrl, '?') === false)
{
$sUrl = $sUrl."?moduleversion=".$sModuleVersion;
}
else
{
$sUrl = $sUrl."&moduleversion=".$sModuleVersion;
}
return $sUrl;
}));
// Function to check our current environment
// Usage in twig: {% if is_development_environment() %}
$oTwigEnv->addFunction(new Twig_SimpleFunction('is_development_environment', function () {
return utils::IsDevelopmentEnvironment();
}));
// Function to get configuration parameter
// Usage in twig: {{ get_config_parameter('foo') }}
$oTwigEnv->addFunction(new Twig_SimpleFunction('get_config_parameter', function ($sParamName) {
$oConfig = MetaModel::GetConfig();
return $oConfig->Get($sParamName);
}));
}
}

View File

@@ -1,25 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2019 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\TwigBase\Twig;
use Twig_Environment;
use Twig_Loader_Filesystem;
@include_once(APPROOT.'/lib/silex/vendor/autoload.php');
class TwigHelper
{
public static function GetTwigEnvironment($sViewPath)
{
$oLoader = new Twig_Loader_Filesystem($sViewPath);
$oTwig = new Twig_Environment($oLoader);
Extension::RegisterTwigExtensions($oTwig);
return $oTwig;
}
}