bug2996: include images in precompiled signature
@@ -173,12 +173,15 @@ class ThemeHandler
|
||||
$iStyleLastModified = 0;
|
||||
clearstatcache();
|
||||
// Loading files to import and stylesheet to compile, also getting most recent modification time on overall files
|
||||
|
||||
$aStylesheetFile=array();
|
||||
foreach ($aThemeParameters['imports'] as $sImport)
|
||||
{
|
||||
$sTmpThemeScssContent .= '@import "'.$sImport.'";'."\n";
|
||||
|
||||
$sFile = static::FindStylesheetFile($sImport, $aImportsPaths);
|
||||
$iImportLastModified = @filemtime($sFile);
|
||||
$aStylesheetFile[]=$sFile;
|
||||
$iStyleLastModified = $iStyleLastModified < $iImportLastModified ? $iImportLastModified : $iStyleLastModified;
|
||||
}
|
||||
foreach ($aThemeParameters['stylesheets'] as $sStylesheet)
|
||||
@@ -187,9 +190,25 @@ class ThemeHandler
|
||||
|
||||
$sFile = static::FindStylesheetFile($sStylesheet, $aImportsPaths);
|
||||
$iStylesheetLastModified = @filemtime($sFile);
|
||||
$aStylesheetFile[]=$sFile;
|
||||
$iStyleLastModified = $iStyleLastModified < $iStylesheetLastModified ? $iStylesheetLastModified : $iStyleLastModified;
|
||||
}
|
||||
|
||||
$aIncludedImages=self::GetIncludedImages($aThemeParameters['variables'], $aStylesheetFile, $sThemeFolderPath);
|
||||
foreach ($aIncludedImages as $sImage)
|
||||
{
|
||||
if (!is_file($sImage))
|
||||
{
|
||||
//TODO log warning
|
||||
echo "Cannot find $sImage\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$iStylesheetLastModified = @filemtime($sImage);
|
||||
$iStyleLastModified = $iStyleLastModified < $iStylesheetLastModified ? $iStylesheetLastModified : $iStyleLastModified;
|
||||
}
|
||||
}
|
||||
|
||||
// Checking if our compiled css is outdated
|
||||
$iFilemetime = @filemtime($sThemeCssPath);
|
||||
$bFileExists = file_exists($sThemeCssPath);
|
||||
@@ -208,7 +227,7 @@ class ThemeHandler
|
||||
if (!$bFileExists || $bVarSignatureChanged || (is_writable($sThemeFolderPath) && ($iFilemetime < $iStyleLastModified)))
|
||||
{
|
||||
// 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);
|
||||
$sActualSignature = static::ComputeSignature($aThemeParameters, $aImportsPaths, $aIncludedImages);
|
||||
|
||||
if ($bFileExists && !$bSetup)
|
||||
{
|
||||
@@ -251,16 +270,18 @@ CSS;
|
||||
*
|
||||
* @param string[] $aThemeParameters
|
||||
* @param string[] $aImportsPaths
|
||||
* @param $aIncludedImages
|
||||
*
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function ComputeSignature($aThemeParameters, $aImportsPaths)
|
||||
public static function ComputeSignature($aThemeParameters, $aImportsPaths, $aIncludedImages)
|
||||
{
|
||||
$aSignature = array(
|
||||
'variables' => md5(json_encode($aThemeParameters['variables'])),
|
||||
'stylesheets' => array(),
|
||||
'imports' => array(),
|
||||
'images' => array(),
|
||||
);
|
||||
|
||||
foreach ($aThemeParameters['imports'] as $key => $sImport)
|
||||
@@ -273,9 +294,278 @@ CSS;
|
||||
$sFile = static::FindStylesheetFile($sStylesheet, $aImportsPaths);
|
||||
$aSignature['stylesheets'][$key] = md5_file($sFile);
|
||||
}
|
||||
foreach ($aIncludedImages as $sImage)
|
||||
{
|
||||
if (is_file($sImage))
|
||||
{
|
||||
$aSignature['images'][$sImage] = md5_file($sImage);
|
||||
}
|
||||
}
|
||||
|
||||
return json_encode($aSignature);
|
||||
}
|
||||
|
||||
const IMAGE_EXTENSIONS = array('png', 'gif', 'jpg', 'jpeg');
|
||||
|
||||
/**
|
||||
* Search for images referenced in stylesheet files
|
||||
* @param $aThemeParametersVariables
|
||||
* @param $aStylesheetFile
|
||||
* @param $sThemeFolderPath : used as relative paths to find css images
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function GetIncludedImages($aThemeParametersVariables, $aStylesheetFile, $sThemeFolderPath)
|
||||
{
|
||||
$aCompleteUrls = array();
|
||||
$aToCompleteUrls = array();
|
||||
$aMissingVariables = array();
|
||||
$aFoundVariables = array();
|
||||
$aMap = array(
|
||||
'aCompleteUrls' => $aCompleteUrls,
|
||||
'aToCompleteUrls' => $aToCompleteUrls,
|
||||
'aMissingVariables' => $aMissingVariables,
|
||||
'aFoundVariables' => $aFoundVariables,
|
||||
);
|
||||
|
||||
foreach ($aStylesheetFile as $sStylesheetFile)
|
||||
{
|
||||
$aRes = self::GetAllUrlFromScss($aThemeParametersVariables, $sStylesheetFile);
|
||||
foreach($aMap as $key => /** array */$aVal)
|
||||
{
|
||||
if (array_key_exists($key, $aMap))
|
||||
{
|
||||
$aMap[$key]=array_merge($aVal, $aRes[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$aMap = ThemeHandler::ResolveUncompleteUrlsFromScss($aMap, $aThemeParametersVariables, $aStylesheetFile);
|
||||
$aImages = array();
|
||||
foreach ($aMap ['aCompleteUrls'] as $sUrl)
|
||||
{
|
||||
$sImg = $sUrl;
|
||||
if (preg_match("/(.*)\?/", $sUrl, $aMatches))
|
||||
{
|
||||
$sImg=$aMatches[1];
|
||||
}
|
||||
|
||||
if (self::HasImageExtension($sImg)
|
||||
&& ! array_key_exists($sImg, $aImages))
|
||||
{
|
||||
if (!is_file($sImg))
|
||||
{
|
||||
$sImg=$sThemeFolderPath.DIRECTORY_SEPARATOR.$sImg;
|
||||
}
|
||||
$aImages[$sImg]=$sImg;
|
||||
}
|
||||
}
|
||||
|
||||
return array_values($aImages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete url using provided variables. Example with $var=1: XX + $var => XX1
|
||||
* @param $aMap
|
||||
* @param $aThemeParametersVariables
|
||||
* @param $aStylesheetFile
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function ResolveUncompleteUrlsFromScss($aMap, $aThemeParametersVariables, $aStylesheetFile)
|
||||
{
|
||||
$sContent="";
|
||||
foreach ($aStylesheetFile as $sStylesheetFile)
|
||||
{
|
||||
if (is_file($sStylesheetFile))
|
||||
{
|
||||
$sContent .= '\n' . file_get_contents($sStylesheetFile);
|
||||
}
|
||||
}
|
||||
|
||||
$aMissingVariables=$aMap['aMissingVariables'];
|
||||
$aFoundVariables=$aMap['aFoundVariables'];
|
||||
$aToCompleteUrls=$aMap['aToCompleteUrls'];
|
||||
$aCompleteUrls=$aMap['aCompleteUrls'];
|
||||
list($aMissingVariables, $aFoundVariables) = self::FindMissingVariables($aThemeParametersVariables, $aMissingVariables, $aFoundVariables, $sContent);
|
||||
list($aToCompleteUrls, $aCompleteUrls) = self::ResolveUrls($aFoundVariables, $aToCompleteUrls, $aCompleteUrls);
|
||||
$aMap['aMissingVariables']=$aMissingVariables;
|
||||
$aMap['aFoundVariables']=$aFoundVariables;
|
||||
$aMap['aToCompleteUrls']=$aToCompleteUrls;
|
||||
$aMap['aCompleteUrls']=$aCompleteUrls;
|
||||
return $aMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find missing variable values from SCSS content based on their name.
|
||||
* @param $aThemeParametersVariables
|
||||
* @param $aMissingVariables
|
||||
* @param $aFoundVariables
|
||||
* @param $sContent: scss content
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function FindMissingVariables($aThemeParametersVariables, $aMissingVariables, $aFoundVariables, $sContent)
|
||||
{
|
||||
if (!empty($aMissingVariables))
|
||||
{
|
||||
foreach ($aMissingVariables as $var)
|
||||
{
|
||||
if (array_key_exists($var, $aThemeParametersVariables))
|
||||
{
|
||||
$aFoundVariables[$var] = $aThemeParametersVariables[$var];
|
||||
unset($aMissingVariables[$var]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (preg_match_all("/\\\$$var\s*:\s*[\"'](.*)[\"']/", $sContent, $aValues))
|
||||
{
|
||||
$aFoundVariables[$var] = $aValues[1][0];
|
||||
unset($aMissingVariables[$var]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array($aMissingVariables, $aFoundVariables);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $aFoundVariables
|
||||
* @param array $aToCompleteUrls
|
||||
* @param array $aCompleteUrls
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function ResolveUrls($aFoundVariables, array $aToCompleteUrls, array $aCompleteUrls)
|
||||
{
|
||||
if (!empty($aFoundVariables))
|
||||
{
|
||||
foreach ($aToCompleteUrls as $sUrlTemplate)
|
||||
{
|
||||
unset($aToCompleteUrls[$sUrlTemplate]);
|
||||
$sResolvedUrl = ThemeHandler::ResolveUrl($sUrlTemplate, $aFoundVariables);
|
||||
if ($sResolvedUrl == false)
|
||||
{
|
||||
$aToCompleteUrls[$sUrlTemplate] = $sUrlTemplate;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aCompleteUrls[$sUrlTemplate] = $sResolvedUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array($aToCompleteUrls, $aCompleteUrls);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all referenced URLs from a SCSS file.
|
||||
* @param $aThemeParametersVariables
|
||||
* @param $sStylesheetFile
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function GetAllUrlFromScss($aThemeParametersVariables, $sStylesheetFile)
|
||||
{
|
||||
$aCompleteUrls = array();
|
||||
$aToCompleteUrls = array();
|
||||
$aMissingVariables = array();
|
||||
$aFoundVariables = array();
|
||||
|
||||
if (is_file($sStylesheetFile))
|
||||
{
|
||||
$sContent = file_get_contents($sStylesheetFile);
|
||||
if (preg_match_all("/url\s*\((.*)\)/", $sContent, $aMatches))
|
||||
{
|
||||
foreach ($aMatches[1] as $path)
|
||||
{
|
||||
if (!array_key_exists($path, $aCompleteUrls)
|
||||
&& !array_key_exists($path, $aToCompleteUrls))
|
||||
{
|
||||
if (preg_match_all("/\\$([\w-_]+)/", $path, $aCurrentVars))
|
||||
{
|
||||
foreach ($aCurrentVars[1] as $var)
|
||||
{
|
||||
if (!array_key_exists($var, $aMissingVariables))
|
||||
{
|
||||
$aMissingVariables[$var] = $var;
|
||||
}
|
||||
}
|
||||
$aToCompleteUrls[$path] = $path;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aCompleteUrls[$path] = trim($path, "\"'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty($aMissingVariables))
|
||||
{
|
||||
list($aMissingVariables, $aFoundVariables) = self::FindMissingVariables($aThemeParametersVariables, $aMissingVariables, $aFoundVariables, $sContent);
|
||||
list($aToCompleteUrls, $aCompleteUrls) = self::ResolveUrls($aFoundVariables, $aToCompleteUrls, $aCompleteUrls);
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
'aCompleteUrls' => $aCompleteUrls,
|
||||
'aToCompleteUrls' => $aToCompleteUrls,
|
||||
'aMissingVariables' => $aMissingVariables,
|
||||
'aFoundVariables' => $aFoundVariables,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate url based on its template + variables.
|
||||
* @param $sUrlTemplate
|
||||
* @param $aFoundVariables
|
||||
*
|
||||
* @return bool|string
|
||||
*/
|
||||
public static function ResolveUrl($sUrlTemplate, $aFoundVariables)
|
||||
{
|
||||
$aPattern=array();
|
||||
$aReplacement=array();
|
||||
foreach ($aFoundVariables as $aFoundVariable => $aFoundVariableValue)
|
||||
{
|
||||
//XX + $key + YY
|
||||
$aPattern[]="/['\"]\s*\+\s*\\\$" . $aFoundVariable . "[\s\+]+\s*['\"]/";
|
||||
//$key + YY
|
||||
$aPattern[]="/\\\$" . $aFoundVariable. "[\s\+]+\s*['\"]/";
|
||||
//XX + $key
|
||||
$aPattern[]="/['\"]\s*[\+\s]+\\\$" . $aFoundVariable . "$/";
|
||||
$aReplacement[]=$aFoundVariableValue;
|
||||
$aReplacement[]=$aFoundVariableValue;
|
||||
$aReplacement[]=$aFoundVariableValue;
|
||||
}
|
||||
$sResolvedUrl=preg_replace($aPattern, $aReplacement, $sUrlTemplate);
|
||||
if (strpos($sResolvedUrl, "+")!=false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return trim($sResolvedUrl, "\"'");
|
||||
}
|
||||
|
||||
/**
|
||||
* indicate whether a string ends with image suffix.
|
||||
* @param $path
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function HasImageExtension($path)
|
||||
{
|
||||
foreach (self::IMAGE_EXTENSIONS as $sExt)
|
||||
{
|
||||
if (endsWith($path, $sExt))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
@@ -14,6 +14,7 @@ class ThemeHandlerTest extends ItopTestCase
|
||||
private $cssPath;
|
||||
private $jsonThemeParamFile;
|
||||
private $tmpDir;
|
||||
private $aDirsToCleanup=array();
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
@@ -25,6 +26,7 @@ class ThemeHandlerTest extends ItopTestCase
|
||||
ThemeHandler::mockCompileCSSService($this->compileCSSServiceMock);
|
||||
|
||||
$this->tmpDir=$this->tmpdir();
|
||||
$aDirsToCleanup[] = $this->tmpDir;
|
||||
|
||||
if (!is_dir($this->tmpDir ."/branding"))
|
||||
{
|
||||
@@ -37,6 +39,30 @@ class ThemeHandlerTest extends ItopTestCase
|
||||
$this->recurse_copy(APPROOT."/test/application/theme-handler/expected/css", $this->tmpDir."/branding/css");
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
parent::tearDown();
|
||||
foreach ($this->aDirsToCleanup as $dir)
|
||||
{
|
||||
$this->rrmdir($dir);
|
||||
}
|
||||
}
|
||||
|
||||
function rrmdir($dir) {
|
||||
if (is_dir($dir)) {
|
||||
$objects = scandir($dir);
|
||||
foreach ($objects as $object) {
|
||||
if ($object != "." && $object != "..") {
|
||||
if (is_dir($dir."/".$object))
|
||||
$this->rrmdir($dir."/".$object);
|
||||
else
|
||||
unlink($dir."/".$object);
|
||||
}
|
||||
}
|
||||
rmdir($dir);
|
||||
}
|
||||
}
|
||||
|
||||
function tmpdir() {
|
||||
$tmpfile=tempnam(sys_get_temp_dir(),'');
|
||||
if (file_exists($tmpfile))
|
||||
@@ -68,11 +94,106 @@ class ThemeHandlerTest extends ItopTestCase
|
||||
closedir($dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test used to be notified by CI when precompiled styles are not up to date anymore in code repository.
|
||||
* @param $xmlDataCusto
|
||||
* @dataProvider providePrecompiledStyleSheets
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function testValidatePrecompiledStyles($xmlDataCusto)
|
||||
{
|
||||
echo "=== datamodel custo: $xmlDataCusto\n";
|
||||
$oDom = new MFDocument();
|
||||
$oDom->load($xmlDataCusto);
|
||||
/**DOMNodeList **/$oThemeNodes=$oDom->GetNodes("/itop_design/branding/themes/theme");
|
||||
$this->assertNotNull($oThemeNodes);
|
||||
|
||||
// Parsing themes from DM
|
||||
foreach($oThemeNodes as $oTheme)
|
||||
{
|
||||
$sPrecompiledStylesheet = $oTheme->GetChildText('precompiled_stylesheet', '');
|
||||
if (empty($sPrecompiledStylesheet))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$sThemeId = $oTheme->getAttribute('id');
|
||||
|
||||
echo "=== theme: $sThemeId ===\n";
|
||||
$precompiledSig= ThemeHandler::GetSignature(dirname(__FILE__)."/../../datamodels/2.x/".$sPrecompiledStylesheet);
|
||||
echo " precompiled signature: $precompiledSig\n";
|
||||
$this->assertFalse(empty($precompiledSig), "Signature in precompiled theme '".$sThemeId."' is not retrievable (cf precompiledsheet $sPrecompiledStylesheet / datamodel $xmlDataCusto)");
|
||||
|
||||
$aThemeParameters = array(
|
||||
'variables' => array(),
|
||||
'imports' => array(),
|
||||
'stylesheets' => array(),
|
||||
'precompiled_stylesheet' => '',
|
||||
);
|
||||
|
||||
$aThemeParameters['precompiled_stylesheet'] = $sPrecompiledStylesheet;
|
||||
/** @var \DOMNodeList $oVariables */
|
||||
$oVariables = $oTheme->GetNodes('variables/variable');
|
||||
foreach($oVariables as $oVariable)
|
||||
{
|
||||
$sVariableId = $oVariable->getAttribute('id');
|
||||
$aThemeParameters['variables'][$sVariableId] = $oVariable->GetText();
|
||||
}
|
||||
|
||||
/** @var \DOMNodeList $oImports */
|
||||
$oImports = $oTheme->GetNodes('imports/import');
|
||||
foreach($oImports as $oImport)
|
||||
{
|
||||
$sImportId = $oImport->getAttribute('id');
|
||||
$aThemeParameters['imports'][$sImportId] = $oImport->GetText();
|
||||
}
|
||||
|
||||
/** @var \DOMNodeList $oStylesheets */
|
||||
$oStylesheets = $oTheme->GetNodes('stylesheets/stylesheet');
|
||||
foreach($oStylesheets as $oStylesheet)
|
||||
{
|
||||
$sStylesheetId = $oStylesheet->getAttribute('id');
|
||||
$aThemeParameters['stylesheets'][$sStylesheetId] = $oStylesheet->GetText();
|
||||
}
|
||||
$sThemeFolderPath = APPROOT.'env-production/branding/themes/'.$sThemeId.'/test';
|
||||
if (!is_dir($sThemeFolderPath))
|
||||
{
|
||||
mkdir($sThemeFolderPath);
|
||||
}
|
||||
$compiled_json_sig = ThemeHandler::ComputeSignature($aThemeParameters, array(APPROOT.'datamodels'), $sThemeFolderPath);
|
||||
echo " current signature: $compiled_json_sig\n";
|
||||
rmdir($sThemeFolderPath);
|
||||
$this->assertEquals($precompiledSig, $compiled_json_sig, "Precompiled signature does not match currently compiled one on theme '".$sThemeId."' (cf precompiledsheet $sPrecompiledStylesheet / datamodel $xmlDataCusto)");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function providePrecompiledStyleSheets()
|
||||
{
|
||||
$datamodelfiles=glob(dirname(__FILE__)."/../../datamodels/2.x/**/datamodel*.xml");
|
||||
$test_set = array();
|
||||
|
||||
foreach ($datamodelfiles as $datamodelfile)
|
||||
{
|
||||
if (is_file($datamodelfile) &&
|
||||
$datamodelfile=="/var/www/html/iTop/test/application/../../datamodels/2.x/itop-config-mgmt/datamodel.itop-config-mgmt.xml")
|
||||
{
|
||||
$content=file_get_contents($datamodelfile);
|
||||
if (strpos($content, "precompiled_stylesheet")!==false)
|
||||
{
|
||||
$test_set[$datamodelfile]=array($datamodelfile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $test_set;
|
||||
}
|
||||
|
||||
public function testGetSignature()
|
||||
{
|
||||
$sig = ThemeHandler::GetSignature(APPROOT.'test/application/theme-handler/expected/themes/basque-red/main.css');
|
||||
$expect_sig=<<<JSON
|
||||
{"variables":"37c31105548fce44fecca5cb34e455c9","stylesheets":{"css-variables":"934888ebb4991d4c76555be6b6d1d5cc","jqueryui":"78cfafc3524dac98e61fc2460918d4e5","main":"52d8a7c5530ceb3a4d777364fa4e1eea"},"imports":[]}
|
||||
{"variables":"37c31105548fce44fecca5cb34e455c9","stylesheets":{"css-variables":"934888ebb4991d4c76555be6b6d1d5cc","jqueryui":"78cfafc3524dac98e61fc2460918d4e5","main":"52d8a7c5530ceb3a4d777364fa4e1eea"},"imports":[],"images":[]}
|
||||
JSON;
|
||||
|
||||
$this->assertEquals($expect_sig,$sig);
|
||||
@@ -182,9 +303,31 @@ JSON;
|
||||
*/
|
||||
public function testCompileThemes($ThemeParametersJson, $CompileCSSFromSASSCount, $missingFile=0, $filesTouchedRecently=0, $fileMd5sumModified=0, $fileToTest=null, $expected_maincss_path=null, $bSetup=true)
|
||||
{
|
||||
$fileToTest=$this->tmpDir.'/'.$fileToTest;
|
||||
if (is_file($this->tmpDir.'/'.$fileToTest))
|
||||
{
|
||||
$fileToTest=$this->tmpDir.'/'.$fileToTest;
|
||||
}
|
||||
else
|
||||
{
|
||||
$fileToTest=APPROOT.'/'.$fileToTest;
|
||||
}
|
||||
|
||||
$cssPath = $this->tmpDir . '/branding/themes/basque-red/main.css';
|
||||
copy(APPROOT . 'test/application/theme-handler/expected/themes/basque-red/main.css', $cssPath);
|
||||
copy(APPROOT."test/application/theme-handler/expected/themes/basque-red/main_testcompilethemes.css", $cssPath);
|
||||
|
||||
$sAbsoluteImagePath = APPROOT .'test/application/theme-handler/copied/testimages/';
|
||||
@mkdir(APPROOT .'test/application/theme-handler/copied/');
|
||||
@mkdir($sAbsoluteImagePath);
|
||||
$aDirsToCleanup[] = $sAbsoluteImagePath;
|
||||
$this->recurse_copy(APPROOT .'test/application/theme-handler/expected/testimages/', $sAbsoluteImagePath);
|
||||
|
||||
//change approot-relative in css-variable to use absolute path
|
||||
$sCssVarPath = $this->tmpDir."/branding/css/css-variables.scss";
|
||||
$sCssVariableContent = file_get_contents($sCssVarPath);
|
||||
$sLine = '$approot-relative: "' . $sAbsoluteImagePath . '" !default;';
|
||||
|
||||
$sCssVariableContent=preg_replace("/\\\$approot-relative: \"(.*)\"/", $sLine, $sCssVariableContent);
|
||||
file_put_contents($sCssVarPath, $sCssVariableContent);
|
||||
|
||||
if ($missingFile==1)
|
||||
{
|
||||
@@ -223,14 +366,16 @@ JSON;
|
||||
$modifiedVariableThemeParameterJson='{"variables":{"brand-primary1":"#C53030","hover-background-color":"#F6F6F6","icons-filter":"grayscale(1)","search-form-container-bg-color":"#4A5568"},"imports":{"css-variables":"..\/css\/css-variables.scss"},"stylesheets":{"jqueryui":"..\/css\/ui-lightness\/jqueryui.scss","main":"..\/css\/light-grey.scss"}}';
|
||||
$initialThemeParamJson='{"variables":{"brand-primary":"#C53030","hover-background-color":"#F6F6F6","icons-filter":"grayscale(1)","search-form-container-bg-color":"#4A5568"},"imports":{"css-variables":"..\/css\/css-variables.scss"},"stylesheets":{"jqueryui":"..\/css\/ui-lightness\/jqueryui.scss","main":"..\/css\/light-grey.scss"}}';
|
||||
$import_file_path = '/branding/css/css-variables.scss';
|
||||
$importmodified_maincss="test/application/theme-handler/expected/themes/basque-red/main_importmodified.css";
|
||||
$varchanged_maincss="test/application/theme-handler/expected/themes/basque-red/main_varchanged.css";
|
||||
$stylesheet_maincss="test/application/theme-handler/expected/themes/basque-red/main_stylesheet.css";
|
||||
$image_maincss="test/application/theme-handler/expected/themes/basque-red/main_imagemodified.css";
|
||||
$importmodified_maincss="test/application/theme-handler/expected/themes/basque-red/main_importmodified.css";
|
||||
$stylesheet_file_path = '/branding/css/light-grey.scss';
|
||||
$image_file_path = 'test/application/theme-handler/copied/testimages/images/green-header.gif';
|
||||
return array(
|
||||
"setup context: variables list modified without any file touched" => array($modifiedVariableThemeParameterJson, 1,0,0,0,$import_file_path, $varchanged_maincss),
|
||||
"setup context: variables list modified with files touched" => array($modifiedVariableThemeParameterJson, 1,0,1,0,$import_file_path, $varchanged_maincss, false),
|
||||
"itop page/theme loading; variables list modified sans touch de fichier" => array($modifiedVariableThemeParameterJson, 0,0,0,0,$import_file_path, $varchanged_maincss, false),
|
||||
"itop page/theme loading; variables list modified without any file touched" => array($modifiedVariableThemeParameterJson, 0,0,0,0,$import_file_path, $varchanged_maincss, false),
|
||||
//imports
|
||||
"import file missing" => array($initialThemeParamJson, 0, 1, 0, 0, $import_file_path),
|
||||
"import file touched" => array($initialThemeParamJson, 0, 0, 1, 0, $import_file_path),
|
||||
@@ -238,96 +383,81 @@ JSON;
|
||||
//stylesheets
|
||||
"stylesheets file missing" => array($initialThemeParamJson, 0, 1, 0, 0, $stylesheet_file_path),
|
||||
"stylesheets file touched" => array($initialThemeParamJson, 0, 0, 1, 0, $stylesheet_file_path),
|
||||
"stylesheets file modified" => array($initialThemeParamJson, 1, 0, 0, 1, $stylesheet_file_path, $stylesheet_maincss)
|
||||
"stylesheets file modified" => array($initialThemeParamJson, 1, 0, 0, 1, $stylesheet_file_path, $stylesheet_maincss),
|
||||
//images
|
||||
"image file missing" => array($initialThemeParamJson, 0, 1, 0, 0, $image_file_path),
|
||||
"image file touched" => array($initialThemeParamJson, 0, 0, 1, 0, $image_file_path),
|
||||
"image file modified" => array($initialThemeParamJson, 1, 0, 0, 1, $image_file_path, $image_maincss),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $xmlDataCusto
|
||||
* @dataProvider providePrecompiledStyleSheets
|
||||
* @throws \Exception
|
||||
* @param $sScssFile
|
||||
*
|
||||
* @dataProvider GetAllUrlFromScssProvider
|
||||
*/
|
||||
public function testValidatePrecompiledStyles($xmlDataCusto)
|
||||
public function testGetAllUrlFromScss($sScssFile)
|
||||
{
|
||||
echo "=== datamodel custo: $xmlDataCusto\n";
|
||||
$oDom = new MFDocument();
|
||||
$oDom->load($xmlDataCusto);
|
||||
/**DOMNodeList **/$oThemeNodes=$oDom->GetNodes("/itop_design/branding/themes/theme");
|
||||
$this->assertNotNull($oThemeNodes);
|
||||
|
||||
// Parsing themes from DM
|
||||
foreach($oThemeNodes as $oTheme)
|
||||
{
|
||||
$sPrecompiledStylesheet = $oTheme->GetChildText('precompiled_stylesheet', '');
|
||||
if (empty($sPrecompiledStylesheet))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$sThemeId = $oTheme->getAttribute('id');
|
||||
|
||||
echo "=== theme: $sThemeId ===\n";
|
||||
$precompiledSig= ThemeHandler::GetSignature(dirname(__FILE__)."/../../datamodels/2.x/".$sPrecompiledStylesheet);
|
||||
echo " precompiled signature: $precompiledSig\n";
|
||||
$this->assertFalse(empty($precompiledSig), "Signature in precompiled theme '".$sThemeId."' is not retrievable (cf precompiledsheet $sPrecompiledStylesheet / datamodel $xmlDataCusto)");
|
||||
|
||||
$aThemeParameters = array(
|
||||
'variables' => array(),
|
||||
'imports' => array(),
|
||||
'stylesheets' => array(),
|
||||
'precompiled_stylesheet' => '',
|
||||
);
|
||||
|
||||
$aThemeParameters['precompiled_stylesheet'] = $sPrecompiledStylesheet;
|
||||
/** @var \DOMNodeList $oVariables */
|
||||
$oVariables = $oTheme->GetNodes('variables/variable');
|
||||
foreach($oVariables as $oVariable)
|
||||
{
|
||||
$sVariableId = $oVariable->getAttribute('id');
|
||||
$aThemeParameters['variables'][$sVariableId] = $oVariable->GetText();
|
||||
}
|
||||
|
||||
/** @var \DOMNodeList $oImports */
|
||||
$oImports = $oTheme->GetNodes('imports/import');
|
||||
foreach($oImports as $oImport)
|
||||
{
|
||||
$sImportId = $oImport->getAttribute('id');
|
||||
$aThemeParameters['imports'][$sImportId] = $oImport->GetText();
|
||||
}
|
||||
|
||||
/** @var \DOMNodeList $oStylesheets */
|
||||
$oStylesheets = $oTheme->GetNodes('stylesheets/stylesheet');
|
||||
foreach($oStylesheets as $oStylesheet)
|
||||
{
|
||||
$sStylesheetId = $oStylesheet->getAttribute('id');
|
||||
$aThemeParameters['stylesheets'][$sStylesheetId] = $oStylesheet->GetText();
|
||||
}
|
||||
$compiled_json_sig = ThemeHandler::ComputeSignature($aThemeParameters, array(APPROOT.'datamodels'));
|
||||
echo " current signature: $compiled_json_sig\n";
|
||||
$this->assertEquals($precompiledSig, $compiled_json_sig, "Precompiled signature does not match currently compiled one on theme '".$sThemeId."' (cf precompiledsheet $sPrecompiledStylesheet / datamodel $xmlDataCusto)");
|
||||
}
|
||||
|
||||
$aIncludedUrls = ThemeHandler::GetAllUrlFromScss(array('attr' => "123"),APPROOT.$sScssFile);
|
||||
$this->assertEquals(array('version1'), array_values($aIncludedUrls['aMissingVariables']));
|
||||
$this->assertEquals(array("approot-relative" => "../../../../../", "version" => "aaa", "attr"=>"123"),
|
||||
$aIncludedUrls['aFoundVariables']);
|
||||
$expected_array = array(
|
||||
'css/ui-lightness/images/tutu.jpg',
|
||||
"css/ui-lightness/images/tata.jpeg",
|
||||
'abc/../../../../../css/ui-lightness/images/toutou.png?v=aaa',
|
||||
"../../../../../css/ui-lightness/images/toto.png?v=aaa",
|
||||
"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7?v=aaa",
|
||||
"css/ui-lightness/images/tete.jpeg?g=123"
|
||||
);
|
||||
$aIncludedUrls['aCompleteUrls'];
|
||||
$this->assertEquals($expected_array, array_values($aIncludedUrls['aCompleteUrls']));
|
||||
$this->assertEquals(array('$approot-relative + \'css/ui-lightness/images/titi.gif?v=\' + $version1'), array_values($aIncludedUrls['aToCompleteUrls']));
|
||||
}
|
||||
|
||||
public function providePrecompiledStyleSheets()
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function GetAllUrlFromScssProvider()
|
||||
{
|
||||
$datamodelfiles=glob(dirname(__FILE__)."/../../datamodels/2.x/**/datamodel*.xml");
|
||||
$test_set = array();
|
||||
|
||||
foreach ($datamodelfiles as $datamodelfile)
|
||||
{
|
||||
if (is_file($datamodelfile) &&
|
||||
$datamodelfile=="/var/www/html/iTop/test/application/../../datamodels/2.x/itop-config-mgmt/datamodel.itop-config-mgmt.xml")
|
||||
{
|
||||
$content=file_get_contents($datamodelfile);
|
||||
if (strpos($content, "precompiled_stylesheet")!==false)
|
||||
{
|
||||
$test_set[$datamodelfile]=array($datamodelfile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $test_set;
|
||||
return array('test-getimages.scss' => array('test/application/theme-handler/getimages/test-getimages.scss'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sUrlTemplate
|
||||
* @param $aFoundVariables
|
||||
* @param $sExpectedUrl
|
||||
*
|
||||
* @dataProvider ResolveUrlProvider
|
||||
*/
|
||||
public function testResolveUrl($sUrlTemplate, $aFoundVariables, $sExpectedUrl)
|
||||
{
|
||||
$this->assertEquals($sExpectedUrl, ThemeHandler::ResolveUrl($sUrlTemplate, $aFoundVariables));
|
||||
}
|
||||
|
||||
public function ResolveUrlProvider()
|
||||
{
|
||||
return array(
|
||||
'XXX + $key1 UNresolved' => array("abc/'+ \$key1", array('key'=>'123'), false),
|
||||
'$key1 + XXX UNresolved' => array("\$key1 + abs", array('key'=>'123'), false),
|
||||
'XXX + $key UNresolved' => array("abc/'+ \$unknownkey", array('key'=>'123'), false),
|
||||
'XXX + $key resolved' => array("abc/'+ \$key", array('key'=>'123'), "abc/123"),
|
||||
'XXX + $key1 resolved' => array("abc/'+ \$key1", array('key1'=>'123'), "abc/123"),
|
||||
'$key + XXX resolved' => array("\$key + \"/abc", array('key'=>'123'), "123/abc"),
|
||||
'XXX + $key + YYY resolved' => array("abc/'+ \$key + '/def", array('key'=>'123'), "abc/123/def"),
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetIncludedImages()
|
||||
{
|
||||
$aStylesheetFile=glob($this->tmpDir."/branding/css/*.scss");
|
||||
$aStylesheetFile[]=$this->tmpDir."/branding/css/ui-lightness/jqueryui.scss";
|
||||
$expectJsonFilePath = APPROOT.'test/application/theme-handler/expected/themes/basque-red/theme-parameters.json';
|
||||
$expectedThemeParamJson = file_get_contents($expectJsonFilePath);
|
||||
$aThemeParametersVariables = json_decode($expectedThemeParamJson, true);
|
||||
$aIncludedImages = ThemeHandler::GetIncludedImages($aThemeParametersVariables['variables'], $aStylesheetFile, "RELATIVEPATH");
|
||||
|
||||
$aExpectedImages = json_decode(file_get_contents(APPROOT.'test/application/theme-handler/getimages/expected-getimages.json'), true);
|
||||
$this->assertEquals($aExpectedImages, $aIncludedImages);
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 418 B |
|
After Width: | Height: | Size: 312 B |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 61 B |
|
After Width: | Height: | Size: 240 B |
BIN
test/application/theme-handler/copied/testimages/images/bg.gif
Normal file
|
After Width: | Height: | Size: 64 B |
|
After Width: | Height: | Size: 324 B |
|
After Width: | Height: | Size: 676 B |
|
After Width: | Height: | Size: 842 B |
BIN
test/application/theme-handler/copied/testimages/images/desc.gif
Normal file
|
After Width: | Height: | Size: 54 B |
|
After Width: | Height: | Size: 543 B |
|
After Width: | Height: | Size: 274 B |
|
After Width: | Height: | Size: 237 B |
|
After Width: | Height: | Size: 355 B |
|
After Width: | Height: | Size: 327 B |
|
After Width: | Height: | Size: 1011 B |
|
After Width: | Height: | Size: 56 B |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 850 B |
|
After Width: | Height: | Size: 139 B |
BIN
test/application/theme-handler/copied/testimages/images/ok.png
Normal file
|
After Width: | Height: | Size: 643 B |
|
After Width: | Height: | Size: 122 B |
BIN
test/application/theme-handler/copied/testimages/images/plus.gif
Normal file
|
After Width: | Height: | Size: 142 B |
|
After Width: | Height: | Size: 122 B |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 85 B |
|
After Width: | Height: | Size: 781 B |
|
After Width: | Height: | Size: 89 B |
|
After Width: | Height: | Size: 787 B |
|
After Width: | Height: | Size: 65 B |
|
After Width: | Height: | Size: 750 B |
|
After Width: | Height: | Size: 418 B |
|
After Width: | Height: | Size: 312 B |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 61 B |
|
After Width: | Height: | Size: 240 B |
BIN
test/application/theme-handler/expected/testimages/images/bg.gif
Normal file
|
After Width: | Height: | Size: 64 B |
|
After Width: | Height: | Size: 324 B |
|
After Width: | Height: | Size: 676 B |
|
After Width: | Height: | Size: 842 B |
|
After Width: | Height: | Size: 54 B |
|
After Width: | Height: | Size: 543 B |
|
After Width: | Height: | Size: 274 B |
|
After Width: | Height: | Size: 237 B |
|
After Width: | Height: | Size: 355 B |
|
After Width: | Height: | Size: 327 B |
|
After Width: | Height: | Size: 1011 B |
|
After Width: | Height: | Size: 120 B |
|
After Width: | Height: | Size: 56 B |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 850 B |
|
After Width: | Height: | Size: 139 B |
BIN
test/application/theme-handler/expected/testimages/images/ok.png
Normal file
|
After Width: | Height: | Size: 643 B |
|
After Width: | Height: | Size: 122 B |
|
After Width: | Height: | Size: 142 B |
|
After Width: | Height: | Size: 122 B |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 85 B |
|
After Width: | Height: | Size: 781 B |
|
After Width: | Height: | Size: 89 B |
|
After Width: | Height: | Size: 787 B |
|
After Width: | Height: | Size: 65 B |
|
After Width: | Height: | Size: 750 B |
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
=== SIGNATURE BEGIN ===
|
||||
{"variables":"37c31105548fce44fecca5cb34e455c9","stylesheets":{"css-variables":"934888ebb4991d4c76555be6b6d1d5cc","jqueryui":"78cfafc3524dac98e61fc2460918d4e5","main":"52d8a7c5530ceb3a4d777364fa4e1eea"},"imports":[]}
|
||||
{"variables":"37c31105548fce44fecca5cb34e455c9","stylesheets":{"css-variables":"934888ebb4991d4c76555be6b6d1d5cc","jqueryui":"78cfafc3524dac98e61fc2460918d4e5","main":"52d8a7c5530ceb3a4d777364fa4e1eea"},"imports":[],"images":[]}
|
||||
=== SIGNATURE END ===
|
||||
*/
|
||||
====CSSCOMPILEDCONTENT====
|
||||
@@ -0,0 +1,42 @@
|
||||
[
|
||||
"RELATIVEPATH/../../../../../css/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png",
|
||||
"RELATIVEPATH/../../../../../css/ui-lightness/images/ui-icons_ffffff_256x240.png",
|
||||
"RELATIVEPATH/../../../../../images/actions_right.png",
|
||||
"RELATIVEPATH/../../../../../images/ac-background.gif",
|
||||
"RELATIVEPATH/../../../../../images/green-square.gif",
|
||||
"RELATIVEPATH/../../../../../images/tv-item.gif",
|
||||
"RELATIVEPATH/../../../../../images/tv-collapsable.gif",
|
||||
"RELATIVEPATH/../../../../../images/tv-expandable.gif",
|
||||
"RELATIVEPATH/../../../../../images/tv-item-last.gif",
|
||||
"RELATIVEPATH/../../../../../images/tv-collapsable-last.gif",
|
||||
"RELATIVEPATH/../../../../../images/tv-expandable-last.gif",
|
||||
"RELATIVEPATH/../../../../../images/red-header.gif",
|
||||
"RELATIVEPATH/../../../../../images/green-header.gif",
|
||||
"RELATIVEPATH/../../../../../images/orange-header.gif",
|
||||
"RELATIVEPATH/../../../../../images/calendar.png",
|
||||
"RELATIVEPATH/../../../../../images/truncated.png",
|
||||
"RELATIVEPATH/../../../../../images/itop-logo-2.png",
|
||||
"RELATIVEPATH/../../../../../images/splitter-bkg.png",
|
||||
"RELATIVEPATH/../../../../../images/plus.gif",
|
||||
"RELATIVEPATH/../../../../../images/minus.gif",
|
||||
"RELATIVEPATH/../../../../../images/full-screen.png",
|
||||
"RELATIVEPATH/../../../../../images/indicator.gif",
|
||||
"RELATIVEPATH/../../../../../images/delete.png",
|
||||
"RELATIVEPATH/../../../../../images/bg.gif",
|
||||
"RELATIVEPATH/../../../../../images/desc.gif",
|
||||
"RELATIVEPATH/../../../../../images/asc.gif",
|
||||
"RELATIVEPATH/../../../../../images/info-mini.png",
|
||||
"RELATIVEPATH/../../../../../images/ok.png",
|
||||
"RELATIVEPATH/../../../../../images/error.png",
|
||||
"RELATIVEPATH/../../../../../images/eye-open-555.png",
|
||||
"RELATIVEPATH/../../../../../images/eye-closed-555.png",
|
||||
"RELATIVEPATH/../../../../../images/eye-open-fff.png",
|
||||
"RELATIVEPATH/../../../../../images/eye-closed-fff.png",
|
||||
"RELATIVEPATH/../../../../../css/ui-lightness/images/ui-icons_222222_256x240.png",
|
||||
"RELATIVEPATH/../../../../../images/breadcrumb-separator.png",
|
||||
"RELATIVEPATH/../../../../../css/ui-lightness/images/ui-icons_E87C1E_256x240.png",
|
||||
"RELATIVEPATH/../../../../../css/ui-lightness/images/ui-icons_1c94c4_256x240.png",
|
||||
"RELATIVEPATH/../../../../../css/ui-lightness/images/ui-icons_F26522_256x240.png",
|
||||
"RELATIVEPATH/../../../../../css/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png",
|
||||
"RELATIVEPATH/../../../../../css/ui-lightness/images/ui-icons_ffd27a_256x240.png"
|
||||
]
|
||||
@@ -0,0 +1,9 @@
|
||||
$approot-relative: "../../../../../"
|
||||
$version:"aaa"
|
||||
background-image: url('abc/'+ $approot-relative + "css/ui-lightness/images/toutou.png?v=" + $version);
|
||||
background-image: url($approot-relative + "css/ui-lightness/images/toto.png?v=" + $version);
|
||||
background: #666666 url($approot-relative + 'css/ui-lightness/images/titi.gif?v=' + $version1) 50% 50% repeat;
|
||||
list-style-image: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7?v=" + $version);
|
||||
background-image: url('css/ui-lightness/images/tutu.jpg');
|
||||
background-image: url("css/ui-lightness/images/tata.jpeg");
|
||||
background-image: url("css/ui-lightness/images/tete.jpeg?g=" + $attr);
|
||||