mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-19 16:48:42 +02:00
N°2982: theme precompilation fix work even when coming from /extensions or /datamodels/X.x
This commit is contained in:
@@ -51,6 +51,8 @@ class DOMFormatException extends Exception
|
||||
*/
|
||||
class MFCompiler
|
||||
{
|
||||
const DATA_PRECOMPILED_FOLDER = 'data' . DIRECTORY_SEPARATOR . 'precompiled_styles' . DIRECTORY_SEPARATOR;
|
||||
|
||||
/** @var \ModelFactory */
|
||||
protected $oFactory;
|
||||
|
||||
@@ -2862,6 +2864,11 @@ EOF;
|
||||
$aThemes[$aDefaultThemeInfo['name']] = $aDefaultThemeInfo['parameters'];
|
||||
}
|
||||
|
||||
$sPostCompilationPrecompiledThemeFolder = APPROOT . self::DATA_PRECOMPILED_FOLDER;
|
||||
if (! is_dir($sPostCompilationPrecompiledThemeFolder)){
|
||||
mkdir($sPostCompilationPrecompiledThemeFolder);
|
||||
}
|
||||
|
||||
// Compile themes
|
||||
$fStart = microtime(true);
|
||||
foreach($aThemes as $sThemeId => $aThemeParameters)
|
||||
@@ -2871,30 +2878,87 @@ EOF;
|
||||
{
|
||||
SetupUtils::builddir($sThemeDir);
|
||||
}
|
||||
|
||||
// Check if a precompiled version of the theme is supplied
|
||||
$sPrecompiledFile = $sTempTargetDir.$aThemeParameters['precompiled_stylesheet'];
|
||||
if (file_exists($sPrecompiledFile) && !is_dir($sPrecompiledFile))
|
||||
{
|
||||
copy($sPrecompiledFile, $sThemeDir.'/main.css');
|
||||
$sPostCompilationLatestPrecompiledFile = $sPostCompilationPrecompiledThemeFolder . $sThemeId . ".css";
|
||||
|
||||
$sPrecompiledFileToUse = $this->UseLatestPrecompiledFile($sTempTargetDir, $aThemeParameters['precompiled_stylesheet'], $sPostCompilationLatestPrecompiledFile, $sThemeId);
|
||||
if ($sPrecompiledFileToUse != null){
|
||||
copy($sPrecompiledFileToUse, $sThemeDir.'/main.css');
|
||||
// Make sure that the copy of the precompiled file is older than any other files to force a validation of the signature
|
||||
touch($sThemeDir.'/main.css', 1577836800 /* 2020-01-01 00:00:00 */);
|
||||
|
||||
}
|
||||
else if ($sPrecompiledFile != '')
|
||||
{
|
||||
$this->Log("Precompiled file not found: '$sPrecompiledFile'");
|
||||
}
|
||||
|
||||
$bHasCompiled = ThemeHandler::CompileTheme($sThemeId, true, $this->sCompilationTimeStamp, $aThemeParameters, $aImportsPaths, $sTempTargetDir);
|
||||
$sInitialPrecompiledFilePath = APPROOT.'datamodels/2.x/'.$aThemeParameters['precompiled_stylesheet'];
|
||||
if ($bHasCompiled && is_file($sInitialPrecompiledFilePath))
|
||||
if ($bHasCompiled)
|
||||
{
|
||||
SetupLog::Info("Replacing precompiled file $sInitialPrecompiledFilePath for theme $sThemeId for next setup.");
|
||||
copy($sThemeDir.'/main.css', $sInitialPrecompiledFilePath);
|
||||
SetupLog::Info("Replacing theme '$sThemeId' precompiled file in file $sPostCompilationLatestPrecompiledFile for next setup.");
|
||||
copy($sThemeDir.'/main.css', $sPostCompilationLatestPrecompiledFile);
|
||||
}
|
||||
}
|
||||
$this->Log(sprintf('Themes compilation took: %.3f ms for %d themes.', (microtime(true) - $fStart)*1000.0, count($aThemes)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Choose between precompiled files declared in datamodel XMLs or latest precompiled files generated after latest setup.
|
||||
* @param $sTempTargetDir
|
||||
* @param $sPrecompiledFileUri
|
||||
* @param $sPostCompilationLatestPrecompiledFile
|
||||
* @param $sThemeId
|
||||
*
|
||||
* @return string : file path of latest precompiled file to use for setup
|
||||
*/
|
||||
public function UseLatestPrecompiledFile($sTempTargetDir, $sPrecompiledFileUri, $sPostCompilationLatestPrecompiledFile, $sThemeId) : ?string {
|
||||
$bDataXmlPrecompiledFileExists = false;
|
||||
clearstatcache();
|
||||
if (!empty($sPrecompiledFileUri)){
|
||||
$sDataXmlProvidedPrecompiledFile = $sTempTargetDir . DIRECTORY_SEPARATOR . $sPrecompiledFileUri;
|
||||
$bDataXmlPrecompiledFileExists = file_exists($sDataXmlProvidedPrecompiledFile) ;
|
||||
if (!$bDataXmlPrecompiledFileExists){
|
||||
SetupLog::Warning("Missing defined theme '$sThemeId' precompiled file configured with: '$sPrecompiledFileUri'");
|
||||
} else {
|
||||
$sSourceDir = APPROOT . utils::GetConfig()->Get('source_dir');
|
||||
|
||||
$aDirToCheck = [
|
||||
$sSourceDir,
|
||||
APPROOT . DIRECTORY_SEPARATOR . 'extensions/'
|
||||
];
|
||||
|
||||
$iDataXmlFileLastModified = 0;
|
||||
foreach ($aDirToCheck as $sDir){
|
||||
$sCurrentFile = $sDir . DIRECTORY_SEPARATOR . $sPrecompiledFileUri;
|
||||
if (is_file($sCurrentFile)){
|
||||
$iDataXmlFileLastModified = max($iDataXmlFileLastModified, @filemtime($sCurrentFile));
|
||||
}
|
||||
}
|
||||
|
||||
if ($iDataXmlFileLastModified == 0){
|
||||
SetupLog::Warning("Missing defined theme '$sThemeId' precompiled file in datamodels/X.x or extensions directory configured with: '$sPrecompiledFileUri'. That should not happen!");
|
||||
$bDataXmlPrecompiledFileExists = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
$this->Log(sprintf('Themes compilation took: %.3f ms for %d themes.', (microtime(true) - $fStart)*1000.0, count($aThemes)));
|
||||
|
||||
$bPostCompilationPrecompiledFileExists = file_exists($sPostCompilationLatestPrecompiledFile);
|
||||
|
||||
if (!$bDataXmlPrecompiledFileExists && !$bPostCompilationPrecompiledFileExists){
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!$bDataXmlPrecompiledFileExists){
|
||||
$sPrecompiledFileToUse = $sPostCompilationLatestPrecompiledFile;
|
||||
} else if (!$bPostCompilationPrecompiledFileExists){
|
||||
$sPrecompiledFileToUse = $sDataXmlProvidedPrecompiledFile;
|
||||
} else{
|
||||
$iPostCompilationFileLastModified = @filemtime($sPostCompilationLatestPrecompiledFile);
|
||||
SetupLog::Debug("Theme '$sThemeId' check mtime between data XML file " . $iDataXmlFileLastModified . " and latest postcompilation file: " . $iPostCompilationFileLastModified);
|
||||
|
||||
$sPrecompiledFileToUse = $iDataXmlFileLastModified > $iPostCompilationFileLastModified ? $sDataXmlProvidedPrecompiledFile : $sPostCompilationLatestPrecompiledFile;
|
||||
}
|
||||
|
||||
SetupLog::Info("For theme '$sThemeId' precompiled file used: '$sPrecompiledFileToUse'");
|
||||
return $sPrecompiledFileToUse;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
131
test/setup/MFCompilerTest.php
Normal file
131
test/setup/MFCompilerTest.php
Normal file
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopTestCase;
|
||||
|
||||
/**
|
||||
* @runTestsInSeparateProcesses
|
||||
* @preserveGlobalState disabled
|
||||
* @backupGlobals disabled
|
||||
* @covers utils
|
||||
*/
|
||||
class MFCompilerTest extends ItopTestCase {
|
||||
/** @var array */
|
||||
private static $aFoldersToCleanup;
|
||||
|
||||
/** @var array */
|
||||
private static $aRessources;
|
||||
|
||||
/** @var \MFCompiler */
|
||||
private $oMFCompiler;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
@include_once '/home/combodo/workspace/iTop/approot.inc.php';
|
||||
parent::setUp();
|
||||
require_once(APPROOT.'setup/compiler.class.inc.php');
|
||||
|
||||
$this->oMFCompiler = new MFCompiler($this->createMock(\ModelFactory::class), '');
|
||||
}
|
||||
|
||||
public static function Init(){
|
||||
if (!is_null(self::$aFoldersToCleanup)){
|
||||
return;
|
||||
}
|
||||
clearstatcache();
|
||||
$sPrefix = 'scsstest_';
|
||||
$sAppRootForProvider = dirname(dirname(dirname(__FILE__))) . DIRECTORY_SEPARATOR;
|
||||
$sTempTargetDir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'UseLatestPrecompiledFileProvider';
|
||||
$sExtensionTargetDir = $sAppRootForProvider . 'extensions/UseLatestPrecompiledFileProvider';
|
||||
$sSourceDir = $sAppRootForProvider . 'datamodels' . DIRECTORY_SEPARATOR . '2.x';
|
||||
$sDatamodel2xTargetDir = $sSourceDir . DIRECTORY_SEPARATOR . '/UseLatestPrecompiledFileProvider';
|
||||
|
||||
mkdir($sTempTargetDir);
|
||||
mkdir($sExtensionTargetDir);
|
||||
mkdir($sDatamodel2xTargetDir);
|
||||
|
||||
self::$aFoldersToCleanup = [ $sTempTargetDir, $sExtensionTargetDir, $sDatamodel2xTargetDir ];
|
||||
|
||||
self::$aRessources['sPostCompilation1'] = tempnam($sTempTargetDir, $sPrefix);
|
||||
sleep(1);
|
||||
|
||||
//datamodel XML file in extension folder
|
||||
self::$aRessources['sPrecompiledInExtensionFile1'] = tempnam($sExtensionTargetDir, $sPrefix);
|
||||
self::$aRessources['sPrecompiledInExtensionFileUri1'] = "UseLatestPrecompiledFileProvider" . DIRECTORY_SEPARATOR . basename(self::$aRessources['sPrecompiledInExtensionFile1']);
|
||||
|
||||
//datamodel XML file in source dir /datamodels/2.x folder
|
||||
self::$aRessources['sPrecompiledInDataModelXXFile1'] = tempnam($sDatamodel2xTargetDir, $sPrefix);
|
||||
self::$aRessources['sPrecompiledInDataModelXXFileUri1'] = "UseLatestPrecompiledFileProvider" . DIRECTORY_SEPARATOR . basename(self::$aRessources['sPrecompiledInDataModelXXFile1']);
|
||||
|
||||
sleep(1);
|
||||
|
||||
|
||||
//generate ressources from a previous setup: called postcompiled
|
||||
self::$aRessources['sPostCompilation2'] = tempnam($sTempTargetDir, $sPrefix);
|
||||
sleep(1);
|
||||
|
||||
//simulate copy of /data/models.2.x or extensions ressources during setup in a temp directory
|
||||
self::$aRessources['sCopiedExtensionFile1'] = $sTempTargetDir . DIRECTORY_SEPARATOR . basename(self::$aRessources['sPrecompiledInExtensionFile1']);
|
||||
copy(self::$aRessources['sPrecompiledInExtensionFile1'], self::$aRessources['sCopiedExtensionFile1']);
|
||||
|
||||
self::$aRessources['sCopiedDataModelXXFile1'] = $sTempTargetDir . DIRECTORY_SEPARATOR . basename(self::$aRessources['sPrecompiledInDataModelXXFile1']);
|
||||
copy(self::$aRessources['sPrecompiledInDataModelXXFile1'], self::$aRessources['sCopiedDataModelXXFile1']);
|
||||
|
||||
self::$aRessources['sMissingFile'] = tempnam($sTempTargetDir, $sPrefix);
|
||||
unlink(self::$aRessources['sMissingFile']);
|
||||
|
||||
/*foreach (self::$aRessources as $sKey => $sRessource){
|
||||
if (is_file($sRessource)) {
|
||||
var_dump("$sKey $sRessource:" . filemtime($sRessource));
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
public static function tearDownAfterClass()
|
||||
{
|
||||
if (is_null(self::$aFoldersToCleanup)){
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (self::$aFoldersToCleanup as $sFolder){
|
||||
if (is_dir($sFolder)){
|
||||
foreach (glob("$sFolder/**") as $sFile){
|
||||
unlink($sFile);
|
||||
}
|
||||
rmdir($sFolder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider UseLatestPrecompiledFileProvider
|
||||
*/
|
||||
public function testUseLatestPrecompiledFile(string $sTempTargetDir, string $sPrecompiledFileUri, string $sPostCompilationLatestPrecompiledFile, string $sThemeDir, $sExpectedReturn){
|
||||
$sRes = $this->oMFCompiler->UseLatestPrecompiledFile($sTempTargetDir, $sPrecompiledFileUri, $sPostCompilationLatestPrecompiledFile, $sThemeDir);
|
||||
$this->assertEquals($sExpectedReturn, $sRes);
|
||||
}
|
||||
|
||||
public function UseLatestPrecompiledFileProvider(){
|
||||
self::init();
|
||||
return [
|
||||
'no precompiled file configured in precompiled_stylesheet XM section' => $this->BuildProviderUseCaseArray('', self::$aRessources['sPostCompilation1'], self::$aRessources['sPostCompilation1']),
|
||||
'missing precompiled file in precompiled_stylesheet section' => $this->BuildProviderUseCaseArray(self::$aRessources['sMissingFile'], self::$aRessources['sPostCompilation1'], self::$aRessources['sPostCompilation1'] ),
|
||||
'no precompiled file generated in previous setup in /data/precompiled_styles' => $this->BuildProviderUseCaseArray(self::$aRessources['sPrecompiledInExtensionFileUri1'], self::$aRessources['sMissingFile'], self::$aRessources['sCopiedExtensionFile1'] ),
|
||||
'(extensions) XML precompiled_stylesheet file older than last post setup generated file in /data/precompiled_styles' => $this->BuildProviderUseCaseArray(self::$aRessources['sPrecompiledInExtensionFileUri1'], self::$aRessources['sPostCompilation2'], self::$aRessources['sPostCompilation2'] ),
|
||||
'last post setup generated file in /data/precompiled_styles older than (extensions) XML precompiled_stylesheet file' => $this->BuildProviderUseCaseArray(self::$aRessources['sPrecompiledInExtensionFileUri1'], self::$aRessources['sPostCompilation1'], self::$aRessources['sCopiedExtensionFile1'] ),
|
||||
'(datamodels/N.x) XML precompiled_stylesheet file older than last post setup generated file in /data/precompiled_styles' => $this->BuildProviderUseCaseArray(self::$aRessources['sPrecompiledInDataModelXXFileUri1'], self::$aRessources['sPostCompilation2'], self::$aRessources['sPostCompilation2'] ),
|
||||
'(datamodels/N.x) last post setup generated file in /data/precompiled_styles older than (extensions) XML precompiled_stylesheet file' => $this->BuildProviderUseCaseArray(self::$aRessources['sPrecompiledInDataModelXXFileUri1'], self::$aRessources['sPostCompilation1'], self::$aRessources['sCopiedDataModelXXFile1'] ),
|
||||
];
|
||||
}
|
||||
|
||||
private function BuildProviderUseCaseArray(string $sPrecompiledFileUri, string $sPostCompilationLatestPrecompiledFile, $sExpectedReturn) : array{
|
||||
return [
|
||||
"sTempTargetDir" => sys_get_temp_dir(),
|
||||
"sPrecompiledFileUri" => $sPrecompiledFileUri,
|
||||
"sPostCompilationLatestPrecompiledFile" => $sPostCompilationLatestPrecompiledFile,
|
||||
"sThemeDir" => "test",
|
||||
"sExpectedReturn" => $sExpectedReturn
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user