Merge branch 'develop' into feature/fast-theme-compilation

This commit is contained in:
odain-cbd
2020-08-07 16:12:47 +02:00
committed by GitHub
358 changed files with 6357 additions and 4182 deletions

View File

@@ -318,14 +318,27 @@ class MFCompiler
}
else
{
/** @var \DOMElement $oClass */
/** @var \MFElement $oClass */
foreach($oClasses as $oClass)
{
$sClass = $oClass->getAttribute("id");
$aAllClasses[] = $sClass;
try
{
$sCompiledCode .= $this->CompileClass($oClass, $sTempTargetDir, $sFinalTargetDir, $sRelativeDir);
$aCompiledClasses = $this->CompileClass($oClass, $sTempTargetDir, $sFinalTargetDir, $sRelativeDir);
foreach ($aCompiledClasses['required_files'] as $sIncludeFile)
{
$sCompiledCode .= "require_once('$sIncludeFile');\n";
}
foreach ($aCompiledClasses['code'] as $sClass => $sCompiledClass)
{
$sClassFileName = DIRECTORY_SEPARATOR.$sRelativeDir.DIRECTORY_SEPARATOR.'src'.DIRECTORY_SEPARATOR.'Model'.DIRECTORY_SEPARATOR.$sClass.'.php';
$sClassFile = "{$sTempTargetDir}{$sClassFileName}";
$this->WritePHPFile($sClassFile, $sModuleName, $sModuleVersion, $sCompiledClass);
$sCompiledCode .= "require_once ('{$sFinalTargetDir}{$sClassFileName}');\n";
}
}
catch (DOMFormatException $e)
{
@@ -466,49 +479,7 @@ EOF;
// Write the code into the given module as model.<module>.php
//
$sResultFile = $sTempTargetDir.'/'.$sRelativeDir.'/model.'.$sModuleName.'.php';
if (is_file($sResultFile))
{
$this->Log("Updating $sResultFile for module $sModuleName in version $sModuleVersion ($iClassCount classes)");
} else
{
$sResultDir = dirname($sResultFile);
if (!is_dir($sResultDir))
{
$this->Log("Creating directory $sResultDir");
mkdir($sResultDir, 0777, true);
}
$this->Log("Creating $sResultFile for module $sModuleName in version $sModuleVersion ($iClassCount classes)");
}
// Compile the module into a single file
//
$sCurrDate = date(DATE_ISO8601);
$sAuthor = 'iTop compiler';
$sLicence = 'http://opensource.org/licenses/AGPL-3.0';
$sFileHeader =
<<<EOF
<?php
//
// File generated by ... on the $sCurrDate
// Please do not edit manually
//
/**
* Classes and menus for $sModuleName (version $sModuleVersion)
*
* @author $sAuthor
* @license $sLicence
*/
EOF;
$ret = file_put_contents($sResultFile, $sFileHeader.$sCompiledCode);
if ($ret === false)
{
$iLen = strlen($sFileHeader.$sCompiledCode);
$fFree = @disk_free_space(dirname($sResultFile));
$aErr = error_get_last();
throw new Exception("Failed to write '$sResultFile'. Last error: '{$aErr['message']}', content to write: $iLen bytes, available free space on disk: $fFree.");
}
$this->WritePHPFile($sResultFile, $sModuleName, $sModuleVersion, $sCompiledCode);
}
else
{
@@ -611,8 +582,8 @@ EOF;
SetupUtils::builddir($sTempTargetDir.'/core');
$sPHPFile = $sTempTargetDir.'/core/main.php';
file_put_contents($sPHPFile, $this->sMainPHPCode);
$sCurrDate = date(DATE_ISO8601);
// Autoload
$sPHPFile = $sTempTargetDir.'/autoload.php';
$sPHPFileContent =
@@ -1018,7 +989,7 @@ EOF
* @param string $sFinalTargetDir
* @param string $sModuleRelativeDir
*
* @return string
* @return array
* @throws \DOMFormatException
*/
protected function CompileClass($oClass, $sTempTargetDir, $sFinalTargetDir, $sModuleRelativeDir)
@@ -1997,7 +1968,7 @@ EOF
$sClassName = $oClass->getAttribute('id');
$bIsAbstractClass = ($oProperties->GetChildText('abstract') == 'true');
$oPhpParent = $oClass->GetUniqueElement('php_parent', false);
$aRequiredFiles = array();
$aRequiredFiles = [];
if ($oPhpParent)
{
$sParentClass = $oPhpParent->GetChildText('name', '');
@@ -2030,8 +2001,7 @@ $sZlists;
EOF;
// some other stuff (magical attributes like friendlyName) are done in MetaModel::InitClasses and though not present in the
// generated PHP
$sPHP = $this->GeneratePhpCodeForClass($sClassName, $sParentClass, $sClassParams, $sInitMethodCalls,
$bIsAbstractClass, $sMethods, $aRequiredFiles, $sCodeComment);
$aPHP[$sClassName] = $this->GeneratePhpCodeForClass($sClassName, $sParentClass, $sClassParams, $sInitMethodCalls, $bIsAbstractClass, $sMethods, $sCodeComment);
// N°931 generates TagFieldData classes for AttributeTag fields
if (!empty($aTagFieldsInfo))
@@ -2060,11 +2030,11 @@ EOF
{
$sTagClassName = static::GetTagDataClassName($sClassName, $sTagFieldName);
$sTagClassParams = var_export($aTagClassParams, true);
$sPHP .= $this->GeneratePhpCodeForClass($sTagClassName, $sTagClassParentClass, $sTagClassParams, $sTagInitMethodCalls);
$aPHP[$sTagClassName] = $this->GeneratePhpCodeForClass($sTagClassName, $sTagClassParentClass, $sTagClassParams, $sTagInitMethodCalls);
}
}
return $sPHP;
return ['code' => $aPHP, 'required_files' => $aRequiredFiles];
}
private static function GetTagDataClassName($sClass, $sAttCode)
@@ -3008,22 +2978,21 @@ EOF;
* @param bool $bIsAbstractClass
* @param string $sMethods
*
* @param array $aRequiredFiles
* @param string $sCodeComment
*
* @return string php code for the class
*/
private function GeneratePhpCodeForClass(
$sClassName, $sParentClassName, $sClassParams, $sInitMethodCalls = '', $bIsAbstractClass = false, $sMethods = '',
$aRequiredFiles = array(), $sCodeComment = ''
$sClassName,
$sParentClassName,
$sClassParams,
$sInitMethodCalls = '',
$bIsAbstractClass = false,
$sMethods = '',
$sCodeComment = ''
) {
$sPHP = "\n\n$sCodeComment\n";
foreach ($aRequiredFiles as $sIncludeFile)
{
$sPHP .= "\nrequire_once('$sIncludeFile');\n";
}
if ($bIsAbstractClass)
{
$sPHP .= 'abstract class '.$sClassName;
@@ -3126,7 +3095,7 @@ EOF;
*/
protected function WriteStaticOnlyWebConfig($sTempTargetDir)
{
$sContent = <<<EOF
$sContent = <<<XML
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.webServer>
@@ -3159,8 +3128,57 @@ EOF;
</system.webServer>
</configuration>
EOF;
XML;
$this->WriteFile("$sTempTargetDir/web.config", $sContent);
}
/**
* @param $sResultFile
* @param $sModuleName
* @param $sModuleVersion
* @param $sCompiledCode
*
* @throws \Exception
*/
protected function WritePHPFile($sResultFile, $sModuleName, $sModuleVersion, $sCompiledCode)
{
if (is_file($sResultFile))
{
$this->Log("Updating $sResultFile for module $sModuleName in version $sModuleVersion");
}
else
{
$sResultDir = dirname($sResultFile);
if (!is_dir($sResultDir))
{
$this->Log("Creating directory $sResultDir");
mkdir($sResultDir, 0777, true);
}
$this->Log("Creating $sResultFile for module $sModuleName in version $sModuleVersion");
}
// Compile the module into a single file
//
$sCurrDate = date(DATE_ISO8601);
$sAuthor = 'iTop compiler';
$sLicence = 'http://opensource.org/licenses/AGPL-3.0';
$sFileHeader =
<<<EOF
<?php
//
// File generated by ... on the $sCurrDate
// Please do not edit manually
//
/**
* Classes and menus for $sModuleName (version $sModuleVersion)
*
* @author $sAuthor
* @license $sLicence
*/
EOF;
$this->WriteFile($sResultFile, $sFileHeader.$sCompiledCode);
}
}

View File

@@ -277,7 +277,6 @@ class iTopExtensionsMap
$oExtension->sVersion = $oXml->Get('version');
$oExtension->bMandatory = ($oXml->Get('mandatory') == 'true');
$oExtension->sMoreInfoUrl = $oXml->Get('more_info_url');
$oExtension->sVersion = $oXml->Get('version');
$oExtension->sSource = $sSource;
$oExtension->sSourceDir = $sSearchDir;

View File

@@ -256,18 +256,21 @@ class MFModule
public function GetDictionaryFiles()
{
$aDictionaries = array();
if ($hDir = opendir($this->sRootDir))
foreach (array($this->sRootDir, $this->sRootDir.'/dictionaries') as $sRootDir)
{
while (($sFile = readdir($hDir)) !== false)
if ($hDir = @opendir($sRootDir))
{
$aMatches = array();
if (preg_match("/^[^\\.]+.dict.".$this->sName.'.php$/i', $sFile,
$aMatches)) // Dictionary files are named like <Lang>.dict.<ModuleName>.php
while (($sFile = readdir($hDir)) !== false)
{
$aDictionaries[] = $this->sRootDir.'/'.$sFile;
$aMatches = array();
if (preg_match("/^[^\\.]+.dict.".$this->sName.'.php$/i', $sFile,
$aMatches)) // Dictionary files are named like <Lang>.dict.<ModuleName>.php
{
$aDictionaries[] = $sRootDir.'/'.$sFile;
}
}
closedir($hDir);
}
closedir($hDir);
}
return $aDictionaries;
@@ -466,18 +469,21 @@ class MFDictModule extends MFModule
public function GetDictionaryFiles()
{
$aDictionaries = array();
if ($hDir = opendir($this->sRootDir))
foreach (array($this->sRootDir, $this->sRootDir.'/dictionaries') as $sRootDir)
{
while (($sFile = readdir($hDir)) !== false)
if ($hDir = @opendir($sRootDir))
{
$aMatches = array();
if (preg_match("/^.*dictionary\\.itop.*.php$/i", $sFile,
$aMatches)) // Dictionary files are named like <Lang>.dict.<ModuleName>.php
while (($sFile = readdir($hDir)) !== false)
{
$aDictionaries[] = $this->sRootDir.'/'.$sFile;
$aMatches = array();
if (preg_match("/^.*dictionary\\.itop.*.php$/i", $sFile,
$aMatches)) // Dictionary files are named like <Lang>.dict.<ModuleName>.php
{
$aDictionaries[] = $sRootDir.'/'.$sFile;
}
}
closedir($hDir);
}
closedir($hDir);
}
return $aDictionaries;
@@ -2898,4 +2904,4 @@ class MFParameters
return $merged;
}
}
}

View File

@@ -134,17 +134,20 @@ class ModuleDiscovery
{
$sModuleName = $aMatches[1];
$sDir = dirname($sFilePath);
if ($hDir = opendir($sDir))
foreach (array($sDir, $sDir.'/dictionaries') as $sRootDir)
{
while (($sFile = readdir($hDir)) !== false)
if ($hDir = @opendir($sRootDir))
{
$aMatches = array();
if (preg_match("/^[^\\.]+.dict.$sModuleName.php$/i", $sFile, $aMatches)) // Dictionary files named like <Lang>.dict.<ModuleName>.php are loaded automatically
while (($sFile = readdir($hDir)) !== false)
{
self::$m_aModules[$sId]['dictionary'][] = self::$m_sModulePath.'/'.$sFile;
$aMatches = array();
if (preg_match("/^[^\\.]+.dict.$sModuleName.php$/i", $sFile, $aMatches)) // Dictionary files named like <Lang>.dict.<ModuleName>.php are loaded automatically
{
self::$m_aModules[$sId]['dictionary'][] = $sRootDir.'/'.$sFile;
}
}
closedir($hDir);
}
closedir($hDir);
}
}
}

View File

@@ -39,6 +39,50 @@ class CheckResult
$this->sLabel = $sLabel;
$this->sDescription = $sDescription;
}
/**
* @return string
* @since 2.8.0 N°2214
*/
public function __toString()
{
$sPrintDesc = (empty($this->sDescription)) ? '' : " ({$this->sDescription})";
return "{$this->sLabel}$sPrintDesc";
}
/**
* @param \CheckResult[] $aResults
*
* @return \CheckResult[] only elements that are error (iSeverity===ERROR)
*
* @since 2.8.0 N°2214
*/
public static function KeepOnlyErrors($aResults)
{
return array_filter($aResults,
static function ($v)
{
if ($v->iSeverity === CheckResult::ERROR) {
return $v;
}
},
ARRAY_FILTER_USE_BOTH);
}
/**
* @param \CheckResult[] $aResults
*
* @return string[]
* @uses \CheckResult::__toString
*
* @since 2.8.0 N°2214
*/
public static function FromObjectsToStrings($aResults)
{
return array_map(function ($value) {
return $value->__toString();
}, $aResults);
}
}
/**
@@ -50,15 +94,15 @@ class CheckResult
class SetupUtils
{
// -- Minimum versions (requirements : forbids installation if not met)
const PHP_MIN_VERSION = '5.6.0'; // 5.6 will be supported until the end of 2018 (see http://php.net/supported-versions.php)
const PHP_MIN_VERSION = '7.1.3'; // 7 will be supported until the end of 2019 (see http://php.net/supported-versions.php)
const MYSQL_MIN_VERSION = '5.6.0'; // 5.6 to have fulltext on InnoDB for Tags fields (N°931)
const MYSQL_NOT_VALIDATED_VERSION = ''; // MySQL 8 is now OK (N°2010 in 2.7.0) but has no query cache so mind the perf on large volumes !
// -- versions that will be the minimum in next iTop major release (warning if not met)
const PHP_NEXT_MIN_VERSION = '7.1.3'; // we are aiming on switching to Symfony 4 in iTop 2.8
const PHP_NEXT_MIN_VERSION = ''; //
const MYSQL_NEXT_MIN_VERSION = ''; // no new MySQL requirement for next iTop version
// -- First recent version that is not yet validated by Combodo (warning)
const PHP_NOT_VALIDATED_VERSION = '7.5.0';
const PHP_NOT_VALIDATED_VERSION = '8.0.0';
const MIN_MEMORY_LIMIT = 33554432; // 32 * 1024 * 1024 - we can use expressions in const since PHP 5.6 but we are in the setup !
const SUHOSIN_GET_MAX_VALUE_LENGTH = 2048;
@@ -76,7 +120,7 @@ class SetupUtils
* @internal SetupPage $oP The page used only for its 'log' method
* @return CheckResult[]
*/
static function CheckPhpAndExtensions()
public static function CheckPhpAndExtensions()
{
$aResult = array();
@@ -370,6 +414,37 @@ class SetupUtils
return $aResult;
}
/**
* @param \CLIPage $oCliPage
* @param int $iExitCode
*
* @since 2.8.0 N°2214
*/
public static function CheckPhpAndExtensionsForCli($oCliPage, $iExitCode = -1)
{
$aPhpCheckResults = self::CheckPhpAndExtensions();
$aPhpCheckErrors = CheckResult::KeepOnlyErrors($aPhpCheckResults);
if (empty($aPhpCheckErrors))
{
return;
}
$sMessageTitle = 'Error: PHP minimum requirements are not met !';
$oCliPage->p($sMessageTitle);
$aPhpCheckErrorsForPrint = CheckResult::FromObjectsToStrings($aPhpCheckErrors);
foreach ($aPhpCheckErrorsForPrint as $sError)
{
$oCliPage->p(' * '.$sError);
}
$oCliPage->output();
// some CLI scripts are launched automatically
// we need a log so that we don't miss errors after migration !
IssueLog::Error($oCliPage->s_title.' '.$sMessageTitle, 'CLI', $aPhpCheckErrorsForPrint);
exit($iExitCode);
}
/**
* @param CheckResult[] $aResult checks log
*/
@@ -419,7 +494,7 @@ class SetupUtils
* @param $aSelectedModules
* @return array
*/
static function CheckSelectedModules($sSourceDir, $sExtensionDir, $aSelectedModules)
public static function CheckSelectedModules($sSourceDir, $sExtensionDir, $aSelectedModules)
{
$aResult = array();
SetupPage::log('Info - CheckSelectedModules');
@@ -450,7 +525,7 @@ class SetupUtils
* @return array An array of CheckResults objects
* @internal param Page $oP The page used only for its 'log' method
*/
static function CheckBackupPrerequisites($sDBBackupPath, $sMySQLBinDir = null)
public static function CheckBackupPrerequisites($sDBBackupPath, $sMySQLBinDir = null)
{
$aResult = array();
SetupPage::log('Info - CheckBackupPrerequisites');
@@ -540,7 +615,7 @@ class SetupUtils
* @return CheckResult The result of the check
* @internal param string $GraphvizPath The path where graphviz' dot program is installed
*/
static function CheckGraphviz($sGraphvizPath)
public static function CheckGraphviz($sGraphvizPath)
{
$oResult = null;
SetupPage::log('Info - CheckGraphviz');
@@ -589,7 +664,7 @@ class SetupUtils
* Emulates sys_get_temp_dir if needed (PHP < 5.2.1)
* @return string Path to the system's temp directory
*/
static function GetTmpDir()
public static function GetTmpDir()
{
return realpath(sys_get_temp_dir());
}
@@ -598,7 +673,7 @@ class SetupUtils
* Helper function to retrieve the directory where files are to be uploaded
* @return string Path to the temp directory used for uploading files
*/
static function GetUploadTmpDir()
public static function GetUploadTmpDir()
{
$sPath = ini_get('upload_tmp_dir');
if (empty($sPath))
@@ -821,7 +896,7 @@ class SetupUtils
}
}
static function GetPreviousInstance($sDir)
public static function GetPreviousInstance($sDir)
{
$sSourceDir = '';
$sSourceEnvironment = '';
@@ -875,7 +950,7 @@ class SetupUtils
* @return bool|float false if failure
* @uses \disk_free_space()
*/
static function CheckDiskSpace($sDir)
public static function CheckDiskSpace($sDir)
{
while(($f = @disk_free_space($sDir)) == false)
{
@@ -887,7 +962,7 @@ class SetupUtils
return $f;
}
static function HumanReadableSize($fBytes)
public static function HumanReadableSize($fBytes)
{
$aSizes = array('bytes', 'Kb', 'Mb', 'Gb', 'Tb', 'Pb', 'Hb');
$index = 0;
@@ -912,7 +987,7 @@ class SetupUtils
* @param string $sTlsCA
* @param string $sNewDBName
*/
static function DisplayDBParameters(
public static function DisplayDBParameters(
$oPage, $bIsItopInstall, $sDBServer, $sDBUser, $sDBPwd, $sDBName, $sDBPrefix, $bTlsEnabled, $sTlsCA,
$sNewDBName = ''
) {
@@ -1155,7 +1230,7 @@ EOF
* @return bool|array false if the connection failed or array('checks' => Array of CheckResult, 'databases' =>
* Array of database names (as strings) or null if not allowed)
*/
static function CheckDbServer(
public static function CheckDbServer(
$sDBServer, $sDBUser, $sDBPwd, $bTlsEnabled = false, $sTlsCA = null
)
{
@@ -1606,29 +1681,16 @@ JS
}
$sManualInstallModulesFullPath = APPROOT.$sExtensionsDir.DIRECTORY_SEPARATOR;
$aManualInstallModules = array_filter($aModules,
static function ($v, $k) use ($sManualInstallModulesFullPath) {
if (!isset($v['root_dir'])) // avoid index undefined for the _Root_ entry
{
return false;
}
// calling realpath to avoid problems with dir separator (almost everywhere we are adding '/' instead of DIRECTORY_SEPARATOR)
$return = utils::RealPath($v['root_dir'], $sManualInstallModulesFullPath);
if ($return === false)
{
return false;
}
return true;
},
ARRAY_FILTER_USE_BOTH);
if (empty($aManualInstallModules))
//simple test in order to prevent install iTop pro with module in extension folder
$aFileInfo = scandir($sManualInstallModulesFullPath);
foreach ($aFileInfo as $sFolder)
{
return '';
if ($sFolder != "." && $sFolder != ".." && is_dir($sManualInstallModulesFullPath.$sFolder) === true)
{
return "Some modules are present in the '$sExtensionsDir' directory, this is not allowed when using ".ITOP_APPLICATION;
}
}
return "Some modules are present in the '$sExtensionsDir' directory, this is not allowed when using ".ITOP_APPLICATION;
return '';
}
/**
@@ -2067,7 +2129,7 @@ JS
*/
class SetupInfo
{
static $aSelectedModules = array();
public static $aSelectedModules = array();
/**
* Called by the setup process to initializes the list of selected modules. Do not call this method
@@ -2075,7 +2137,7 @@ class SetupInfo
* @param hash $aModules
* @return void
*/
static function SetSelectedModules($aModules)
public static function SetSelectedModules($aModules)
{
self::$aSelectedModules = $aModules;
}
@@ -2086,7 +2148,7 @@ class SetupInfo
* @param string $sModuleId The identifier of the module (without the version number. Example: itop-config-mgmt)
* @return boolean True if the module is already selected, false otherwise
*/
static function ModuleIsSelected($sModuleId)
public static function ModuleIsSelected($sModuleId)
{
return (array_key_exists($sModuleId, self::$aSelectedModules));
}