mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-12 23:14:18 +01:00
New symlink flag set by the compiler, new compile with symlinks checkbox in the setup (#190)
The flag consists of the new `data/.compilation-symlinks` file, which is included in the code base (but not available in the Combodo packages). It will be used and generated if and only if : * we are on a dev env (`\utils::IsDevelopmentEnvironment`) * the `symlink` PHP function exists The flag is generated low level in the compiler (\MFCompiler::DoCompile) In the setup, if the flag is present and all conditions are met then a new option will be displayed in the "Miscellaneous Parameters" wizard step. When unchecking/checking the flag will be updated accordingly by an ajax query. All other compiler consumers (Designer / Hub connectors, Core Update, scripts using `RunTimeEnvironment` class) will : * if they provide a value for the symlink option, it will be used * otherwise the flag will be used instead, if conditions are met
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -32,6 +32,8 @@ test/vendor/*
|
||||
!/data/.htaccess
|
||||
!/data/index.php
|
||||
!/data/web.config
|
||||
!/data/exclude.txt
|
||||
!/data/.compilation-symlinks
|
||||
|
||||
# iTop extensions
|
||||
/extensions/**
|
||||
|
||||
0
data/.compilation-symlinks
Normal file
0
data/.compilation-symlinks
Normal file
5
data/exclude.txt
Normal file
5
data/exclude.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
#
|
||||
# The following source files are not re-distributed with the "build" of the application
|
||||
# since they are used solely for debugging. The minified version is normally used instead.
|
||||
#
|
||||
.compilation-symlinks
|
||||
@@ -164,23 +164,27 @@ try
|
||||
/** @var WizardStep $oStep */
|
||||
$oStep = new $sClass($oDummyController, $sState);
|
||||
$sConfigFile = utils::GetConfigFilePath();
|
||||
if (file_exists($sConfigFile) && !is_writable($sConfigFile) && $oStep->RequiresWritableConfig())
|
||||
{
|
||||
if (file_exists($sConfigFile) && !is_writable($sConfigFile) && $oStep->RequiresWritableConfig()) {
|
||||
$sRelativePath = utils::GetConfigFilePathRelative();
|
||||
$oPage->error("<b>Error:</b> the configuration file '".$sRelativePath."' already exists and cannot be overwritten.");
|
||||
$oPage->p("The wizard cannot modify the configuration file for you. If you want to upgrade ".ITOP_APPLICATION.", make sure that the file '<b>".$sRelativePath."</b>' can be modified by the web server.");
|
||||
$oPage->output();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$oStep->AsyncAction($oPage, $sActionCode, $aParams);
|
||||
}
|
||||
}
|
||||
$oPage->output();
|
||||
break;
|
||||
$oPage->output();
|
||||
break;
|
||||
|
||||
case 'toggle_use_symbolic_links':
|
||||
$sUseSymbolicLinks = Utils::ReadParam('bUseSymbolicLinks', false);
|
||||
$bUseSymbolicLinks = ($sUseSymbolicLinks === 'true');
|
||||
MFCompiler::SetUseSymbolicLinksFlag($bUseSymbolicLinks);
|
||||
echo "toggle useSymbolicLInks file : $bUseSymbolicLinks";
|
||||
break;
|
||||
|
||||
default:
|
||||
throw(new Exception("Error unsupported operation '$sOperation'"));
|
||||
throw(new Exception("Error unsupported operation '$sOperation'"));
|
||||
}
|
||||
}
|
||||
catch(Exception $e)
|
||||
|
||||
@@ -274,17 +274,14 @@ class ApplicationInstaller
|
||||
$sExtensionDir = $this->oParams->Get('extensions_dir', 'extensions');
|
||||
$sTargetEnvironment = $this->GetTargetEnv();
|
||||
$sTargetDir = $this->GetTargetDir();
|
||||
$bUseSymbolicLinks = false;
|
||||
$aMiscOptions = $this->oParams->Get('options', array());
|
||||
if (isset($aMiscOptions['symlinks']) && $aMiscOptions['symlinks'])
|
||||
{
|
||||
if (function_exists('symlink'))
|
||||
{
|
||||
|
||||
$bUseSymbolicLinks = null;
|
||||
if ((isset($aMiscOptions['symlinks']) && $aMiscOptions['symlinks'])) {
|
||||
if (function_exists('symlink')) {
|
||||
$bUseSymbolicLinks = true;
|
||||
SetupLog::Info("Using symbolic links instead of copying data model files (for developers only!)");
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
SetupLog::Info("Symbolic links (function symlinks) does not seem to be supported on this platform (OS/PHP version).");
|
||||
}
|
||||
}
|
||||
@@ -504,8 +501,7 @@ class ApplicationInstaller
|
||||
{
|
||||
$oBackup = new SetupDBBackup($oConfig);
|
||||
$sTargetFile = $oBackup->MakeName($sBackupFileFormat);
|
||||
if (!empty($sMySQLBinDir))
|
||||
{
|
||||
if (!empty($sMySQLBinDir)) {
|
||||
$oBackup->SetMySQLBinDir($sMySQLBinDir);
|
||||
}
|
||||
|
||||
@@ -513,8 +509,8 @@ class ApplicationInstaller
|
||||
$oBackup->CreateCompressedBackup($sTargetFile, $sSourceConfigFile);
|
||||
}
|
||||
|
||||
|
||||
protected static function DoCompile($aSelectedModules, $sSourceDir, $sExtensionDir, $sTargetDir, $sEnvironment, $bUseSymbolicLinks = false)
|
||||
|
||||
protected static function DoCompile($aSelectedModules, $sSourceDir, $sExtensionDir, $sTargetDir, $sEnvironment, $bUseSymbolicLinks = null)
|
||||
{
|
||||
SetupLog::Info("Compiling data model.");
|
||||
|
||||
@@ -522,8 +518,7 @@ class ApplicationInstaller
|
||||
require_once(APPROOT.'setup/modelfactory.class.inc.php');
|
||||
require_once(APPROOT.'setup/compiler.class.inc.php');
|
||||
|
||||
if (empty($sSourceDir) || empty($sTargetDir))
|
||||
{
|
||||
if (empty($sSourceDir) || empty($sTargetDir)) {
|
||||
throw new Exception("missing parameter source_dir and/or target_dir");
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,15 @@ class DOMFormatException extends Exception
|
||||
*/
|
||||
class MFCompiler
|
||||
{
|
||||
const DATA_PRECOMPILED_FOLDER = 'data' . DIRECTORY_SEPARATOR . 'precompiled_styles' . DIRECTORY_SEPARATOR;
|
||||
const DATA_PRECOMPILED_FOLDER = 'data'.DIRECTORY_SEPARATOR.'precompiled_styles'.DIRECTORY_SEPARATOR;
|
||||
|
||||
/**
|
||||
* Path to the "use symlinks" file
|
||||
* If this file is present, then we will compile to symlink !
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const USE_SYMBOLIC_LINKS_FILE_PATH = APPROOT.'data/.compilation-symlinks';
|
||||
|
||||
/** @var \ThemeHandlerService */
|
||||
protected static $oThemeHandlerService;
|
||||
@@ -93,54 +101,112 @@ class MFCompiler
|
||||
|
||||
protected function DumpLog($oPage)
|
||||
{
|
||||
foreach ($this->aLog as $sText)
|
||||
{
|
||||
foreach ($this->aLog as $sText) {
|
||||
$oPage->p($sText);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function GetLog()
|
||||
{
|
||||
return $this->aLog;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param bool $bForce if true then will just check file existence
|
||||
*
|
||||
* @return bool possible return values :
|
||||
* * always false if not in dev env
|
||||
* * `symlink` function non existent : false
|
||||
* * if flag is present true, false otherwise
|
||||
*
|
||||
* @uses utils::IsDevelopmentEnvironment()
|
||||
* @uses \function_exists()
|
||||
* @uses \file_exists()
|
||||
* @uses USE_SYMBOLIC_LINKS_FILE_PATH
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static function IsUseSymbolicLinksFlagPresent(bool $bForce = false): bool
|
||||
{
|
||||
if (!$bForce) {
|
||||
if (!utils::IsDevelopmentEnvironment()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!function_exists('symlink')) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return (file_exists(static::USE_SYMBOLIC_LINKS_FILE_PATH));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $bUseSymbolicLinks
|
||||
*
|
||||
* @since 3.0.0 method creation
|
||||
* @uses USE_SYMBOLIC_LINKS_FILE_PATH
|
||||
*/
|
||||
public static function SetUseSymbolicLinksFlag(bool $bUseSymbolicLinks): void
|
||||
{
|
||||
$bHasUseSymlinksFile = static::IsUseSymbolicLinksFlagPresent(true);
|
||||
|
||||
if ($bUseSymbolicLinks) {
|
||||
if ($bHasUseSymlinksFile) {
|
||||
return;
|
||||
}
|
||||
|
||||
touch(static::USE_SYMBOLIC_LINKS_FILE_PATH);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$bHasUseSymlinksFile) {
|
||||
return;
|
||||
}
|
||||
unlink(static::USE_SYMBOLIC_LINKS_FILE_PATH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the data model into PHP files and data structures
|
||||
*
|
||||
* @param string $sTargetDir The target directory where to put the resulting files
|
||||
* @param Page $oP For some output...
|
||||
* @param bool $bUseSymbolicLinks
|
||||
* @param bool $bSkipTempDir
|
||||
* @throws Exception
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
public function Compile($sTargetDir, $oP = null, $bUseSymbolicLinks = false, $bSkipTempDir = false)
|
||||
public function Compile($sTargetDir, $oP = null, $bUseSymbolicLinks = null, $bSkipTempDir = false)
|
||||
{
|
||||
if (is_null($bUseSymbolicLinks)) {
|
||||
$bUseSymbolicLinks = false;
|
||||
if (self::IsUseSymbolicLinksFlagPresent()) {
|
||||
// We are only overriding the useSymLinks option if the consumer didn't specify anything
|
||||
// The toolkit always send this parameter for example, but not the Designer Connector
|
||||
$bUseSymbolicLinks = true;
|
||||
}
|
||||
}
|
||||
|
||||
$sFinalTargetDir = $sTargetDir;
|
||||
$bIsAlreadyInMaintenanceMode = SetupUtils::IsInMaintenanceMode();
|
||||
$sConfigFilePath = utils::GetConfigFilePath($this->sEnvironment);
|
||||
if (is_file($sConfigFilePath))
|
||||
{
|
||||
if (is_file($sConfigFilePath)) {
|
||||
$oConfig = new Config($sConfigFilePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$oConfig = null;
|
||||
}
|
||||
if (($this->sEnvironment == 'production') && !$bIsAlreadyInMaintenanceMode)
|
||||
{
|
||||
if (($this->sEnvironment == 'production') && !$bIsAlreadyInMaintenanceMode) {
|
||||
|
||||
SetupUtils::EnterMaintenanceMode($oConfig);
|
||||
}
|
||||
if ($bUseSymbolicLinks || $bSkipTempDir)
|
||||
{
|
||||
if ($bUseSymbolicLinks || $bSkipTempDir) {
|
||||
// Skip the creation of a temporary dictionary, not compatible with symbolic links
|
||||
$sTempTargetDir = $sFinalTargetDir;
|
||||
SetupUtils::rrmdir($sFinalTargetDir);
|
||||
SetupUtils::builddir($sFinalTargetDir); // Here is the directory
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Create a temporary directory
|
||||
// Once the compilation is 100% successful, then move the results into the target directory
|
||||
$sTempTargetDir = tempnam(SetupUtils::GetTmpDir(), 'itop-');
|
||||
@@ -226,17 +292,14 @@ class MFCompiler
|
||||
// The bullet proof implementation would be to compile in a separate directory as it has been done with the dictionaries... that's another story
|
||||
$aModules = $this->oFactory->GetLoadedModules();
|
||||
$sUserRightsModule = '';
|
||||
foreach($aModules as $foo => $oModule)
|
||||
{
|
||||
if ($oModule->GetName() == 'itop-profiles-itil')
|
||||
{
|
||||
foreach ($aModules as $foo => $oModule) {
|
||||
if ($oModule->GetName() == 'itop-profiles-itil') {
|
||||
$sUserRightsModule = 'itop-profiles-itil';
|
||||
break;
|
||||
}
|
||||
}
|
||||
$oUserRightsNode = $this->oFactory->GetNodes('user_rights')->item(0);
|
||||
if ($oUserRightsNode && ($sUserRightsModule == ''))
|
||||
{
|
||||
if ($oUserRightsNode && ($sUserRightsModule == '')) {
|
||||
// Legacy algorithm (itop <= 2.0.3)
|
||||
$sUserRightsModule = $oUserRightsNode->getAttribute('_created_in');
|
||||
}
|
||||
@@ -245,13 +308,12 @@ class MFCompiler
|
||||
// List root classes
|
||||
//
|
||||
$this->aRootClasses = array();
|
||||
foreach ($this->oFactory->ListRootClasses() as $oClass)
|
||||
{
|
||||
foreach ($this->oFactory->ListRootClasses() as $oClass) {
|
||||
$this->Log("Root class (with child classes): ".$oClass->getAttribute('id'));
|
||||
$this->aRootClasses[$oClass->getAttribute('id')] = $oClass;
|
||||
}
|
||||
|
||||
$this->LoadSnippets();
|
||||
|
||||
$this->LoadSnippets();
|
||||
|
||||
// Compile, module by module
|
||||
//
|
||||
@@ -264,33 +326,25 @@ class MFCompiler
|
||||
$this->WriteStaticOnlyHtaccess($sTempTargetDir);
|
||||
$this->WriteStaticOnlyWebConfig($sTempTargetDir);
|
||||
|
||||
foreach($aModules as $foo => $oModule)
|
||||
{
|
||||
static::SetUseSymbolicLinksFlag($bUseSymbolicLinks);
|
||||
|
||||
foreach ($aModules as $foo => $oModule) {
|
||||
$sModuleName = $oModule->GetName();
|
||||
$sModuleVersion = $oModule->GetVersion();
|
||||
|
||||
$sModuleRootDir = $oModule->GetRootDir();
|
||||
if ($sModuleRootDir != '')
|
||||
{
|
||||
if ($sModuleRootDir != '') {
|
||||
$sModuleRootDir = realpath($sModuleRootDir);
|
||||
$sRelativeDir = basename($sModuleRootDir);
|
||||
if ($bUseSymbolicLinks)
|
||||
{
|
||||
if ($bUseSymbolicLinks) {
|
||||
$sRealRelativeDir = substr($sModuleRootDir, $iStart);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$sRealRelativeDir = $sRelFinalTargetDir.'/'.$sRelativeDir;
|
||||
}
|
||||
|
||||
// Push the other module files
|
||||
SetupUtils::copydir($sModuleRootDir, $sTempTargetDir.'/'.$sRelativeDir, $bUseSymbolicLinks);
|
||||
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$sRelativeDir = $sModuleName;
|
||||
$sRealRelativeDir = $sModuleName;
|
||||
}
|
||||
|
||||
@@ -490,11 +490,13 @@ class RunTimeEnvironment
|
||||
* The list of modules to be installed in the target 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 target environment: data/<target_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 = false)
|
||||
public function CompileFrom($sSourceEnv, $bUseSymLinks = null)
|
||||
{
|
||||
$oSourceConfig = new Config(utils::GetConfigFilePath($sSourceEnv));
|
||||
$sSourceDir = $oSourceConfig->Get('source_dir');
|
||||
@@ -504,7 +506,7 @@ class RunTimeEnvironment
|
||||
//
|
||||
$oFactory = new ModelFactory($sSourceDirFull);
|
||||
$aModulesToCompile = $this->GetMFModulesToCompile($sSourceEnv, $sSourceDir);
|
||||
foreach($aModulesToCompile as $oModule)
|
||||
foreach ($aModulesToCompile as $oModule)
|
||||
{
|
||||
if ($oModule instanceof MFDeltaModule)
|
||||
{
|
||||
@@ -514,15 +516,12 @@ class RunTimeEnvironment
|
||||
}
|
||||
$oFactory->LoadModule($oModule);
|
||||
}
|
||||
|
||||
|
||||
if ($oModule instanceof MFDeltaModule)
|
||||
{
|
||||
|
||||
if ($oModule instanceof MFDeltaModule) {
|
||||
// A delta was loaded, let's save a second copy of the datamodel
|
||||
$oFactory->SaveToFile(APPROOT.'data/datamodel-'.$this->sTargetEnv.'-with-delta.xml');
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// No delta was loaded, let's save the datamodel now
|
||||
$oFactory->SaveToFile(APPROOT.'data/datamodel-'.$this->sTargetEnv.'.xml');
|
||||
}
|
||||
|
||||
@@ -1161,6 +1161,30 @@ class WizStepUpgradeMiscParams extends WizardStep
|
||||
});
|
||||
EOF
|
||||
);
|
||||
|
||||
if (MFCompiler::IsUseSymbolicLinksFlagPresent()) {
|
||||
$oPage->add('<fieldset>');
|
||||
$oPage->add('<legend>Dev parameters</legend>');
|
||||
$oPage->p('<input id="use-symbolic-links" type="checkbox" checked><label for="use-symbolic-links"> Create symbolic links instead of creating a copy in env-production (useful for debugging extensions)');
|
||||
$oPage->add('</fieldset>');
|
||||
$oPage->add_ready_script(<<<'JS'
|
||||
$("#use-symbolic-links").on("click", function() {
|
||||
var $this = $(this),
|
||||
bUseSymbolicLinks = $this.prop("checked");
|
||||
if (!bUseSymbolicLinks){
|
||||
if (!window.confirm("This will disable symbolic links generation.\nYou'll need the toolkit to restore this option.\n\nAre you sure ?")) {
|
||||
$this.prop("checked", true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var sAuthent = $('#authent_token').val();
|
||||
var oAjaxParams = { operation: 'toggle_use_symbolic_links', bUseSymbolicLinks: bUseSymbolicLinks, authent: sAuthent};
|
||||
$.post(GetAbsoluteUrlAppRoot()+'setup/ajax.dataloader.php', oAjaxParams);
|
||||
});
|
||||
JS
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function AsyncAction(WebPage $oPage, $sCode, $aParameters)
|
||||
|
||||
Reference in New Issue
Block a user