mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-13 07:24:13 +01:00
Faster compilation of themes
- Ability to provided precompiled themes in the datamodel - Check that a precompiled theme is still up-to-date based on a signature
This commit is contained in:
@@ -116,7 +116,9 @@ class ThemeHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the $sThemeId theme
|
||||
* Compile the $sThemeId theme, the actual compilation is skipped when either
|
||||
* 1) The produced CSS file exists and is more recent than any of its components (imports, stylesheets)
|
||||
* 2) The produced CSS file exists and its signature is equal to the expected signature (imports, stylesheets, variables)
|
||||
*
|
||||
* @param string $sThemeId
|
||||
* @param array|null $aThemeParameters Parameters (variables, imports, stylesheets) for the theme, if not passed, will be retrieved from compiled DM
|
||||
@@ -168,23 +170,130 @@ class ThemeHandler
|
||||
{
|
||||
$sTmpThemeScssContent .= '@import "'.$sImport.'";'."\n";
|
||||
|
||||
$iImportLastModified = @filemtime($sWorkingPath.$sImport);
|
||||
$sFile = static::FindStylesheetFile($sImport, $aImportsPaths);
|
||||
$iImportLastModified = @filemtime($sFile);
|
||||
$iStyleLastModified = $iStyleLastModified < $iImportLastModified ? $iImportLastModified : $iStyleLastModified;
|
||||
}
|
||||
foreach ($aThemeParameters['stylesheets'] as $sStylesheet)
|
||||
{
|
||||
$sTmpThemeScssContent .= '@import "'.$sStylesheet.'";'."\n";
|
||||
|
||||
$iStylesheetLastModified = @filemtime($sWorkingPath.$sStylesheet);
|
||||
$sFile = static::FindStylesheetFile($sStylesheet, $aImportsPaths);
|
||||
$iStylesheetLastModified = @filemtime($sFile);
|
||||
$iStyleLastModified = $iStyleLastModified < $iStylesheetLastModified ? $iStylesheetLastModified : $iStyleLastModified;
|
||||
}
|
||||
|
||||
// Checking if our compiled css is outdated
|
||||
if (!file_exists($sThemeCssPath) || (is_writable($sThemeFolderPath) && (@filemtime($sThemeCssPath) < $iStyleLastModified)))
|
||||
$iFilemetime = @filemtime($sThemeCssPath);
|
||||
if (!file_exists($sThemeCssPath) || (is_writable($sThemeFolderPath) && ($iFilemetime < $iStyleLastModified)))
|
||||
{
|
||||
$sTmpThemeCssContent = utils::CompileCSSFromSASS($sTmpThemeScssContent, $aImportsPaths, $aThemeParameters['variables']);
|
||||
file_put_contents($sThemeCssPath, $sTmpThemeCssContent);
|
||||
// Dates don't match. Second chance: check if the already compiled stylesheet exists and is consistent based on its signature
|
||||
$sActualSignature = static::ComputeSignature($aThemeParameters, $aImportsPaths);
|
||||
if (file_exists($sThemeCssPath))
|
||||
{
|
||||
$sPrecompiledSignature = static::GetSignature($sThemeCssPath);
|
||||
if($sActualSignature == $sPrecompiledSignature)
|
||||
{
|
||||
touch($sThemeCssPath); // Stylesheet is up to date, mark it as more recent to speedup next time
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Alas, we really need to recompile
|
||||
// Add the signature to the generated CSS file so that the file can be used as a precompiled stylesheet if needed
|
||||
$sSignatureComment =
|
||||
<<<CSS
|
||||
/*
|
||||
=== SIGNATURE BEGIN ===
|
||||
$sActualSignature
|
||||
=== SIGNATURE END ===
|
||||
*/
|
||||
|
||||
CSS
|
||||
;
|
||||
$sTmpThemeCssContent = utils::CompileCSSFromSASS($sTmpThemeScssContent, $aImportsPaths, $aThemeParameters['variables']);
|
||||
file_put_contents($sThemeCssPath, $sSignatureComment.$sTmpThemeCssContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the signature of a theme defined by its theme parameters. The signature is a JSON structure of
|
||||
* 1) one MD5 of all the variables/values (JSON encoded)
|
||||
* 2) the MD5 of each stylesheet file
|
||||
* 3) the MD5 of each import file
|
||||
*
|
||||
* @param string[] $aThemeParameters
|
||||
* @param string[] $aImportsPaths
|
||||
* @return string
|
||||
*/
|
||||
private static function ComputeSignature($aThemeParameters, $aImportsPaths)
|
||||
{
|
||||
$aSignature = array(
|
||||
'variables' => md5(json_encode($aThemeParameters['variables'])),
|
||||
'stylesheets' => array(),
|
||||
'imports' => array(),
|
||||
);
|
||||
|
||||
foreach ($aThemeParameters['imports'] as $key => $sImport)
|
||||
{
|
||||
$sFile = static::FindStylesheetFile($sImport, $aImportsPaths);
|
||||
$aSignature['stylesheets'][$key] = md5_file($sFile);
|
||||
}
|
||||
foreach ($aThemeParameters['stylesheets'] as $key => $sStylesheet)
|
||||
{
|
||||
$sFile = static::FindStylesheetFile($sStylesheet, $aImportsPaths);
|
||||
$aSignature['stylesheets'][$key] = md5_file($sFile);
|
||||
}
|
||||
return json_encode($aSignature);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the signature for a generated CSS file. The signature MUST be alone one line immediately
|
||||
* followed (on the next line) by the === SIGNATURE END === pattern
|
||||
*
|
||||
* Note the signature can be place anywhere in the CSS file (obviously inside a CSS comment !) but the
|
||||
* function will be faster if the signature is at the beginning of the file (since the file is scanned from the start)
|
||||
*
|
||||
* @param string $sFile
|
||||
* @return string
|
||||
*/
|
||||
private static function GetSignature($sFilepath)
|
||||
{
|
||||
$sPreviousLine = '';
|
||||
$hFile = @fopen($sFilepath, "r");
|
||||
if ($hFile !== false)
|
||||
{
|
||||
$sLine = '';
|
||||
do
|
||||
{
|
||||
$sPreviousLine = $sLine;
|
||||
$sLine = rtrim(fgets($hFile)); // Remove the trailing \n
|
||||
}
|
||||
while (($sLine !== false) && ($sLine != '=== SIGNATURE END ==='));
|
||||
fclose($hFile);
|
||||
}
|
||||
return $sPreviousLine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the given file in the list of ImportsPaths directory
|
||||
* @param string $sFile
|
||||
* @param string[] $aImportsPaths
|
||||
* @throws Exception
|
||||
* @return string
|
||||
*/
|
||||
private static function FindStylesheetFile($sFile, $aImportsPaths)
|
||||
{
|
||||
foreach($aImportsPaths as $sPath)
|
||||
{
|
||||
$sImportedFile = realpath($sPath.'/'.$sFile);
|
||||
if (file_exists($sImportedFile))
|
||||
{
|
||||
return $sImportedFile;
|
||||
}
|
||||
}
|
||||
return ''; // Not found, fail silently, maybe the SCSS compiler knowns better...
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8529,6 +8529,7 @@
|
||||
<stylesheet id="jqueryui">../css/ui-lightness/jqueryui.scss</stylesheet>
|
||||
<stylesheet id="main">../css/light-grey.scss</stylesheet>
|
||||
</stylesheets>
|
||||
<precompiled_stylesheet>itop-config-mgmt/precompiled-themes/light-grey/main.css</precompiled_stylesheet>
|
||||
</theme>
|
||||
<theme id="test-red" _delta="define">
|
||||
<variables>
|
||||
@@ -8544,6 +8545,7 @@
|
||||
<stylesheet id="main">../css/light-grey.scss</stylesheet>
|
||||
<stylesheet id="environment-banner">../css/backoffice-environment-banner.scss</stylesheet>
|
||||
</stylesheets>
|
||||
<precompiled_stylesheet>itop-config-mgmt/precompiled-themes/test-red/main.css</precompiled_stylesheet>
|
||||
</theme>
|
||||
</themes>
|
||||
</branding>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
5246
datamodels/2.x/itop-config-mgmt/precompiled-themes/test-red/main.css
Normal file
5246
datamodels/2.x/itop-config-mgmt/precompiled-themes/test-red/main.css
Normal file
File diff suppressed because it is too large
Load Diff
@@ -2701,6 +2701,7 @@ EOF;
|
||||
'variables' => array(),
|
||||
'imports' => array(),
|
||||
'stylesheets' => array(),
|
||||
'precompiled_stylesheet' => '',
|
||||
);
|
||||
|
||||
/** @var \DOMNodeList $oVariables */
|
||||
@@ -2726,7 +2727,7 @@ EOF;
|
||||
$sStylesheetId = $oStylesheet->getAttribute('id');
|
||||
$aThemeParameters['stylesheets'][$sStylesheetId] = $oStylesheet->GetText();
|
||||
}
|
||||
|
||||
$aThemeParameters['precompiled_stylesheet'] = $oTheme->GetChildText('precompiled_stylesheet', '');
|
||||
$aThemes[$sThemeId] = $aThemeParameters;
|
||||
}
|
||||
|
||||
@@ -2738,6 +2739,7 @@ EOF;
|
||||
}
|
||||
|
||||
// Compile themes
|
||||
$fStart = microtime(true);
|
||||
foreach($aThemes as $sThemeId => $aThemeParameters)
|
||||
{
|
||||
$sThemeDir = $sThemesDir.$sThemeId;
|
||||
@@ -2745,10 +2747,21 @@ EOF;
|
||||
{
|
||||
SetupUtils::builddir($sThemeDir);
|
||||
}
|
||||
|
||||
|
||||
// Check if a precompiled version of the theme is supplied
|
||||
$sPrecompiledFile = $sTempTargetDir.$aThemeParameters['precompiled_stylesheet'];
|
||||
if (file_exists($sPrecompiledFile))
|
||||
{
|
||||
copy($sPrecompiledFile, $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'");
|
||||
}
|
||||
ThemeHandler::CompileTheme($sThemeId, $aThemeParameters, $aImportsPaths, $sTempTargetDir);
|
||||
}
|
||||
$this->Log(sprintf('Themes compilation took: %.3f ms for %d themes.', (microtime(true) - $fStart)*1000.0, count($aThemes)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user