N°2996 - Remove iTop version from css-variable.scss

Include images in precompilation check
This commit is contained in:
odain
2020-06-04 08:55:28 +02:00
parent 2d8b888a18
commit 307713d326
93 changed files with 819 additions and 200 deletions

View File

@@ -25,7 +25,6 @@ require_once (__DIR__.DIRECTORY_SEPARATOR.'update.classes.inc.php');
/** @var \FileVersionUpdater[] $aFilesUpdaters */
$aFilesUpdaters = array(
new iTopVersionFileUpdater(),
new CssVariablesFileUpdater(),
new DatamodelsModulesFiles(),
);

View File

@@ -89,26 +89,6 @@ class iTopVersionFileUpdater extends AbstractSingleFileVersionUpdater
}
}
class CssVariablesFileUpdater extends AbstractSingleFileVersionUpdater
{
public function __construct()
{
parent::__construct('css/css-variables.scss');
}
/**
* @inheritDoc
*/
public function UpdateFileContent($sVersionLabel, $sFileContent, $sFileFullPath)
{
return preg_replace(
'/(\$version: "v)[^"]*(";)/',
'${1}'.$sVersionLabel.'${2}',
$sFileContent
);
}
}
abstract class AbstractGlobFileVersionUpdater extends FileVersionUpdater
{
protected $sGlobPattern;

View File

@@ -25,6 +25,8 @@
*/
class ThemeHandler
{
const IMAGE_EXTENSIONS = array('png', 'gif', 'jpg', 'jpeg');
private static $oCompileCSSService;
/**
* Return default theme name and parameters
@@ -173,12 +175,15 @@ class ThemeHandler
$iStyleLastModified = 0;
clearstatcache();
// Loading files to import and stylesheet to compile, also getting most recent modification time on overall files
$aStylesheetFiles = array();
foreach ($aThemeParameters['imports'] as $sImport)
{
$sTmpThemeScssContent .= '@import "'.$sImport.'";'."\n";
$sFile = static::FindStylesheetFile($sImport, $aImportsPaths);
$iImportLastModified = @filemtime($sFile);
$aStylesheetFiles[] = $sFile;
$iStyleLastModified = $iStyleLastModified < $iImportLastModified ? $iImportLastModified : $iStyleLastModified;
}
foreach ($aThemeParameters['stylesheets'] as $sStylesheet)
@@ -187,9 +192,24 @@ class ThemeHandler
$sFile = static::FindStylesheetFile($sStylesheet, $aImportsPaths);
$iStylesheetLastModified = @filemtime($sFile);
$aStylesheetFiles[] = $sFile;
$iStyleLastModified = $iStyleLastModified < $iStylesheetLastModified ? $iStylesheetLastModified : $iStyleLastModified;
}
$aIncludedImages=static::GetIncludedImages($aThemeParameters['variables'], $aStylesheetFiles, $sThemeFolderPath);
foreach ($aIncludedImages as $sImage)
{
if (!is_file($sImage))
{
IssueLog::Warning("Cannot find $sImage during SCSS $sThemeId precompilation");
}
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 +228,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)
{
@@ -232,11 +252,11 @@ $sActualSignature
*/
CSS;
if (!self::$oCompileCSSService)
if (!static::$oCompileCSSService)
{
self::$oCompileCSSService = new CompileCSSService();
static::$oCompileCSSService = new CompileCSSService();
}
$sTmpThemeCssContent = self::$oCompileCSSService->CompileCSSFromSASS($sTmpThemeScssContent, $aImportsPaths,
$sTmpThemeCssContent = static::$oCompileCSSService->CompileCSSFromSASS($sTmpThemeScssContent, $aImportsPaths,
$aThemeParameters['variables']);
file_put_contents($sThemeCssPath, $sSignatureComment.$sTmpThemeCssContent);
}
@@ -251,16 +271,18 @@ CSS;
*
* @param string[] $aThemeParameters
* @param string[] $aImportsPaths
* @param string[] $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 +295,303 @@ 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);
}
/**
* Search for images referenced in stylesheet files
* @param array $aThemeParametersVariables
* @param array $aStylesheetFiles
* @param string $sThemeFolderPath : used as relative paths to find css images
*
* @return array
* @since 2.8.0
*/
public static function GetIncludedImages($aThemeParametersVariables, $aStylesheetFiles, $sThemeFolderPath)
{
$aCompleteUrls = array();
$aToCompleteUrls = array();
$aMissingVariables = array();
$aFoundVariables = array('version'=>'');
$aMap = array(
'aCompleteUrls' => $aCompleteUrls,
'aToCompleteUrls' => $aToCompleteUrls,
'aMissingVariables' => $aMissingVariables,
'aFoundVariables' => $aFoundVariables,
);
foreach ($aStylesheetFiles as $sStylesheetFile)
{
$aRes = static::GetAllUrlFromScss($aThemeParametersVariables, $sStylesheetFile);
/** @var array $aVal */
foreach($aMap as $key => $aVal)
{
if (array_key_exists($key, $aMap))
{
$aMap[$key] = array_merge($aVal, $aRes[$key]);
}
}
}
$aMap = static::ResolveUncompleteUrlsFromScss($aMap, $aThemeParametersVariables, $aStylesheetFiles);
$aImages = array();
foreach ($aMap ['aCompleteUrls'] as $sUrl)
{
$sImg = $sUrl;
if (preg_match("/(.*)\?/", $sUrl, $aMatches))
{
$sImg=$aMatches[1];
}
if (static::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) = static::FindMissingVariables($aThemeParametersVariables, $aMissingVariables, $aFoundVariables, $sContent, true);
list($aToCompleteUrls, $aCompleteUrls) = static::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
* @param bool $bForceEmptyValueWhenNotFound
*
* @return array
*/
public static function FindMissingVariables($aThemeParametersVariables, $aMissingVariables, $aFoundVariables, $sContent, $bForceEmptyValueWhenNotFound=false)
{
$aNewMissingVars = array();
if (!empty($aMissingVariables))
{
foreach ($aMissingVariables as $var)
{
if (array_key_exists($var, $aThemeParametersVariables))
{
$aFoundVariables[$var] = $aThemeParametersVariables[$var];
}
else
{
if (preg_match_all("/\\\$$var\s*:\s*[\"']{0,1}(.*)[\"']{0,1};/", $sContent, $aValues))
{
$sValue = $aValues[1][0];
if (preg_match_all("/([^!]+)!/", $sValue, $aSubValues))
{
$sValue = trim($aSubValues[1][0], ' "\'');
}
if (strpos($sValue, '$') === false)
{
$aFoundVariables[$var] = $sValue;
}
else{
$aNewMissingVars[] = $var;
}
}
else
{
if ($bForceEmptyValueWhenNotFound)
{
$aFoundVariables[$var] = '';
}
else
{
$aNewMissingVars[] = $var;
}
}
}
}
}
return array($aNewMissingVars, $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 = static::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))
{
/** @var string $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) = static::FindMissingVariables($aThemeParametersVariables, $aMissingVariables, $aFoundVariables, $sContent);
list($aToCompleteUrls, $aCompleteUrls) = static::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*['\"]/";
$aReplacement[]=$aFoundVariableValue;
//$key + YY
$aPattern[]="/\\\$" . $aFoundVariable. "[\s\+]+\s*['\"]/";
$aReplacement[]=$aFoundVariableValue;
//XX + $key
$aPattern[]="/['\"]\s*[\+\s]+\\\$" . $aFoundVariable . "$/";
$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 (static::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
@@ -322,7 +638,7 @@ CSS;
* @throws Exception
* @return string
*/
private static function FindStylesheetFile($sFile, $aImportsPaths)
public static function FindStylesheetFile($sFile, $aImportsPaths)
{
foreach($aImportsPaths as $sPath)
{
@@ -337,7 +653,7 @@ CSS;
public static function mockCompileCSSService($oCompileCSSServiceMock)
{
self::$oCompileCSSService = $oCompileCSSServiceMock;
static::$oCompileCSSService = $oCompileCSSServiceMock;
}
}

View File

@@ -1858,17 +1858,13 @@ class utils
}
/**
* @deprecated
* @param string $sModuleName
* @return string|NULL compiled version of a given module, as it was seen by the compiler
*/
public static function GetCompiledModuleVersion($sModuleName)
{
$aModulesInfo = GetModulesInfo();
if (array_key_exists($sModuleName, $aModulesInfo))
{
return $aModulesInfo[$sModuleName]['version'];
}
return null;
return static::GetCacheBusterTimestamp();
}
/**

View File

@@ -15,9 +15,6 @@
*
* You should have received a copy of the GNU Affero General Public License
*/
// Beware the version number MUST be enclosed with quotes otherwise v2.3.0 becomes v2 0.3 .0
$version: "v2.7.1";
$approot-relative: "../../../../../" !default; // relative to env-***/branding/themes/***/main.css
// Base colors

View File

@@ -1,6 +1,6 @@
/*
=== SIGNATURE BEGIN ===
{"variables":"d751713988987e9331980363e24189ce","stylesheets":{"css-variables":"934888ebb4991d4c76555be6b6d1d5cc","jqueryui":"78cfafc3524dac98e61fc2460918d4e5","main":"52d8a7c5530ceb3a4d777364fa4e1eea"},"imports":[]}
{"variables":"d751713988987e9331980363e24189ce","stylesheets":{"css-variables":"1d4b4ae2a6fba3db101f8dd1cecab082","jqueryui":"78cfafc3524dac98e61fc2460918d4e5","main":"0dd837aeddc3f407c980e0b20f06dd4c"},"imports":[],"images":[]}
=== SIGNATURE END ===
*/
/*!

View File

@@ -1,6 +1,6 @@
/*
=== SIGNATURE BEGIN ===
{"variables":"8cfe86f2c55d8eff36d57eb4e83d89f1","stylesheets":{"css-variables":"934888ebb4991d4c76555be6b6d1d5cc","jqueryui":"78cfafc3524dac98e61fc2460918d4e5","main":"52d8a7c5530ceb3a4d777364fa4e1eea","environment-banner":"3de3ffb8232b9a649e912b570a64bf5d"},"imports":[]}
{"variables":"8cfe86f2c55d8eff36d57eb4e83d89f1","stylesheets":{"css-variables":"1d4b4ae2a6fba3db101f8dd1cecab082","jqueryui":"78cfafc3524dac98e61fc2460918d4e5","main":"0dd837aeddc3f407c980e0b20f06dd4c","environment-banner":"3de3ffb8232b9a649e912b570a64bf5d"},"imports":[],"images":[]}
=== SIGNATURE END ===
*/
/*!

View File

@@ -268,6 +268,7 @@ return array(
'HistoryBlock' => $baseDir . '/application/displayblock.class.inc.php',
'Html2Text\\Html2Text' => $baseDir . '/application/Html2Text.php',
'Html2Text\\Html2TextException' => $baseDir . '/application/Html2TextException.php',
'ILogFileNameBuilder' => $baseDir . '/core/log.class.inc.php',
'ITopArchiveTar' => $baseDir . '/core/tar-itop.class.inc.php',
'InlineImage' => $baseDir . '/core/inlineimage.class.inc.php',
'InlineImageGC' => $baseDir . '/core/inlineimage.class.inc.php',
@@ -286,7 +287,6 @@ return array(
'ListOqlExpression' => $baseDir . '/core/oql/oqlquery.class.inc.php',
'LogAPI' => $baseDir . '/core/log.class.inc.php',
'LogFileNameBuilderFactory' => $baseDir . '/core/log.class.inc.php',
'LogFileRotationProcess' => $baseDir . '/core/log.class.inc.php',
'LoginBlockExtension' => $baseDir . '/application/logintwig.class.inc.php',
'LoginTwigContext' => $baseDir . '/application/logintwig.class.inc.php',
'LoginTwigRenderer' => $baseDir . '/application/logintwig.class.inc.php',
@@ -304,7 +304,6 @@ return array(
'ModuleDesign' => $baseDir . '/core/moduledesign.class.inc.php',
'ModuleHandlerAPI' => $baseDir . '/core/modulehandler.class.inc.php',
'ModuleHandlerApiInterface' => $baseDir . '/core/modulehandler.class.inc.php',
'MonthlyRotatingLogFileNameBuilder' => $baseDir . '/core/log.class.inc.php',
'MyHelpers' => $baseDir . '/core/MyHelpers.class.inc.php',
'MySQLException' => $baseDir . '/core/cmdbsource.class.inc.php',
'MySQLHasGoneAwayException' => $baseDir . '/core/cmdbsource.class.inc.php',
@@ -606,6 +605,9 @@ return array(
'Psr\\Log\\LoggerInterface' => $vendorDir . '/psr/log/Psr/Log/LoggerInterface.php',
'Psr\\Log\\LoggerTrait' => $vendorDir . '/psr/log/Psr/Log/LoggerTrait.php',
'Psr\\Log\\NullLogger' => $vendorDir . '/psr/log/Psr/Log/NullLogger.php',
'Psr\\Log\\Test\\DummyTest' => $vendorDir . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php',
'Psr\\Log\\Test\\LoggerInterfaceTest' => $vendorDir . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php',
'Psr\\Log\\Test\\TestLogger' => $vendorDir . '/psr/log/Psr/Log/Test/TestLogger.php',
'Psr\\SimpleCache\\CacheException' => $vendorDir . '/psr/simple-cache/src/CacheException.php',
'Psr\\SimpleCache\\CacheInterface' => $vendorDir . '/psr/simple-cache/src/CacheInterface.php',
'Psr\\SimpleCache\\InvalidArgumentException' => $vendorDir . '/psr/simple-cache/src/InvalidArgumentException.php',
@@ -844,6 +846,9 @@ return array(
'Symfony\\Bundle\\FrameworkBundle\\Templating\\TemplateNameParser' => $vendorDir . '/symfony/framework-bundle/Templating/TemplateNameParser.php',
'Symfony\\Bundle\\FrameworkBundle\\Templating\\TemplateReference' => $vendorDir . '/symfony/framework-bundle/Templating/TemplateReference.php',
'Symfony\\Bundle\\FrameworkBundle\\Templating\\TimedPhpEngine' => $vendorDir . '/symfony/framework-bundle/Templating/TimedPhpEngine.php',
'Symfony\\Bundle\\FrameworkBundle\\Test\\ForwardCompatTestTrait' => $vendorDir . '/symfony/framework-bundle/Test/ForwardCompatTestTrait.php',
'Symfony\\Bundle\\FrameworkBundle\\Test\\KernelTestCase' => $vendorDir . '/symfony/framework-bundle/Test/KernelTestCase.php',
'Symfony\\Bundle\\FrameworkBundle\\Test\\WebTestCase' => $vendorDir . '/symfony/framework-bundle/Test/WebTestCase.php',
'Symfony\\Bundle\\FrameworkBundle\\Translation\\PhpExtractor' => $vendorDir . '/symfony/framework-bundle/Translation/PhpExtractor.php',
'Symfony\\Bundle\\FrameworkBundle\\Translation\\PhpStringTokenParser' => $vendorDir . '/symfony/framework-bundle/Translation/PhpStringTokenParser.php',
'Symfony\\Bundle\\FrameworkBundle\\Translation\\TranslationLoader' => $vendorDir . '/symfony/framework-bundle/Translation/TranslationLoader.php',
@@ -1656,6 +1661,7 @@ return array(
'Symfony\\Component\\VarDumper\\Dumper\\DataDumperInterface' => $vendorDir . '/symfony/var-dumper/Dumper/DataDumperInterface.php',
'Symfony\\Component\\VarDumper\\Dumper\\HtmlDumper' => $vendorDir . '/symfony/var-dumper/Dumper/HtmlDumper.php',
'Symfony\\Component\\VarDumper\\Exception\\ThrowingCasterException' => $vendorDir . '/symfony/var-dumper/Exception/ThrowingCasterException.php',
'Symfony\\Component\\VarDumper\\Test\\VarDumperTestTrait' => $vendorDir . '/symfony/var-dumper/Test/VarDumperTestTrait.php',
'Symfony\\Component\\VarDumper\\VarDumper' => $vendorDir . '/symfony/var-dumper/VarDumper.php',
'Symfony\\Component\\Yaml\\Command\\LintCommand' => $vendorDir . '/symfony/yaml/Command/LintCommand.php',
'Symfony\\Component\\Yaml\\Dumper' => $vendorDir . '/symfony/yaml/Dumper.php',
@@ -1859,6 +1865,8 @@ return array(
'Twig\\Source' => $vendorDir . '/twig/twig/src/Source.php',
'Twig\\Template' => $vendorDir . '/twig/twig/src/Template.php',
'Twig\\TemplateWrapper' => $vendorDir . '/twig/twig/src/TemplateWrapper.php',
'Twig\\Test\\IntegrationTestCase' => $vendorDir . '/twig/twig/src/Test/IntegrationTestCase.php',
'Twig\\Test\\NodeTestCase' => $vendorDir . '/twig/twig/src/Test/NodeTestCase.php',
'Twig\\Token' => $vendorDir . '/twig/twig/src/Token.php',
'Twig\\TokenParser\\AbstractTokenParser' => $vendorDir . '/twig/twig/src/TokenParser/AbstractTokenParser.php',
'Twig\\TokenParser\\ApplyTokenParser' => $vendorDir . '/twig/twig/src/TokenParser/ApplyTokenParser.php',
@@ -2056,6 +2064,11 @@ return array(
'Twig_Test' => $vendorDir . '/twig/twig/lib/Twig/Test.php',
'Twig_TestCallableInterface' => $vendorDir . '/twig/twig/lib/Twig/TestCallableInterface.php',
'Twig_TestInterface' => $vendorDir . '/twig/twig/lib/Twig/TestInterface.php',
'Twig_Test_Function' => $vendorDir . '/twig/twig/lib/Twig/Test/Function.php',
'Twig_Test_IntegrationTestCase' => $vendorDir . '/twig/twig/lib/Twig/Test/IntegrationTestCase.php',
'Twig_Test_Method' => $vendorDir . '/twig/twig/lib/Twig/Test/Method.php',
'Twig_Test_Node' => $vendorDir . '/twig/twig/lib/Twig/Test/Node.php',
'Twig_Test_NodeTestCase' => $vendorDir . '/twig/twig/lib/Twig/Test/NodeTestCase.php',
'Twig_Token' => $vendorDir . '/twig/twig/lib/Twig/Token.php',
'Twig_TokenParser' => $vendorDir . '/twig/twig/lib/Twig/TokenParser.php',
'Twig_TokenParserBroker' => $vendorDir . '/twig/twig/lib/Twig/TokenParserBroker.php',
@@ -2127,7 +2140,6 @@ return array(
'iDBObjectSetIterator' => $baseDir . '/core/dbobjectiterator.php',
'iDBObjectURLMaker' => $baseDir . '/application/applicationcontext.class.inc.php',
'iDisplay' => $baseDir . '/core/dbobject.class.php',
'iLogFileNameBuilder' => $baseDir . '/core/log.class.inc.php',
'iLoginExtension' => $baseDir . '/application/applicationextension.inc.php',
'iLoginFSMExtension' => $baseDir . '/application/applicationextension.inc.php',
'iLoginUIExtension' => $baseDir . '/application/applicationextension.inc.php',

View File

@@ -498,6 +498,7 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b
'HistoryBlock' => __DIR__ . '/../..' . '/application/displayblock.class.inc.php',
'Html2Text\\Html2Text' => __DIR__ . '/../..' . '/application/Html2Text.php',
'Html2Text\\Html2TextException' => __DIR__ . '/../..' . '/application/Html2TextException.php',
'ILogFileNameBuilder' => __DIR__ . '/../..' . '/core/log.class.inc.php',
'ITopArchiveTar' => __DIR__ . '/../..' . '/core/tar-itop.class.inc.php',
'InlineImage' => __DIR__ . '/../..' . '/core/inlineimage.class.inc.php',
'InlineImageGC' => __DIR__ . '/../..' . '/core/inlineimage.class.inc.php',
@@ -516,7 +517,6 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b
'ListOqlExpression' => __DIR__ . '/../..' . '/core/oql/oqlquery.class.inc.php',
'LogAPI' => __DIR__ . '/../..' . '/core/log.class.inc.php',
'LogFileNameBuilderFactory' => __DIR__ . '/../..' . '/core/log.class.inc.php',
'LogFileRotationProcess' => __DIR__ . '/../..' . '/core/log.class.inc.php',
'LoginBlockExtension' => __DIR__ . '/../..' . '/application/logintwig.class.inc.php',
'LoginTwigContext' => __DIR__ . '/../..' . '/application/logintwig.class.inc.php',
'LoginTwigRenderer' => __DIR__ . '/../..' . '/application/logintwig.class.inc.php',
@@ -534,7 +534,6 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b
'ModuleDesign' => __DIR__ . '/../..' . '/core/moduledesign.class.inc.php',
'ModuleHandlerAPI' => __DIR__ . '/../..' . '/core/modulehandler.class.inc.php',
'ModuleHandlerApiInterface' => __DIR__ . '/../..' . '/core/modulehandler.class.inc.php',
'MonthlyRotatingLogFileNameBuilder' => __DIR__ . '/../..' . '/core/log.class.inc.php',
'MyHelpers' => __DIR__ . '/../..' . '/core/MyHelpers.class.inc.php',
'MySQLException' => __DIR__ . '/../..' . '/core/cmdbsource.class.inc.php',
'MySQLHasGoneAwayException' => __DIR__ . '/../..' . '/core/cmdbsource.class.inc.php',
@@ -836,6 +835,9 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b
'Psr\\Log\\LoggerInterface' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerInterface.php',
'Psr\\Log\\LoggerTrait' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerTrait.php',
'Psr\\Log\\NullLogger' => __DIR__ . '/..' . '/psr/log/Psr/Log/NullLogger.php',
'Psr\\Log\\Test\\DummyTest' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php',
'Psr\\Log\\Test\\LoggerInterfaceTest' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php',
'Psr\\Log\\Test\\TestLogger' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/TestLogger.php',
'Psr\\SimpleCache\\CacheException' => __DIR__ . '/..' . '/psr/simple-cache/src/CacheException.php',
'Psr\\SimpleCache\\CacheInterface' => __DIR__ . '/..' . '/psr/simple-cache/src/CacheInterface.php',
'Psr\\SimpleCache\\InvalidArgumentException' => __DIR__ . '/..' . '/psr/simple-cache/src/InvalidArgumentException.php',
@@ -1074,6 +1076,9 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b
'Symfony\\Bundle\\FrameworkBundle\\Templating\\TemplateNameParser' => __DIR__ . '/..' . '/symfony/framework-bundle/Templating/TemplateNameParser.php',
'Symfony\\Bundle\\FrameworkBundle\\Templating\\TemplateReference' => __DIR__ . '/..' . '/symfony/framework-bundle/Templating/TemplateReference.php',
'Symfony\\Bundle\\FrameworkBundle\\Templating\\TimedPhpEngine' => __DIR__ . '/..' . '/symfony/framework-bundle/Templating/TimedPhpEngine.php',
'Symfony\\Bundle\\FrameworkBundle\\Test\\ForwardCompatTestTrait' => __DIR__ . '/..' . '/symfony/framework-bundle/Test/ForwardCompatTestTrait.php',
'Symfony\\Bundle\\FrameworkBundle\\Test\\KernelTestCase' => __DIR__ . '/..' . '/symfony/framework-bundle/Test/KernelTestCase.php',
'Symfony\\Bundle\\FrameworkBundle\\Test\\WebTestCase' => __DIR__ . '/..' . '/symfony/framework-bundle/Test/WebTestCase.php',
'Symfony\\Bundle\\FrameworkBundle\\Translation\\PhpExtractor' => __DIR__ . '/..' . '/symfony/framework-bundle/Translation/PhpExtractor.php',
'Symfony\\Bundle\\FrameworkBundle\\Translation\\PhpStringTokenParser' => __DIR__ . '/..' . '/symfony/framework-bundle/Translation/PhpStringTokenParser.php',
'Symfony\\Bundle\\FrameworkBundle\\Translation\\TranslationLoader' => __DIR__ . '/..' . '/symfony/framework-bundle/Translation/TranslationLoader.php',
@@ -1886,6 +1891,7 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b
'Symfony\\Component\\VarDumper\\Dumper\\DataDumperInterface' => __DIR__ . '/..' . '/symfony/var-dumper/Dumper/DataDumperInterface.php',
'Symfony\\Component\\VarDumper\\Dumper\\HtmlDumper' => __DIR__ . '/..' . '/symfony/var-dumper/Dumper/HtmlDumper.php',
'Symfony\\Component\\VarDumper\\Exception\\ThrowingCasterException' => __DIR__ . '/..' . '/symfony/var-dumper/Exception/ThrowingCasterException.php',
'Symfony\\Component\\VarDumper\\Test\\VarDumperTestTrait' => __DIR__ . '/..' . '/symfony/var-dumper/Test/VarDumperTestTrait.php',
'Symfony\\Component\\VarDumper\\VarDumper' => __DIR__ . '/..' . '/symfony/var-dumper/VarDumper.php',
'Symfony\\Component\\Yaml\\Command\\LintCommand' => __DIR__ . '/..' . '/symfony/yaml/Command/LintCommand.php',
'Symfony\\Component\\Yaml\\Dumper' => __DIR__ . '/..' . '/symfony/yaml/Dumper.php',
@@ -2089,6 +2095,8 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b
'Twig\\Source' => __DIR__ . '/..' . '/twig/twig/src/Source.php',
'Twig\\Template' => __DIR__ . '/..' . '/twig/twig/src/Template.php',
'Twig\\TemplateWrapper' => __DIR__ . '/..' . '/twig/twig/src/TemplateWrapper.php',
'Twig\\Test\\IntegrationTestCase' => __DIR__ . '/..' . '/twig/twig/src/Test/IntegrationTestCase.php',
'Twig\\Test\\NodeTestCase' => __DIR__ . '/..' . '/twig/twig/src/Test/NodeTestCase.php',
'Twig\\Token' => __DIR__ . '/..' . '/twig/twig/src/Token.php',
'Twig\\TokenParser\\AbstractTokenParser' => __DIR__ . '/..' . '/twig/twig/src/TokenParser/AbstractTokenParser.php',
'Twig\\TokenParser\\ApplyTokenParser' => __DIR__ . '/..' . '/twig/twig/src/TokenParser/ApplyTokenParser.php',
@@ -2286,6 +2294,11 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b
'Twig_Test' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Test.php',
'Twig_TestCallableInterface' => __DIR__ . '/..' . '/twig/twig/lib/Twig/TestCallableInterface.php',
'Twig_TestInterface' => __DIR__ . '/..' . '/twig/twig/lib/Twig/TestInterface.php',
'Twig_Test_Function' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Test/Function.php',
'Twig_Test_IntegrationTestCase' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Test/IntegrationTestCase.php',
'Twig_Test_Method' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Test/Method.php',
'Twig_Test_Node' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Test/Node.php',
'Twig_Test_NodeTestCase' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Test/NodeTestCase.php',
'Twig_Token' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Token.php',
'Twig_TokenParser' => __DIR__ . '/..' . '/twig/twig/lib/Twig/TokenParser.php',
'Twig_TokenParserBroker' => __DIR__ . '/..' . '/twig/twig/lib/Twig/TokenParserBroker.php',
@@ -2357,7 +2370,6 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b
'iDBObjectSetIterator' => __DIR__ . '/../..' . '/core/dbobjectiterator.php',
'iDBObjectURLMaker' => __DIR__ . '/../..' . '/application/applicationcontext.class.inc.php',
'iDisplay' => __DIR__ . '/../..' . '/core/dbobject.class.php',
'iLogFileNameBuilder' => __DIR__ . '/../..' . '/core/log.class.inc.php',
'iLoginExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'iLoginFSMExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'iLoginUIExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',

View File

@@ -10,10 +10,13 @@ use Combodo\iTop\Test\UnitTest\ItopTestCase;
*/
class ThemeHandlerTest extends ItopTestCase
{
const PATTERN = '|\\\/var[^"]+testimages|';
private $compileCSSServiceMock;
private $cssPath;
private $jsonThemeParamFile;
private $tmpDir;
private $aDirsToCleanup=array();
public function setUp()
{
@@ -25,18 +28,38 @@ class ThemeHandlerTest extends ItopTestCase
ThemeHandler::mockCompileCSSService($this->compileCSSServiceMock);
$this->tmpDir=$this->tmpdir();
$aDirsToCleanup[] = $this->tmpDir;
if (!is_dir($this->tmpDir ."/branding"))
{
@mkdir($this->tmpDir."/branding");
}
@mkdir($this->tmpDir."/branding/themes/");
@mkdir($this->tmpDir."/branding/themes/basque-red");
$this->recurseMkdir($this->tmpDir."/branding/themes/basque-red");
$this->cssPath = $this->tmpDir . '/branding/themes/basque-red/main.css';
$this->jsonThemeParamFile = $this->tmpDir . '/branding/themes/basque-red/theme-parameters.json';
$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 +91,129 @@ 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 */
$aStylesheetFiles = array();
$aImportsPaths = array(APPROOT.'datamodels');
$oImports = $oTheme->GetNodes('imports/import');
foreach($oImports as $oImport)
{
$sImportId = $oImport->getAttribute('id');
$aThemeParameters['imports'][$sImportId] = $oImport->GetText();
$sFile = ThemeHandler::FindStylesheetFile($oImport->GetText(), $aImportsPaths);
$aStylesheetFiles[] = $sFile;
}
/** @var \DOMNodeList $oStylesheets */
$oStylesheets = $oTheme->GetNodes('stylesheets/stylesheet');
foreach($oStylesheets as $oStylesheet)
{
$sStylesheetId = $oStylesheet->getAttribute('id');
$aThemeParameters['stylesheets'][$sStylesheetId] = $oStylesheet->GetText();
$sFile = ThemeHandler::FindStylesheetFile($oStylesheet->GetText(), $aImportsPaths);
$aStylesheetFiles[] = $sFile;
}
$sThemeFolderPath = APPROOT.'env-production/branding/themes/'.$sThemeId.'/test';
if (!$this->recurseMkdir($sThemeFolderPath))
{
$this->assertTrue(false, "Cannot create directory $sThemeFolderPath");
}
$aIncludedImages=ThemeHandler::GetIncludedImages($aThemeParameters['variables'], $aStylesheetFiles, $sThemeFolderPath);
$compiled_json_sig = ThemeHandler::ComputeSignature($aThemeParameters, $aImportsPaths, $aIncludedImages);
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)");
}
}
function recurseMkdir($dir)
{
if (is_dir($dir))
{
return true;
}
$sParentDir = dirname($dir);
if (!$this->recurseMkdir($sParentDir))
{
return false;
}
return @mkdir($dir);
}
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":"1d4b4ae2a6fba3db101f8dd1cecab082","jqueryui":"78cfafc3524dac98e61fc2460918d4e5","main":"52d8a7c5530ceb3a4d777364fa4e1eea"},"imports":[],"images":[]}
JSON;
$this->assertEquals($expect_sig,$sig);
@@ -166,168 +307,274 @@ JSON;
);
}
/**
* @param $ThemeParametersJson
* @param $CompileCSSFromSASSCount
* @param int $missingFile
* @param int $filesTouchedRecently
* @param int $fileMd5sumModified
* @param null $fileToTest
*
* @param null $expected_maincss_path
*
* @throws \CoreException
* @dataProvider CompileThemesProvider
*/
public function testCompileThemes($ThemeParametersJson, $CompileCSSFromSASSCount, $missingFile=0, $filesTouchedRecently=0, $fileMd5sumModified=0, $fileToTest=null, $expected_maincss_path=null, $bSetup=true)
{
$fileToTest=$this->tmpDir.'/'.$fileToTest;
$cssPath = $this->tmpDir . '/branding/themes/basque-red/main.css';
copy(APPROOT . 'test/application/theme-handler/expected/themes/basque-red/main.css', $cssPath);
if ($missingFile==1)
{
unlink($fileToTest);
}
if ($filesTouchedRecently==1)
{
sleep(1);
touch($fileToTest);
}
if ($fileMd5sumModified==1)
{
sleep(1);
file_put_contents($fileToTest, "###\n".file_get_contents($fileToTest));
}
$this->compileCSSServiceMock->expects($this->exactly($CompileCSSFromSASSCount))
->method("CompileCSSFromSASS")
->willReturn("====CSSCOMPILEDCONTENT====");
ThemeHandler::CompileTheme('basque-red', $bSetup, json_decode($ThemeParametersJson, true), array($this->tmpDir.'/branding/themes/'), $this->tmpDir);
if ($CompileCSSFromSASSCount==1)
{
$this->assertEquals(file_get_contents(APPROOT . $expected_maincss_path), file_get_contents($cssPath));
}
}
/**
* @return array
*/
public function CompileThemesProvider()
{
$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";
$stylesheet_file_path = '/branding/css/light-grey.scss';
$sModifiedVariableThemeParameterJson='{"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"}}';
$sInitialThemeParamJson='{"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"}}';
$sImportFilePath = '/branding/css/css-variables.scss';
$sVarChangedMainCssPath="test/application/theme-handler/expected/themes/basque-red/main_varchanged.css";
$sStylesheetMainCssPath="test/application/theme-handler/expected/themes/basque-red/main_stylesheet.css";
$sImageMainCssPath="test/application/theme-handler/expected/themes/basque-red/main_imagemodified.css";
$sImportModifiedMainCssPath="test/application/theme-handler/expected/themes/basque-red/main_importmodified.css";
$sStylesheetFilePath = '/branding/css/light-grey.scss';
$sImageFilePath = '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),
"setup context: variables list modified without any file touched" => array($sModifiedVariableThemeParameterJson, 1,false,false,false,$sImportFilePath, $sVarChangedMainCssPath),
"setup context: variables list modified with files touched" => array($sModifiedVariableThemeParameterJson, 1,false,true,false,$sImportFilePath, $sVarChangedMainCssPath, false),
"itop page/theme loading; variables list modified without any file touched" => array($sModifiedVariableThemeParameterJson, 0,false,false,false,$sImportFilePath, $sVarChangedMainCssPath, 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),
"import file modified" => array($initialThemeParamJson, 1, 0, 0, 1, $import_file_path, $importmodified_maincss),
"import file missing" => array($sInitialThemeParamJson, 0, true, false, false, $sImportFilePath),
"import file touched" => array($sInitialThemeParamJson, 0, false, true, false, $sImportFilePath),
"import file modified" => array($sInitialThemeParamJson, 1, false, false, true, $sImportFilePath, $sImportModifiedMainCssPath),
//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 missing" => array($sInitialThemeParamJson, 0, true, false, false, $sStylesheetFilePath),
"stylesheets file touched" => array($sInitialThemeParamJson, 0, false, true, false, $sStylesheetFilePath),
"stylesheets file modified" => array($sInitialThemeParamJson, 1, false, false, true, $sStylesheetFilePath, $sStylesheetMainCssPath),
//images
"image file missing" => array($sInitialThemeParamJson, 0, true, false, false, $sImageFilePath),
"image file touched" => array($sInitialThemeParamJson, 0, false, true, false, $sImageFilePath),
"image file modified" => array($sInitialThemeParamJson, 1, false, false, true, $sImageFilePath, $sImageMainCssPath),
);
}
/**
* @param $xmlDataCusto
* @dataProvider providePrecompiledStyleSheets
* @throws \Exception
* @param $ThemeParametersJson
* @param int $iCompileCSSFromSASSCount
* @param boolean $bMissingFile
* @param boolean $bFilesTouchedRecently
* @param boolean $bFileMd5sumModified
* @param null $sFileToTest
* @param null $sExpectedMainCssPath
* @param bool $bSetup
*
* @throws \CoreException
* @dataProvider CompileThemesProvider
*/
public function testValidatePrecompiledStyles($xmlDataCusto)
public function testCompileThemes($ThemeParametersJson, $iCompileCSSFromSASSCount, $bMissingFile=false, $bFilesTouchedRecently=false, $bFileMd5sumModified=false, $sFileToTest=null, $sExpectedMainCssPath=null, $bSetup=true)
{
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)
$sAfterReplacementCssVariableMd5sum='';
if (is_file($this->tmpDir.'/'.$sFileToTest))
{
$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)");
$sFileToTest=$this->tmpDir.'/'.$sFileToTest;
}
else
{
$sFileToTest=APPROOT.'/'.$sFileToTest;
}
}
//copy images in test dir
$sAbsoluteImagePath = APPROOT .'test/application/theme-handler/copied/testimages/';
$this->recurseMkdir($sAbsoluteImagePath);
$aDirsToCleanup[] = $sAbsoluteImagePath;
$this->recurse_copy(APPROOT .'test/application/theme-handler/expected/testimages/', $sAbsoluteImagePath);
public function providePrecompiledStyleSheets()
{
$datamodelfiles=glob(dirname(__FILE__)."/../../datamodels/2.x/**/datamodel*.xml");
$test_set = array();
foreach ($datamodelfiles as $datamodelfile)
//change approot-relative in css-variable to use absolute path
$sCssVarPath = $this->tmpDir."/branding/css/css-variables.scss";
$sBeforeReplacementCssVariableMd5sum = md5_file($sCssVarPath);
echo 'BEFORE :' . $sBeforeReplacementCssVariableMd5sum .' ' . $sCssVarPath . ' ';
$sCssVariableContent = file_get_contents($sCssVarPath);
$sLine = '$approot-relative: "'.$sAbsoluteImagePath.'" !default;';
$sCssVariableContent=preg_replace("/\\\$approot-relative: \"(.*)\"/", $sLine, $sCssVariableContent);
file_put_contents($sCssVarPath, $sCssVariableContent);
if ($bMissingFile)
{
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);
}
}
$sAfterReplacementCssVariableMd5sum = $sBeforeReplacementCssVariableMd5sum;
unlink($sFileToTest);
}
return $test_set;
if (is_file($sCssVarPath))
{
$sAfterReplacementCssVariableMd5sum = md5_file($sCssVarPath);
}
//change cssvar md5sum + image absolute paths
$sMainCssContent = file_get_contents(APPROOT."test/application/theme-handler/expected/themes/basque-red/main_testcompilethemes.css");
$sMainCssContent = preg_replace('/MD5SUM/', $sAfterReplacementCssVariableMd5sum, $sMainCssContent);
$sReplacement = rtrim($sAbsoluteImagePath, '/');
$sReplacement=preg_replace('|\/|', '\/', $sReplacement);
$sMainCssContent = preg_replace(static::PATTERN, $sReplacement, $sMainCssContent);
$cssPath = $this->tmpDir . '/branding/themes/basque-red/main.css';
echo 'PUT md5sum: '.$sAfterReplacementCssVariableMd5sum.' in '.$cssPath.' ';
file_put_contents($cssPath, $sMainCssContent);
//should be after main.css modification to make sure precompilation check will be performed
if ($bFilesTouchedRecently)
{
sleep(1);
touch($sFileToTest);
}
//same: it should be after main.css modification
if ($bFileMd5sumModified)
{
$sMd5sum = md5_file($sFileToTest);
echo ' BEFORE touch: ' . $sMd5sum .' ' . $sFileToTest;
sleep(1);
file_put_contents($sFileToTest, "###\n".file_get_contents($sFileToTest));
$sMd5sum = md5_file($sFileToTest);
echo ' AFTER touch: ' . $sMd5sum .' ' . $sFileToTest;
}
if (is_file($sCssVarPath))
{
$sAfterReplacementCssVariableMd5sum = md5_file($sCssVarPath);
}
$this->compileCSSServiceMock->expects($this->exactly($iCompileCSSFromSASSCount))
->method("CompileCSSFromSASS")
->willReturn("====CSSCOMPILEDCONTENT====");
$aThemeParameters = json_decode($ThemeParametersJson, true);
ThemeHandler::CompileTheme('basque-red', $bSetup, $aThemeParameters, array($this->tmpDir.'/branding/themes/'), $this->tmpDir);
if ($iCompileCSSFromSASSCount==1)
{
$sExpectedMainCssFile = APPROOT.$sExpectedMainCssPath;
if (!is_file($sExpectedMainCssFile))
{
$this->assertTrue(false, "Cannot find expected main css file $sExpectedMainCssFile");
}
$aPatterns = array(static::PATTERN, '/'.$sBeforeReplacementCssVariableMd5sum.'/');
$aPatterns[] = "/8100523d2e76a70266f3e7110e2fe5fb/";
$aReplacements = array($sReplacement, $sAfterReplacementCssVariableMd5sum);
$aReplacements[] = md5(json_encode($aThemeParameters['variables']));
var_dump($aReplacements);
$this->DoInnerJsonValidation($sExpectedMainCssFile, $cssPath, $aPatterns, $aReplacements);
}
}
public function DoInnerJsonValidation($sExpectedCssFile, $sActualCssFile, $aPatterns, $aReplacements)
{
$sActualContent = file_get_contents($sActualCssFile);
//replace absolute path to fix it in any envt
$sExpectedContent = preg_replace($aPatterns, $aReplacements, file_get_contents($sExpectedCssFile));
//echo($sExpectedContent);
if ($sExpectedContent != $sActualContent)
{
//try to have inner json diff failure
/** @var array $aExpectedJson */
//replace absolute path to fix it in any envt
$sExpectedJson = preg_replace($aPatterns, $aReplacements, ThemeHandler::GetSignature($sExpectedCssFile));
$aExpectedJson = json_decode($sExpectedJson, true);
/** @var array $aActualJson */
$aActualJson = json_decode(ThemeHandler::GetSignature($sActualCssFile), true);
$this->assertEquals($aExpectedJson, $aActualJson, "CSS file dont match ($sExpectedCssFile / $sActualCssFile)");
}
$this->assertTrue(true);
}
/**
* @param $sScssFile
*
* @dataProvider GetAllUrlFromScssProvider
*/
public function testGetAllUrlFromScss($sScssFile)
{
$aIncludedUrls = ThemeHandler::GetAllUrlFromScss(array('attr' => "123"),APPROOT.$sScssFile);
$this->assertEquals(array('approot-relative', 'version', 'version1'), array_values($aIncludedUrls['aMissingVariables']));
$this->assertEquals(array("attr"=>"123"),
$aIncludedUrls['aFoundVariables']);
$aExpectedCompletedUrls = array(
'css/ui-lightness/images/tutu.jpg',
"css/ui-lightness/images/tata.jpeg",
"css/ui-lightness/images/tete.jpeg?g=123"
);
$aExpectedToCompleteUrls = array(
'\'abc/\'+ $approot-relative + "css/ui-lightness/images/toutou.png?v=" + $version',
"\$approot-relative + \"css/ui-lightness/images/toto.png?v=\" + \$version",
'$approot-relative + \'css/ui-lightness/images/titi.gif?v=\' + $version1',
'"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7?v=" + $version',
);
$aIncludedUrls['aCompleteUrls'];
$this->assertEquals($aExpectedCompletedUrls, array_values($aIncludedUrls['aCompleteUrls']));
$this->assertEquals($aExpectedToCompleteUrls, array_values($aIncludedUrls['aToCompleteUrls']));
}
/**
* @return array
*/
public function GetAllUrlFromScssProvider()
{
return array('test-getimages.scss' => array('test/application/theme-handler/getimages/test-getimages.scss'));
}
public function testFindMissingVariables()
{
$sContent = <<< 'SCSS'
$approot-relative: "../../../../../" !default; // relative to env-***/branding/themes/***/main.css
$approot-relative2: "../../" !default; // relative to env-***/branding/themes/***/main.css
$gray-base: #000 !default;
$gray-darker: lighten($gray-base, 13.5%) !default; // #222
$brand-primary: $combodo-orange !default;
$brand-primary-lightest: lighten($brand-primary, 15%) !default;
$content-color: #eeeeee !default;
$default-font-family: Trebuchet MS,Tahoma,Verdana,Arial,sans-serif !default;
$icons-filter: hue-rotate(0deg) !default;
$toto : titi;
SCSS;
$aMissingVariables = array('gabu', 'toto', 'approot-relative', 'approot-relative2', 'gray-base', 'gray-darker', 'brand-primary', 'brand-primary-lightest', 'content-color', 'default-font-family', 'icons-filter');
list($aMissingVariables, $aFoundVariables) = ThemeHandler::FindMissingVariables(array('gabu' => 'zomeu'), $aMissingVariables, array("a" => "b"), $sContent);
$aExpectedFoundVariables = array(
'gabu' => 'zomeu',
'toto' => 'titi',
'approot-relative' => '../../../../../',
'approot-relative2' => '../../',
'gray-base' => '#000',
'a' => 'b',
'content-color' => '#eeeeee',
'default-font-family' => 'Trebuchet MS,Tahoma,Verdana,Arial,sans-serif',
'icons-filter' => 'hue-rotate(0deg)',
'toto' => 'titi',
);
$this->assertEquals($aExpectedFoundVariables, $aFoundVariables);
$this->assertEquals(array('gray-darker', 'brand-primary', 'brand-primary-lightest'), $aMissingVariables);
}
/**
* @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);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 324 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 676 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 842 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 543 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 327 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1011 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 850 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 643 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 781 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 787 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 750 B

View File

@@ -15,9 +15,6 @@
*
* You should have received a copy of the GNU Affero General Public License
*/
// Beware the version number MUST be enclosed with quotes otherwise v2.3.0 becomes v2 0.3 .0
$version: "v2.7.0-1";
$approot-relative: "../../../../../" !default; // relative to env-***/branding/themes/***/main.css
// Base colors

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 324 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 676 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 842 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 543 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 327 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1011 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 850 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 643 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 781 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 787 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 750 B

View File

@@ -1,6 +1,6 @@
/*
=== SIGNATURE BEGIN ===
{"variables":"37c31105548fce44fecca5cb34e455c9","stylesheets":{"css-variables":"934888ebb4991d4c76555be6b6d1d5cc","jqueryui":"78cfafc3524dac98e61fc2460918d4e5","main":"52d8a7c5530ceb3a4d777364fa4e1eea"},"imports":[]}
{"variables":"37c31105548fce44fecca5cb34e455c9","stylesheets":{"css-variables":"1d4b4ae2a6fba3db101f8dd1cecab082","jqueryui":"78cfafc3524dac98e61fc2460918d4e5","main":"52d8a7c5530ceb3a4d777364fa4e1eea"},"imports":[],"images":[]}
=== SIGNATURE END ===
*/
====CSSCOMPILEDCONTENT====

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -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"
]

View File

@@ -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);