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

N°2982 - speedup themes/scss compilation during setup
N°2996 - Remove iTop version from css-variable.scss
This commit is contained in:
odain
2020-08-26 17:59:31 +02:00
56 changed files with 17865 additions and 30 deletions

View File

@@ -25,6 +25,9 @@
*/
class ThemeHandler
{
const IMAGE_EXTENSIONS = ['png', 'gif', 'jpg', 'jpeg'];
private static $oCompileCSSService;
/**
* Return default theme name and parameters
*
@@ -33,19 +36,19 @@ class ThemeHandler
*/
public static function GetDefaultThemeInformation()
{
return array(
return [
'name' => 'light-grey',
'parameters' => array(
'variables' => array(),
'imports' => array(
'parameters' => [
'variables' => [],
'imports' => [
'css-variables' => '../css/css-variables.scss',
),
'stylesheets' => array(
],
'stylesheets' => [
'jqueryui' => '../css/ui-lightness/jqueryui.scss',
'main' => '../css/light-grey.scss',
),
),
);
],
],
];
}
/**
@@ -108,37 +111,49 @@ class ThemeHandler
SetupUtils::builddir($sDefaultThemeDirPath);
}
static::CompileTheme($sThemeId, $aDefaultTheme['parameters']);
static::CompileTheme($sThemeId, false, "", $aDefaultTheme['parameters']);
}
// Return absolute url to theme compiled css
return utils::GetAbsoluteUrlModulesRoot().'/branding/themes/'.$sThemeId.'/main.css';
return utils::GetAbsoluteUrlModulesRoot().'branding/themes/'.$sThemeId.'/main.css';
}
/**
* Compile the $sThemeId theme
* Compile the $sThemeId theme, the actual compilation is skipped when either
* 1) The produced CSS file exists and is more recent than any of its components (imports, stylesheets)
* 2) The produced CSS file exists and its signature is equal to the expected signature (imports, stylesheets, variables)
*
* @param string $sThemeId
* @param boolean $bSetup
* @param string $sSetupCompilationTimestamp : setup compilation timestamp in micro secunds
* @param array|null $aThemeParameters Parameters (variables, imports, stylesheets) for the theme, if not passed, will be retrieved from compiled DM
* @param array|null $aImportsPaths Paths where imports can be found. Must end with '/'
* @param string|null $sWorkingPath Path of the folder used during compilation. Must end with a '/'
*
* @throws \CoreException
* @return boolean: indicate whether theme compilation occured
*/
public static function CompileTheme($sThemeId, $aThemeParameters = null, $aImportsPaths = null, $sWorkingPath = null)
public static function CompileTheme($sThemeId, $bSetup=false, $sSetupCompilationTimestamp="", $aThemeParameters = null, $aImportsPaths = null, $sWorkingPath = null)
{
if ($sSetupCompilationTimestamp==="")
{
$sSetupCompilationTimestamp = microtime(true);
}
$sSetupCompilationTimestampInSecunds = (strpos($sSetupCompilationTimestamp, '.') !==false) ? explode('.', $sSetupCompilationTimestamp)[0] : $sSetupCompilationTimestamp;
$sEnv = APPROOT.'env-'.utils::GetCurrentEnvironment().'/';
// Default working path
if($sWorkingPath === null)
{
$sWorkingPath = APPROOT.'env-'.utils::GetCurrentEnvironment().'/';
$sWorkingPath = $sEnv;
}
// Default import paths (env-*)
if($aImportsPaths === null)
{
$aImportsPaths = array(
APPROOT.'env-'.utils::GetCurrentEnvironment().'/',
);
$aImportsPaths = [ $sEnv];
}
// Note: We do NOT check that the folder exists!
@@ -148,6 +163,11 @@ class ThemeHandler
// Save parameters if passed... (typically during DM compilation)
if(is_array($aThemeParameters))
{
if (!is_dir($sThemeFolderPath))
{
mkdir($sWorkingPath.'/branding/');
mkdir($sWorkingPath.'/branding/themes/');
}
file_put_contents($sThemeFolderPath.'/theme-parameters.json', json_encode($aThemeParameters));
}
// ... Otherwise, retrieve them from compiled DM (typically when switching current theme in the config. file)
@@ -160,31 +180,589 @@ class ThemeHandler
}
}
$aThemeParametersWithVersion = self::CloneThemeParameterAndIncludeVersion($aThemeParameters, $sSetupCompilationTimestampInSecunds);
$sTmpThemeScssContent = '';
$iStyleLastModified = 0;
clearstatcache();
// Loading files to import and stylesheet to compile, also getting most recent modification time on overall files
$aStylesheetFiles = [];
foreach ($aThemeParameters['imports'] as $sImport)
{
$sTmpThemeScssContent .= '@import "'.$sImport.'";'."\n";
$iImportLastModified = @filemtime($sWorkingPath.$sImport);
$sFile = static::FindStylesheetFile($sImport, $aImportsPaths);
$iImportLastModified = @filemtime($sFile);
$aStylesheetFiles[] = $sFile;
$iStyleLastModified = $iStyleLastModified < $iImportLastModified ? $iImportLastModified : $iStyleLastModified;
}
foreach ($aThemeParameters['stylesheets'] as $sStylesheet)
{
$sTmpThemeScssContent .= '@import "'.$sStylesheet.'";'."\n";
$iStylesheetLastModified = @filemtime($sWorkingPath.$sStylesheet);
$sFile = static::FindStylesheetFile($sStylesheet, $aImportsPaths);
$iStylesheetLastModified = @filemtime($sFile);
$aStylesheetFiles[] = $sFile;
$iStyleLastModified = $iStyleLastModified < $iStylesheetLastModified ? $iStylesheetLastModified : $iStyleLastModified;
}
// Checking if our compiled css is outdated
if (!file_exists($sThemeCssPath) || (is_writable($sThemeFolderPath) && (@filemtime($sThemeCssPath) < $iStyleLastModified)))
$aIncludedImages=static::GetIncludedImages($aThemeParametersWithVersion, $aStylesheetFiles, $sThemeId);
foreach ($aIncludedImages as $sImage)
{
$sTmpThemeCssContent = utils::CompileCSSFromSASS($sTmpThemeScssContent, $aImportsPaths, $aThemeParameters['variables']);
file_put_contents($sThemeCssPath, $sTmpThemeCssContent);
if (is_file($sImage))
{
$iStylesheetLastModified = @filemtime($sImage);
$iStyleLastModified = $iStyleLastModified < $iStylesheetLastModified ? $iStylesheetLastModified : $iStyleLastModified;
}
}
// Checking if our compiled css is outdated
$iFilemetime = @filemtime($sThemeCssPath);
$bFileExists = file_exists($sThemeCssPath);
$bVarSignatureChanged=false;
if ($bFileExists && $bSetup)
{
$sPrecompiledSignature = static::GetSignature($sThemeCssPath);
//check variable signature has changed which is independant from any file modification
if (!empty($sPrecompiledSignature)){
$sPreviousVariableSignature = static::GetVarSignature($sPrecompiledSignature);
$sCurrentVariableSignature = md5(json_encode($aThemeParameters['variables']));
$bVarSignatureChanged= ($sPreviousVariableSignature!==$sCurrentVariableSignature);
}
}
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, $aIncludedImages);
if ($bFileExists && !$bSetup)
{
$sPrecompiledSignature = static::GetSignature($sThemeCssPath);
}
if (!empty($sPrecompiledSignature) && $sActualSignature == $sPrecompiledSignature)
{
touch($sThemeCssPath); // Stylesheet is up to date, mark it as more recent to speedup next time
}
else
{
// Alas, we really need to recompile
// Add the signature to the generated CSS file so that the file can be used as a precompiled stylesheet if needed
$sSignatureComment =
<<<CSS
/*
=== SIGNATURE BEGIN ===
$sActualSignature
=== SIGNATURE END ===
*/
CSS;
if (!static::$oCompileCSSService)
{
static::$oCompileCSSService = new CompileCSSService();
}
//store it again to change $version with latest compiled time
$sTmpThemeCssContent = static::$oCompileCSSService->CompileCSSFromSASS($sTmpThemeScssContent, $aImportsPaths,
$aThemeParametersWithVersion);
file_put_contents($sThemeFolderPath.'/theme-parameters.json', json_encode($aThemeParameters));
file_put_contents($sThemeCssPath, $sSignatureComment.$sTmpThemeCssContent);
SetupLog::Info("Theme $sThemeId file compiled.");
return true;
}
}
return false;
}
/**
* Compute the signature of a theme defined by its theme parameters. The signature is a JSON structure of
* 1) one MD5 of all the variables/values (JSON encoded)
* 2) the MD5 of each stylesheet file
* 3) the MD5 of each import file
* 3) the MD5 of each images referenced in style sheets
*
* @param string[] $aThemeParameters
* @param string[] $aImportsPaths
* @param string[] $aIncludedImages
*
* @return string
* @throws \Exception
*/
public static function ComputeSignature($aThemeParameters, $aImportsPaths, $aIncludedImages)
{
$aSignature = [
'variables' => md5(json_encode($aThemeParameters['variables'])),
'stylesheets' => [],
'imports' => [],
'images' => []
];
foreach ($aThemeParameters['imports'] as $key => $sImport)
{
$sFile = static::FindStylesheetFile($sImport, $aImportsPaths);
$aSignature['stylesheets'][$key] = md5_file($sFile);
}
foreach ($aThemeParameters['stylesheets'] as $key => $sStylesheet)
{
$sFile = static::FindStylesheetFile($sStylesheet, $aImportsPaths);
$aSignature['stylesheets'][$key] = md5_file($sFile);
}
foreach ($aIncludedImages as $sImage)
{
if (is_file($sImage))
{
$sUri = str_replace(APPROOT, '', $sImage);
$aSignature['images'][$sUri] = md5_file($sImage);
}
}
return json_encode($aSignature);
}
/**
* Search for images referenced in stylesheet files
*
* @param array $aThemeParametersVariables
* @param array $aStylesheetFiles
* @param string $sThemeId: used only for logging purpose
*
* @return array
* @since 2.8.0
*/
public static function GetIncludedImages($aThemeParametersVariables, $aStylesheetFiles, $sThemeId)
{
$sTargetThemeFolderPath = static::GetCompiledThemeFolderAbsolutePath($sThemeId);
$aCompleteUrls = [];
$aToCompleteUrls = [];
$aMissingVariables = [];
$aFoundVariables = ['version'=>''];
$aMap = [
'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 = [];
foreach ($aMap ['aCompleteUrls'] as $sUri => $sUrl)
{
$sImg = $sUrl;
if (preg_match("/(.*)\?/", $sUrl, $aMatches))
{
$sImg=$aMatches[1];
}
if (static::HasImageExtension($sImg)
&& ! array_key_exists($sImg, $aImages))
{
$sFilePath = realpath($sImg);
if ($sFilePath!==false)
{
$aImages[$sImg]=$sFilePath;
continue;
}
$sCanonicalPath = static::CanonicalizePath($sTargetThemeFolderPath.DIRECTORY_SEPARATOR.$sImg);
$sFilePath=realpath($sCanonicalPath);
if ($sFilePath!==false)
{
$aImages[$sImg]=$sFilePath;
continue;
}
SetupLog::Warning("Cannot find $sCanonicalPath ($sImg) during SCSS $sThemeId precompilation");
}
}
return array_values($aImages);
}
/**
* Reduce path without using realpath (works only when file exists)
* @param $path
*
* @return string
*/
public static function CanonicalizePath($path)
{
$path = explode('/', str_replace('//','/', $path));
$stack = array();
foreach ($path as $seg) {
if ($seg == '..') {
// Ignore this segment, remove last segment from stack
array_pop($stack);
continue;
}
if ($seg == '.') {
// Ignore this segment
continue;
}
$stack[] = $seg;
}
return implode('/', $stack);
}
/**
* 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 = [];
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 [ $aNewMissingVars, $aFoundVariables ];
}
/**
* @param $aFoundVariables
* @param array $aToCompleteUrls
* @param array $aCompleteUrls
*
* @return array
*/
public static function ResolveUrls($aFoundVariables, array $aToCompleteUrls, array $aCompleteUrls)
{
if (!empty($aFoundVariables))
{
$aFoundVariablesWithEmptyValue = [];
foreach ($aFoundVariables as $aFoundVariable => $sValue)
{
$aFoundVariablesWithEmptyValue[$aFoundVariable] = '';
}
foreach ($aToCompleteUrls as $sUrlTemplate)
{
unset($aToCompleteUrls[$sUrlTemplate]);
$sResolvedUrl = static::ResolveUrl($sUrlTemplate, $aFoundVariables);
if ($sResolvedUrl == false)
{
$aToCompleteUrls[$sUrlTemplate] = $sUrlTemplate;
}
else
{
$sUri = static::ResolveUrl($sUrlTemplate, $aFoundVariablesWithEmptyValue);
$aExplodedUri = explode('?', $sUri);
if (empty($aExplodedUri))
{
$aCompleteUrls[$sUri] = $sResolvedUrl;
}
else
{
$aCompleteUrls[$aExplodedUri[0]] = $sResolvedUrl;
}
}
}
}
return [ $aToCompleteUrls, $aCompleteUrls];
}
/**
* Find all referenced URLs from a SCSS file.
* @param $aThemeParametersVariables
* @param $sStylesheetFile
*
* @return array
*/
public static function GetAllUrlFromScss($aThemeParametersVariables, $sStylesheetFile)
{
$aCompleteUrls = [];
$aToCompleteUrls = [];
$aMissingVariables = [];
$aFoundVariables = [];
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 [
'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= [];
$aReplacement= [];
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
*
* Note the signature can be place anywhere in the CSS file (obviously inside a CSS comment !) but the
* function will be faster if the signature is at the beginning of the file (since the file is scanned from the start)
*
* @param $sFilepath
*
* @return string
*/
public static function GetSignature($sFilepath)
{
$sPreviousLine = '';
$hFile = @fopen($sFilepath, "r");
if ($hFile !== false)
{
$sLine = '';
do
{
$sPreviousLine = $sLine;
$sLine = rtrim(fgets($hFile)); // Remove the trailing \n
}
while (($sLine !== false) && ($sLine != '=== SIGNATURE END ==='));
fclose($hFile);
}
return $sPreviousLine;
}
public static function GetVarSignature($JsonSignature)
{
$aJsonArray = json_decode($JsonSignature, true);
if (array_key_exists('variables', $aJsonArray))
{
return $aJsonArray['variables'];
}
return false;
}
/**
* Find the given file in the list of ImportsPaths directory
* @param string $sFile
* @param string[] $aImportsPaths
* @throws Exception
* @return string
*/
public static function FindStylesheetFile($sFile, $aImportsPaths)
{
foreach($aImportsPaths as $sPath)
{
$sImportedFile = realpath($sPath.'/'.$sFile);
if (file_exists($sImportedFile))
{
return $sImportedFile;
}
}
return ''; // Not found, fail silently, maybe the SCSS compiler knowns better...
}
public static function MockCompileCSSService($oCompileCSSServiceMock)
{
static::$oCompileCSSService = $oCompileCSSServiceMock;
}
/**
* Clone variable array and include $version with bSetupCompilationTimestamp value
* @param $aThemeParameters
* @param $bSetupCompilationTimestamp
*
* @return array
*/
public static function CloneThemeParameterAndIncludeVersion($aThemeParameters, $bSetupCompilationTimestamp)
{
$aThemeParametersVariable = [];
if (array_key_exists('variables', $aThemeParameters))
{
if (is_array($aThemeParameters['variables']))
{
$aThemeParametersVariable = array_merge([], $aThemeParameters['variables']);
}
}
$aThemeParametersVariable['$version'] = $bSetupCompilationTimestamp;
return $aThemeParametersVariable;
}
}
class CompileCSSService
{
/**
* CompileCSSService constructor.
*/
public function __construct()
{
}
public function CompileCSSFromSASS($sSassContent, $aImportPaths = [], $aVariables = []){
return utils::CompileCSSFromSASS($sSassContent, $aImportPaths, $aVariables);
}
}

View File

@@ -15,10 +15,7 @@
*
* 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.8.0";
$approot-relative: "../../../../../" !default; // relative to env-***/branding/themes/***/main.css
$approot-relative: "../../../../" !default; // relative to env-***/branding/themes/***/main.css
// Base colors
$gray-base: #000 !default;

View File

@@ -8384,6 +8384,7 @@
<stylesheet id="jqueryui">../css/ui-lightness/jqueryui.scss</stylesheet>
<stylesheet id="main">../css/light-grey.scss</stylesheet>
</stylesheets>
<precompiled_stylesheet>itop-config-mgmt/precompiled-themes/light-grey/main.css</precompiled_stylesheet>
</theme>
<theme id="test-red" _delta="define">
<variables>
@@ -8399,6 +8400,7 @@
<stylesheet id="main">../css/light-grey.scss</stylesheet>
<stylesheet id="environment-banner">../css/backoffice-environment-banner.scss</stylesheet>
</stylesheets>
<precompiled_stylesheet>itop-config-mgmt/precompiled-themes/test-red/main.css</precompiled_stylesheet>
</theme>
</themes>
</branding>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -60,6 +60,7 @@ class MFCompiler
protected $aSnippets;
protected $aRelations;
protected $sEnvironment;
protected $sCompilationTimeStamp;
public function __construct($oModelFactory, $sEnvironment)
{
@@ -74,7 +75,8 @@ class MFCompiler
$this->sMainPHPCode .= " * This file was automatically generated by the compiler on ".date('Y-m-d H:i:s')." -- DO NOT EDIT\n";
$this->sMainPHPCode .= " */\n";
$this->sMainPHPCode .= "\n";
$this->sMainPHPCode .= "define('COMPILATION_TIMESTAMP', '".microtime(true)."');\n";
$this->sCompilationTimeStamp = "".microtime(true);
$this->sMainPHPCode .= "define('COMPILATION_TIMESTAMP', '".$this->sCompilationTimeStamp."');\n";
$this->aSnippets = array();
$this->aRelations = array();
}
@@ -2686,6 +2688,7 @@ EOF;
'variables' => array(),
'imports' => array(),
'stylesheets' => array(),
'precompiled_stylesheet' => '',
);
/** @var \DOMNodeList $oVariables */
@@ -2711,7 +2714,7 @@ EOF;
$sStylesheetId = $oStylesheet->getAttribute('id');
$aThemeParameters['stylesheets'][$sStylesheetId] = $oStylesheet->GetText();
}
$aThemeParameters['precompiled_stylesheet'] = $oTheme->GetChildText('precompiled_stylesheet', '');
$aThemes[$sThemeId] = $aThemeParameters;
}
@@ -2723,6 +2726,7 @@ EOF;
}
// Compile themes
$fStart = microtime(true);
foreach($aThemes as $sThemeId => $aThemeParameters)
{
$sThemeDir = $sThemesDir.$sThemeId;
@@ -2730,10 +2734,29 @@ EOF;
{
SetupUtils::builddir($sThemeDir);
}
// Check if a precompiled version of the theme is supplied
$sPrecompiledFile = $sTempTargetDir.$aThemeParameters['precompiled_stylesheet'];
if (file_exists($sPrecompiledFile))
{
copy($sPrecompiledFile, $sThemeDir.'/main.css');
// Make sure that the copy of the precompiled file is older than any other files to force a validation of the signature
touch($sThemeDir.'/main.css', 1577836800 /* 2020-01-01 00:00:00 */);
}
else if ($sPrecompiledFile != '')
{
$this->Log("Precompiled file not found: '$sPrecompiledFile'");
}
$bHasCompiled = ThemeHandler::CompileTheme($sThemeId, true, $this->sCompilationTimeStamp, $aThemeParameters, $aImportsPaths, $sTempTargetDir);
$sInitialPrecompiledFilePath = APPROOT.'datamodels/2.x/'.$aThemeParameters['precompiled_stylesheet'];
if ($bHasCompiled && is_file($sInitialPrecompiledFilePath))
{
SetupLog::Info("Replacing precompiled file $sInitialPrecompiledFilePath for theme $sThemeId for next setup.");
copy($sThemeDir.'/main.css', $sInitialPrecompiledFilePath);
}
ThemeHandler::CompileTheme($sThemeId, $aThemeParameters, $aImportsPaths, $sTempTargetDir);
}
$this->Log(sprintf('Themes compilation took: %.3f ms for %d themes.', (microtime(true) - $fStart)*1000.0, count($aThemes)));
}
/**

View File

@@ -0,0 +1,623 @@
<?php
use Combodo\iTop\Test\UnitTest\ItopTestCase;
/**
* @runTestsInSeparateProcesses
* @preserveGlobalState disabled
* @backupGlobals disabled
* @covers utils
*/
class ThemeHandlerTest extends ItopTestCase
{
const PATTERN = '|\\\/var[^"]+testimages|';
private $oCompileCSSServiceMock;
private $sCssPath;
private $sJsonThemeParamFile;
private $sTmpDir;
private $aDirsToCleanup= [];
public function setUp()
{
parent::setUp();
require_once(APPROOT.'application/themehandler.class.inc.php');
require_once(APPROOT.'setup/modelfactory.class.inc.php');
$this->oCompileCSSServiceMock = $this->createMock('CompileCSSService');
ThemeHandler::mockCompileCSSService($this->oCompileCSSServiceMock);
$this->sTmpDir=$this->tmpdir();
$aDirsToCleanup[] = $this->sTmpDir;
$this->recurseMkdir($this->sTmpDir."/branding/themes/basque-red");
$this->sCssPath = $this->sTmpDir . '/branding/themes/basque-red/main.css';
$this->sJsonThemeParamFile = $this->sTmpDir . '/branding/themes/basque-red/theme-parameters.json';
$this->recurse_copy(APPROOT."/test/application/theme-handler/expected/css", $this->sTmpDir."/branding/css");
}
public function tearDown()
{
parent::tearDown();
foreach ($this->aDirsToCleanup as $sDir)
{
echo $sDir;
$this->rrmdir($sDir);
}
}
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() {
$sTmpfile=tempnam(sys_get_temp_dir(),'');
if (file_exists($sTmpfile))
{
unlink($sTmpfile);
}
mkdir($sTmpfile);
if (is_dir($sTmpfile))
{
return $sTmpfile;
}
return sys_get_temp_dir();
}
public function recurse_copy($src,$dst) {
$dir = opendir($src);
@mkdir($dst);
while(false !== ( $file = readdir($dir)) ) {
if (( $file != '.' ) && ( $file != '..' )) {
if ( is_dir($src . '/' . $file) ) {
$this->recurse_copy($src . '/' . $file,$dst . '/' . $file);
}
else {
copy($src . '/' . $file,$dst . '/' . $file);
}
}
}
closedir($dir);
}
/**
* Test used to be notified by CI when precompiled styles are not up to date anymore in code repository.
*
* @param $xmlDataCusto
* @param $sPrecompiledStylesheet
* @param $oTheme
*
* @throws \Exception
*/
public function testValidatePrecompiledStyles()
{
$aErrors = [];
$aDataModelFiles=glob(dirname(__FILE__)."/../../datamodels/2.x/**/datamodel*.xml");
$aImportsPaths = [APPROOT.'datamodels'];
foreach ($aDataModelFiles as $sXmlDataCustoFilePath)
{
if (is_file($sXmlDataCustoFilePath))
{
$sXmlDataCustoFilePath = realpath($sXmlDataCustoFilePath);
$sContent = file_get_contents($sXmlDataCustoFilePath);
if (strpos($sContent, "precompiled_stylesheet") !== false)
{
$oDom = new MFDocument();
$oDom->load($sXmlDataCustoFilePath);
$oThemeNodes = $oDom->GetNodes("/itop_design/branding/themes/theme");
// Parsing themes from DM
foreach ($oThemeNodes as $oTheme)
{
/** @var \MFElement $oTheme */
$sPrecompiledStylesheetUri = $oTheme->GetChildText('precompiled_stylesheet', '');
if (empty($sPrecompiledStylesheetUri))
{
continue;
}
$sThemeId = $oTheme->getAttribute('id');
//echo "=== theme: $sThemeId ===\n";
$sPreCompiledSig = ThemeHandler::GetSignature(dirname(__FILE__)."/../../datamodels/2.x/".$sPrecompiledStylesheetUri);
//echo " precompiled signature: $sPreCompiledSig\n";
if (empty($sPreCompiledSig))
{
$aErrors[] = "Signature in precompiled theme '".$sThemeId."' is not retrievable (cf precompiledsheet $sPrecompiledStylesheetUri / datamodel $sXmlDataCustoFilePath)";
continue;
}
$aThemeParameters = [
'variables' => [],
'imports' => [],
'stylesheets' => [],
'precompiled_stylesheet' => $sPrecompiledStylesheetUri,
];
/** @var \DOMNodeList $oVariables */
$oVariables = $oTheme->GetNodes('variables/variable');
foreach ($oVariables as $oVariable)
{
$sVariableId = $oVariable->getAttribute('id');
$aThemeParameters['variables'][$sVariableId] = $oVariable->GetText();
}
$aStylesheetFiles = [];
/** @var \DOMNodeList $oImports */
$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;
}
$aIncludedImages = ThemeHandler::GetIncludedImages($aThemeParameters['variables'], $aStylesheetFiles, $sThemeId);
$compiled_json_sig = ThemeHandler::ComputeSignature($aThemeParameters, $aImportsPaths, $aIncludedImages);
//echo " current signature: $compiled_json_sig\n";
if ($sPreCompiledSig !== $compiled_json_sig)
{
$iLine = $oTheme->GetLineNo();
$aErrors[] = " $sPrecompiledStylesheetUri declared in $sXmlDataCustoFilePath:$iLine.";
$this->assertEquals($sPreCompiledSig, $compiled_json_sig, " $sPrecompiledStylesheetUri declared in $sXmlDataCustoFilePath:$iLine.");
continue;
}
}
}
}
}
if (count($aErrors)!=0)
{
$sMsg = "Below precompiled files are not up to date. Please run a new setup and save your precompiled files again:\n";
$sMsg .= implode("\n", $aErrors);
$this->fail($sMsg);
}
else
{
$this->assertTrue(true);
}
}
function recurseMkdir($dir)
{
if (is_dir($dir))
{
return true;
}
$sParentDir = dirname($dir);
if (!$this->recurseMkdir($sParentDir))
{
return false;
}
return @mkdir($dir);
}
public function testGetSignature()
{
$sSig = ThemeHandler::GetSignature(APPROOT.'test/application/theme-handler/expected/themes/basque-red/main.css');
$sExpectedSig=<<<JSON
{"variables":"37c31105548fce44fecca5cb34e455c9","stylesheets":{"css-variables":"3c3f5adf98b9dbf893658314436c4b93","jqueryui":"78cfafc3524dac98e61fc2460918d4e5","main":"52d8a7c5530ceb3a4d777364fa4e1eea"},"imports":[],"images":{"css\/ui-lightness\/images\/ui-icons_222222_256x240.png":"3a3c5468f484f07ac4a320d9e22acb8c","css\/ui-lightness\/images\/ui-bg_diagonals-thick_20_666666_40x40.png":"4429d568c67d8dfeb9040273ea0fb8c4","css\/ui-lightness\/images\/ui-icons_E87C1E_256x240.png":"7003dd36cb2aa032c8ec871ce4d4e03d","css\/ui-lightness\/images\/ui-icons_1c94c4_256x240.png":"dbd693dc8e0ef04e90a2f7ac7b390086","css\/ui-lightness\/images\/ui-icons_F26522_256x240.png":"16278ec0c07270be571f4c2e97fcc10c","css\/ui-lightness\/images\/ui-bg_diagonals-thick_18_b81900_40x40.png":"e460a66d4b3e093fc651e62a236267cb","css\/ui-lightness\/images\/ui-icons_ffffff_256x240.png":"41612b0f4a034424f8321c9f824a94da","css\/ui-lightness\/images\/ui-icons_ffd27a_256x240.png":"dda1b6f694b0d196aefc66a1d6d758f6","images\/actions_right.png":"31c8906bd25d27b83a0a2466bf903462","images\/ac-background.gif":"76135f3697b41a15aed787cfd77776c7","images\/green-square.gif":"16ea9a497d72f5e66e4e8ea9ae08024e","images\/tv-item.gif":"719fe2d4566108e73162fb8868d3778c","images\/tv-collapsable.gif":"63a3351ea0d580797c9b8c386aa4f48b","images\/tv-expandable.gif":"a2d1af4128e4a798a7f3390b12a28574","images\/tv-item-last.gif":"2ae7e1d9972ce71e5caa65a086bc5b7e","images\/tv-collapsable-last.gif":"71acaa9d7c2616e9e8b7131a75ca65da","images\/tv-expandable-last.gif":"9d51036b3a8102742709da66789fd0f7","images\/red-header.gif":"c73b8765f0c8c3c183cb6a0c2bb0ec69","images\/green-header.gif":"0e22a09bb8051b2a274b3427ede62e82","images\/orange-header.gif":"ce1f93f0af64431771b4cbd6c99c567b","images\/calendar.png":"ab56e59af3c96ca661821257d376465e","images\/truncated.png":"c6f91108afe8159d417b4dc556cd3b2a","images\/plus.gif":"f00e1e6e1161f48608bb2bbc79b9948c","images\/minus.gif":"6d77c0c0c2f86b6995d1cdf78274eaab","images\/full-screen.png":"b541fadd3f1563856a4b44aeebd9d563","images\/indicator.gif":"03ce3dcc84af110e9da8699a841e5200","images\/delete.png":"93c047549c31a270a037840277cf59d3","images\/bg.gif":"a315146ab814c73632480136576cd271","images\/desc.gif":"0f58b33929095ea17795dd53bbced5d9","images\/info-mini.png":"445c090ed777c5e6a08ac390fa896193","images\/ok.png":"f6973773335fd83d8d2875f9a3c925af","images\/error.png":"1af8a1041016f67669c5fd22dc88c82e","images\/eye-open-555.png":"9940f4e5b1248042c238e1924359fd5e","images\/eye-closed-555.png":"6ad3b0bae791bf61addc9d8ca80a642d","images\/eye-open-fff.png":"b7db2402d4d5c72314c25790a66150d4","images\/eye-closed-fff.png":"f9be7454dbb47b0e0bca3aa370ae7db5","images\/breadcrumb-separator.png":"1e7e50a8f573e230cf1e0f0399c516e8"}}
JSON;
$this->assertEquals($sExpectedSig, $sSig);
}
public function testGetVarSignature()
{
$sSignature=<<<JSON
{"variables":"37c31105548fce44fecca5cb34e455c9","stylesheets":{"css-variables":"934888ebb4991d4c76555be6b6d1d5cc","jqueryui":"78cfafc3524dac98e61fc2460918d4e5","main":"52d8a7c5530ceb3a4d777364fa4e1eea"},"imports":[]}
JSON;
$var_sig = ThemeHandler::GetVarSignature($sSignature);
$this->assertEquals("37c31105548fce44fecca5cb34e455c9",$var_sig);
}
/**
* @param bool $readFromParamAttributeFromJson
*
* @throws \CoreException
* @dataProvider CompileThemesProviderWithoutCss
*/
public function testCompileThemeWithoutCssFile_FocusOnParamAttribute($readFromParamAttributeFromJson=false)
{
$sExpectJsonFilePath = APPROOT.'test/application/theme-handler/expected/themes/basque-red/theme-parameters.json';
$sExpectedThemeParamJson = file_get_contents($sExpectJsonFilePath);
$aThemeParameters = json_decode($sExpectedThemeParamJson, true);
if (is_file($this->sJsonThemeParamFile))
{
unlink($this->sJsonThemeParamFile);
}
if (is_file($this->sCssPath))
{
unlink($this->sCssPath);
}
$this->oCompileCSSServiceMock->expects($this->exactly(1))
->method("CompileCSSFromSASS")
->willReturn("====CSSCOMPILEDCONTENT====");
if($readFromParamAttributeFromJson)
{
copy($sExpectJsonFilePath, $this->sJsonThemeParamFile);
$this->assertTrue(ThemeHandler::CompileTheme('basque-red', true, "COMPILATIONTIMESTAMP", null, [$this->sTmpDir.'/branding/themes/'], $this->sTmpDir));
}
else
{
$this->assertTrue(ThemeHandler::CompileTheme('basque-red', true, "COMPILATIONTIMESTAMP", $aThemeParameters, [$this->sTmpDir.'/branding/themes/'], $this->sTmpDir));
}
$this->assertTrue(is_file($this->sCssPath));
$this->assertEquals($sExpectedThemeParamJson, file_get_contents($this->sJsonThemeParamFile));
$this->assertEquals(file_get_contents(APPROOT . 'test/application/theme-handler/expected/themes/basque-red/main.css'), file_get_contents($this->sCssPath));
}
public function CompileThemesProviderWithoutCss()
{
return [
"pass ParamAttributes and Save them in Json" => [false],
"use them from saved json" => [true]
];
}
/**
* @param $ThemeParametersJson
*
* @param int $CompileCount
*
* @throws \CoreException
* @dataProvider CompileThemesProviderEmptyArray
*/
public function testCompileThemesEmptyArray($ThemeParametersJson, $CompileCount=0)
{
$sCssPath = $this->sTmpDir . '/branding/themes/basque-red/main.css';
copy(APPROOT . 'test/application/theme-handler/expected/themes/basque-red/main.css', $sCssPath);
$this->oCompileCSSServiceMock->expects($this->exactly($CompileCount))
->method("CompileCSSFromSASS")
->willReturn("====CSSCOMPILEDCONTENT====");
$this->assertEquals($CompileCount!=0,ThemeHandler::CompileTheme('basque-red', true, "COMPILATIONTIMESTAMP", json_decode($ThemeParametersJson, true), [$this->sTmpDir.'/branding/themes/'], $this->sTmpDir));
}
public function CompileThemesProviderEmptyArray()
{
$aEmptyImports = '{"variables":{"brand-primary":"#C53030","hover-background-color":"#F6F6F6","icons-filter":"grayscale(1)","search-form-container-bg-color":"#4A5568"},"imports":[],"stylesheets":{"jqueryui":"..\/css\/ui-lightness\/jqueryui.scss","main":"..\/css\/light-grey.scss"}}';
$aEmptyStyleSheets='{"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":[]}';
$aEmptyVars='{"variables":[],"imports":{"css-variables":"..\/css\/css-variables.scss"},"stylesheets":{"jqueryui":"..\/css\/ui-lightness\/jqueryui.scss","main":"..\/css\/light-grey.scss"}}';
return [
"empty imports" => [$aEmptyImports],
"empty styles" => [$aEmptyStyleSheets],
"empty vars" => [$aEmptyVars, 1],
];
}
/**
* @return array
*/
public function CompileThemesProvider()
{
$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 [
"setup context: variables list modified without any file touched" => [$sModifiedVariableThemeParameterJson, 1,false,false,false,$sImportFilePath, $sVarChangedMainCssPath],
"setup context: variables list modified with files touched" => [$sModifiedVariableThemeParameterJson, 1,false,true,false,$sImportFilePath, $sVarChangedMainCssPath, false],
"itop page/theme loading; variables list modified without any file touched" => [$sModifiedVariableThemeParameterJson, 0,false,false,false,$sImportFilePath, $sVarChangedMainCssPath, false],
//imports
"import file missing" => [$sInitialThemeParamJson, 0, true, false, false, $sImportFilePath],
"import file touched" => [$sInitialThemeParamJson, 0, false, true, false, $sImportFilePath],
"import file modified" => [$sInitialThemeParamJson, 1, false, false, true, $sImportFilePath, $sImportModifiedMainCssPath],
//stylesheets
"stylesheets file missing" => [$sInitialThemeParamJson, 0, true, false, false, $sStylesheetFilePath],
"stylesheets file touched" => [$sInitialThemeParamJson, 0, false, true, false, $sStylesheetFilePath],
"stylesheets file modified" => [$sInitialThemeParamJson, 1, false, false, true, $sStylesheetFilePath, $sStylesheetMainCssPath],
//images
"image file missing" => [$sInitialThemeParamJson, 0, true, false, false, $sImageFilePath],
"image file touched" => [$sInitialThemeParamJson, 0, false, true, false, $sImageFilePath],
"image file modified" => [$sInitialThemeParamJson, 1, false, false, true, $sImageFilePath, $sImageMainCssPath],
];
}
/**
* @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 testCompileThemes($ThemeParametersJson, $iCompileCSSFromSASSCount, $bMissingFile=false, $bFilesTouchedRecently=false, $bFileMd5sumModified=false, $sFileToTest=null, $sExpectedMainCssPath=null, $bSetup=true)
{
$sAfterReplacementCssVariableMd5sum='';
if (is_file($this->sTmpDir.'/'.$sFileToTest))
{
$sFileToTest=$this->sTmpDir.'/'.$sFileToTest;
}
else
{
$sFileToTest=APPROOT.'/'.$sFileToTest;
}
//copy images in test dir
$sAbsoluteImagePath = APPROOT .'test/application/theme-handler/copied/testimages/';
$this->recurseMkdir($sAbsoluteImagePath);
$aDirsToCleanup[] = dirname($sAbsoluteImagePath);
$this->recurse_copy(APPROOT .'test/application/theme-handler/expected/testimages/', $sAbsoluteImagePath);
//change approot-relative in css-variable to use absolute path
$sCssVarPath = $this->sTmpDir."/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)
{
$sAfterReplacementCssVariableMd5sum = $sBeforeReplacementCssVariableMd5sum;
unlink($sFileToTest);
}
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->sTmpDir . '/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->oCompileCSSServiceMock->expects($this->exactly($iCompileCSSFromSASSCount))
->method("CompileCSSFromSASS")
->willReturn("====CSSCOMPILEDCONTENT====");
$aThemeParameters = json_decode($ThemeParametersJson, true);
$this->assertEquals($iCompileCSSFromSASSCount!=0,ThemeHandler::CompileTheme('basque-red', $bSetup, "COMPILATIONTIMESTAMP", $aThemeParameters, [$this->sTmpDir.'/branding/themes/'], $this->sTmpDir));
if ($iCompileCSSFromSASSCount==1)
{
$sExpectedMainCssFile = APPROOT.$sExpectedMainCssPath;
if (!is_file($sExpectedMainCssFile))
{
$this->assertTrue(false, "Cannot find expected main css file $sExpectedMainCssFile");
}
$aPatterns = [static::PATTERN, '/'.$sBeforeReplacementCssVariableMd5sum.'/'];
$aPatterns[] = "/8100523d2e76a70266f3e7110e2fe5fb/";
$aReplacements = [$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);
echo (ThemeHandler::GetSignature($sActualCssFile));
$this->assertEquals($aExpectedJson, $aActualJson, "CSS file dont match ($sExpectedCssFile / $sActualCssFile)");
}
$this->assertTrue(true);
}
/**
* @param $sScssFile
*
* @dataProvider GetAllUrlFromScssProvider
*/
public function testGetAllUrlFromScss($sScssFile)
{
$aIncludedUrls = ThemeHandler::GetAllUrlFromScss(['attr' => "123"],APPROOT.$sScssFile);
$this->assertEquals(['approot-relative', 'version', 'version1'], array_values($aIncludedUrls['aMissingVariables']));
$this->assertEquals(["attr"=>"123"],
$aIncludedUrls['aFoundVariables']);
$aExpectedCompletedUrls = [
'css/ui-lightness/images/tutu.jpg',
"css/ui-lightness/images/tata.jpeg",
"css/ui-lightness/images/tete.jpeg?g=123"
];
$aExpectedToCompleteUrls = [
'\'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 ['test-getimages.scss' => ['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 = ['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(['gabu' => 'zomeu'], $aMissingVariables, ["a" => "b"], $sContent);
$aExpectedFoundVariables = [
'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(['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 [
'XXX + $key1 UNresolved' => ["abc/'+ \$key1", ['key'=>'123'], false],
'$key1 + XXX UNresolved' => ["\$key1 + abs", ['key'=>'123'], false],
'XXX + $key UNresolved' => ["abc/'+ \$unknownkey", ['key'=>'123'], false],
'XXX + $key resolved' => ["abc/'+ \$key", ['key'=>'123'], "abc/123"],
'XXX + $key1 resolved' => ["abc/'+ \$key1", ['key1'=>'123'], "abc/123"],
'$key + XXX resolved' => ["\$key + \"/abc", ['key'=>'123'], "123/abc"],
'XXX + $key + YYY resolved' => ["abc/'+ \$key + '/def", ['key'=>'123'], "abc/123/def"],
];
}
public function testGetIncludedImages()
{
$aStylesheetFile=glob($this->sTmpDir."/branding/css/*.scss");
$aStylesheetFile[]=$this->sTmpDir."/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);
//simulate adding timestamp
$aThemeParametersVariables['variables']['$version'] = microtime(true);
$aIncludedImages = ThemeHandler::GetIncludedImages($aThemeParametersVariables['variables'], $aStylesheetFile, "basque-red");
$aExpectedUris = json_decode(file_get_contents(APPROOT.'test/application/theme-handler/getimages/expected-getimages.json'), true);
$aExpectedImages = [];
foreach ($aExpectedUris as $sExpectedUri)
{
$aExpectedImages[] = APPROOT . $sExpectedUri;
}
$this->assertEquals($aExpectedImages, $aIncludedImages);
}
/**
* @param $sPath
* @param $sExpectedCanonicalPath
* @dataProvider CanonicalizePathProvider
*/
public function testCanonicalizePath($sExpectedCanonicalPath, $sPath)
{
$this->assertEquals($sExpectedCanonicalPath, ThemeHandler::CanonicalizePath($sPath), "Failed to reduce path $sPath");
}
public function CanonicalizePathProvider()
{
return [
[ '/var/www/html/iTop/images/itop-logo-2.png', '/var/www/html/iTop/env-production/branding/themes/light-grey/../../../../images/itop-logo-2.png' ],
[ '/var/www/html/iTop/env-production/branding/themes/light-grey/images/', '/var/www/html/iTop/env-production/branding/themes/light-grey/images/' ],
[ '/var/www/html/iTop/css/ui-lightness/images/ui-icons_222222_256x240.png', '/var/www/html/iTop/env-production//branding/themes/light-grey//../../../../css/ui-lightness/images/ui-icons_222222_256x240.png' ]
];
}
}

View File

@@ -0,0 +1,123 @@
/*!
* Copyright (C) 2013-2020 Combodo SARL
*
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
*/
$approot-relative: "../../../../" !default; // relative to env-***/branding/themes/***/main.css
// Base colors
$gray-base: #000 !default;
$gray-darker: lighten($gray-base, 13.5%) !default; // #222
$gray-dark: #444 !default;
$gray: #777 !default;
$gray-light: #808080 !default;
$gray-lighter: #ddd !default;
$gray-extra-light: #F1F1F1 !default;
$white: #FFFFFF !default;
$combodo-orange: #EA7D1E !default;
$combodo-dark-gray: #585653 !default;
$combodo-orange-dark: darken($combodo-orange, 13.8%) !default;
$combodo-orange-darker: darken($combodo-orange, 18%) !default;
$combodo-dark-gray-dark: darken($combodo-dark-gray, 13.5%) !default;
$combodo-dark-gray-darker: darken($combodo-dark-gray, 18%) !default;
// Brand colors
// - Bases
$brand-primary: $combodo-orange !default;
$brand-secondary: $combodo-dark-gray !default;
// - Shades
$brand-primary-lightest: lighten($brand-primary, 15%) !default;
$brand-primary-lighter: lighten($brand-primary, 10%) !default;
$brand-primary-light: lighten($brand-primary, 6%) !default;
$brand-primary-dark: darken($brand-primary, 6%) !default;
$brand-primary-darker: darken($brand-primary, 10%) !default;
$brand-primary-darkest: darken($brand-primary, 15%) !default;
$brand-secondary-lightest: lighten($brand-secondary, 15%) !default;
$brand-secondary-lighter: lighten($brand-secondary, 10%) !default;
$brand-secondary-light: lighten($brand-secondary, 6%) !default;
$brand-secondary-dark: darken($brand-secondary, 6%) !default;
$brand-secondary-darker: darken($brand-secondary, 10%) !default;
$brand-secondary-darkest: darken($brand-secondary, 15%) !default;
// Vars
$highlight-color: $brand-primary !default;
$grey-color: #555555 !default;
$complement-color: #1c94c4 !default;
$complement-light: #d6e8ef !default;
$frame-background-color: $gray-extra-light !default;
$text-color: #000 !default;
$box-radius: 0px !default;
$box-shadow-regular: 0 1px 1px rgba(0, 0, 0, 0.15) !default;
$body-background-color : $white !default;
$hyperlink-color: $complement-color !default;
$hyperlink-text-decoration: none !default;
////////////
// Search //
$search-form-container-color: $white !default;
$search-form-container-bg-color: $complement-color !default;
//
$search-criteria-box-color: #2D2D2D !default;
$search-criteria-box-picto-color: $brand-primary !default;
$search-criteria-box-bg-color: #EEEEEE !default;
$search-criteria-box-hover-color: $white !default;
$search-criteria-box-border-color: #CCCCCC !default;
$search-criteria-box-border: 1px solid $search-criteria-box-border-color !default;
$search-criteria-box-radius: 1px !default;
$search-criteria-more-less-details-color: #3F7294 !default;
//
$search-add-criteria-box-color: $search-criteria-box-color !default;
$search-add-criteria-box-bg-color: $white !default;
$search-add-criteria-box-hover-color: $gray-extra-light !default;
//
$search-button-box-color: $brand-primary !default;
$search-button-box-bg-color: $white !default;
$search-button-box-bg-hover-color: $gray-extra-light !default;
/////////////
// Buttons //
/////////////
// Toggle button
$toggle-button-bg-color: #CCCCCC !default;
$toggle-button-bg-checked-color: $brand-primary !default;
$toggle-button-slider-bg-color: #FFFFFF !default;
// Console elements
$summary-details-background: $grey-color !default;
$main-header-background: $frame-background-color !default;
$table-even-background: $frame-background-color !default;
$table-hover-background: #fdf5d0 !default;
$popup-menu-highlight-color: $highlight-color !default;
$popup-menu-text-color: #000 !default;
$popup-menu-background-color: #fff !default;
$popup-menu-text-higlight-color: #fff !default;
$breadcrumb-color: $grey-color !default;
$breadcrumb-highlight-color: $highlight-color !default;
// jQuery UI widgets vars
$primary-text-color: #333333 !default;
$secondary-text-color: $grey-color !default;
$error-text-color: $white !default;
$highlight-text-color: #363636 !default;
$hover-background-color: #fde17c !default;
$border-highlight-color: $brand-primary-dark !default;
$highlight-item-color: $white !default;
$content-color: #eeeeee !default;
$default-font-family: Trebuchet MS,Tahoma,Verdana,Arial,sans-serif !default;
$icons-filter: hue-rotate(0deg) !default;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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

@@ -0,0 +1,6 @@
/*
=== SIGNATURE BEGIN ===
{"variables":"37c31105548fce44fecca5cb34e455c9","stylesheets":{"css-variables":"3c3f5adf98b9dbf893658314436c4b93","jqueryui":"78cfafc3524dac98e61fc2460918d4e5","main":"52d8a7c5530ceb3a4d777364fa4e1eea"},"imports":[],"images":{"css\/ui-lightness\/images\/ui-icons_222222_256x240.png":"3a3c5468f484f07ac4a320d9e22acb8c","css\/ui-lightness\/images\/ui-bg_diagonals-thick_20_666666_40x40.png":"4429d568c67d8dfeb9040273ea0fb8c4","css\/ui-lightness\/images\/ui-icons_E87C1E_256x240.png":"7003dd36cb2aa032c8ec871ce4d4e03d","css\/ui-lightness\/images\/ui-icons_1c94c4_256x240.png":"dbd693dc8e0ef04e90a2f7ac7b390086","css\/ui-lightness\/images\/ui-icons_F26522_256x240.png":"16278ec0c07270be571f4c2e97fcc10c","css\/ui-lightness\/images\/ui-bg_diagonals-thick_18_b81900_40x40.png":"e460a66d4b3e093fc651e62a236267cb","css\/ui-lightness\/images\/ui-icons_ffffff_256x240.png":"41612b0f4a034424f8321c9f824a94da","css\/ui-lightness\/images\/ui-icons_ffd27a_256x240.png":"dda1b6f694b0d196aefc66a1d6d758f6","images\/actions_right.png":"31c8906bd25d27b83a0a2466bf903462","images\/ac-background.gif":"76135f3697b41a15aed787cfd77776c7","images\/green-square.gif":"16ea9a497d72f5e66e4e8ea9ae08024e","images\/tv-item.gif":"719fe2d4566108e73162fb8868d3778c","images\/tv-collapsable.gif":"63a3351ea0d580797c9b8c386aa4f48b","images\/tv-expandable.gif":"a2d1af4128e4a798a7f3390b12a28574","images\/tv-item-last.gif":"2ae7e1d9972ce71e5caa65a086bc5b7e","images\/tv-collapsable-last.gif":"71acaa9d7c2616e9e8b7131a75ca65da","images\/tv-expandable-last.gif":"9d51036b3a8102742709da66789fd0f7","images\/red-header.gif":"c73b8765f0c8c3c183cb6a0c2bb0ec69","images\/green-header.gif":"0e22a09bb8051b2a274b3427ede62e82","images\/orange-header.gif":"ce1f93f0af64431771b4cbd6c99c567b","images\/calendar.png":"ab56e59af3c96ca661821257d376465e","images\/truncated.png":"c6f91108afe8159d417b4dc556cd3b2a","images\/plus.gif":"f00e1e6e1161f48608bb2bbc79b9948c","images\/minus.gif":"6d77c0c0c2f86b6995d1cdf78274eaab","images\/full-screen.png":"b541fadd3f1563856a4b44aeebd9d563","images\/indicator.gif":"03ce3dcc84af110e9da8699a841e5200","images\/delete.png":"93c047549c31a270a037840277cf59d3","images\/bg.gif":"a315146ab814c73632480136576cd271","images\/desc.gif":"0f58b33929095ea17795dd53bbced5d9","images\/info-mini.png":"445c090ed777c5e6a08ac390fa896193","images\/ok.png":"f6973773335fd83d8d2875f9a3c925af","images\/error.png":"1af8a1041016f67669c5fd22dc88c82e","images\/eye-open-555.png":"9940f4e5b1248042c238e1924359fd5e","images\/eye-closed-555.png":"6ad3b0bae791bf61addc9d8ca80a642d","images\/eye-open-fff.png":"b7db2402d4d5c72314c25790a66150d4","images\/eye-closed-fff.png":"f9be7454dbb47b0e0bca3aa370ae7db5","images\/breadcrumb-separator.png":"1e7e50a8f573e230cf1e0f0399c516e8"}}
=== SIGNATURE END ===
*/
====CSSCOMPILEDCONTENT====

View File

@@ -0,0 +1,6 @@
/*
=== SIGNATURE BEGIN ===
{"variables":"37c31105548fce44fecca5cb34e455c9","stylesheets":{"css-variables":"3cfd1f47ff8d26ef555ba2bc861f89ed","jqueryui":"78cfafc3524dac98e61fc2460918d4e5","main":"52d8a7c5530ceb3a4d777364fa4e1eea"},"imports":[],"images":{"test\/application\/theme-handler\/copied\/testimages\/css\/ui-lightness\/images\/ui-icons_222222_256x240.png":"3a3c5468f484f07ac4a320d9e22acb8c","test\/application\/theme-handler\/copied\/testimages\/css\/ui-lightness\/images\/ui-bg_diagonals-thick_20_666666_40x40.png":"4429d568c67d8dfeb9040273ea0fb8c4","test\/application\/theme-handler\/copied\/testimages\/css\/ui-lightness\/images\/ui-icons_E87C1E_256x240.png":"7003dd36cb2aa032c8ec871ce4d4e03d","test\/application\/theme-handler\/copied\/testimages\/css\/ui-lightness\/images\/ui-icons_1c94c4_256x240.png":"dbd693dc8e0ef04e90a2f7ac7b390086","test\/application\/theme-handler\/copied\/testimages\/css\/ui-lightness\/images\/ui-icons_F26522_256x240.png":"16278ec0c07270be571f4c2e97fcc10c","test\/application\/theme-handler\/copied\/testimages\/css\/ui-lightness\/images\/ui-bg_diagonals-thick_18_b81900_40x40.png":"e460a66d4b3e093fc651e62a236267cb","test\/application\/theme-handler\/copied\/testimages\/css\/ui-lightness\/images\/ui-icons_ffffff_256x240.png":"41612b0f4a034424f8321c9f824a94da","test\/application\/theme-handler\/copied\/testimages\/css\/ui-lightness\/images\/ui-icons_ffd27a_256x240.png":"dda1b6f694b0d196aefc66a1d6d758f6","test\/application\/theme-handler\/copied\/testimages\/images\/actions_right.png":"31c8906bd25d27b83a0a2466bf903462","test\/application\/theme-handler\/copied\/testimages\/images\/ac-background.gif":"76135f3697b41a15aed787cfd77776c7","test\/application\/theme-handler\/copied\/testimages\/images\/green-square.gif":"16ea9a497d72f5e66e4e8ea9ae08024e","test\/application\/theme-handler\/copied\/testimages\/images\/tv-item.gif":"719fe2d4566108e73162fb8868d3778c","test\/application\/theme-handler\/copied\/testimages\/images\/tv-collapsable.gif":"63a3351ea0d580797c9b8c386aa4f48b","test\/application\/theme-handler\/copied\/testimages\/images\/tv-expandable.gif":"a2d1af4128e4a798a7f3390b12a28574","test\/application\/theme-handler\/copied\/testimages\/images\/tv-item-last.gif":"2ae7e1d9972ce71e5caa65a086bc5b7e","test\/application\/theme-handler\/copied\/testimages\/images\/tv-collapsable-last.gif":"71acaa9d7c2616e9e8b7131a75ca65da","test\/application\/theme-handler\/copied\/testimages\/images\/tv-expandable-last.gif":"9d51036b3a8102742709da66789fd0f7","test\/application\/theme-handler\/copied\/testimages\/images\/red-header.gif":"c73b8765f0c8c3c183cb6a0c2bb0ec69","test\/application\/theme-handler\/copied\/testimages\/images\/green-header.gif":"06886d405efe86b85023ef64c4349095","test\/application\/theme-handler\/copied\/testimages\/images\/orange-header.gif":"ce1f93f0af64431771b4cbd6c99c567b","test\/application\/theme-handler\/copied\/testimages\/images\/calendar.png":"ab56e59af3c96ca661821257d376465e","test\/application\/theme-handler\/copied\/testimages\/images\/truncated.png":"c6f91108afe8159d417b4dc556cd3b2a","test\/application\/theme-handler\/copied\/testimages\/images\/plus.gif":"f00e1e6e1161f48608bb2bbc79b9948c","test\/application\/theme-handler\/copied\/testimages\/images\/minus.gif":"6d77c0c0c2f86b6995d1cdf78274eaab","test\/application\/theme-handler\/copied\/testimages\/images\/full-screen.png":"b541fadd3f1563856a4b44aeebd9d563","test\/application\/theme-handler\/copied\/testimages\/images\/indicator.gif":"03ce3dcc84af110e9da8699a841e5200","test\/application\/theme-handler\/copied\/testimages\/images\/delete.png":"93c047549c31a270a037840277cf59d3","test\/application\/theme-handler\/copied\/testimages\/images\/bg.gif":"a315146ab814c73632480136576cd271","test\/application\/theme-handler\/copied\/testimages\/images\/desc.gif":"0f58b33929095ea17795dd53bbced5d9","test\/application\/theme-handler\/copied\/testimages\/images\/info-mini.png":"445c090ed777c5e6a08ac390fa896193","test\/application\/theme-handler\/copied\/testimages\/images\/ok.png":"f6973773335fd83d8d2875f9a3c925af","test\/application\/theme-handler\/copied\/testimages\/images\/error.png":"1af8a1041016f67669c5fd22dc88c82e","test\/application\/theme-handler\/copied\/testimages\/images\/eye-open-555.png":"9940f4e5b1248042c238e1924359fd5e","test\/application\/theme-handler\/copied\/testimages\/images\/eye-closed-555.png":"6ad3b0bae791bf61addc9d8ca80a642d","test\/application\/theme-handler\/copied\/testimages\/images\/eye-open-fff.png":"b7db2402d4d5c72314c25790a66150d4","test\/application\/theme-handler\/copied\/testimages\/images\/eye-closed-fff.png":"f9be7454dbb47b0e0bca3aa370ae7db5","test\/application\/theme-handler\/copied\/testimages\/images\/breadcrumb-separator.png":"1e7e50a8f573e230cf1e0f0399c516e8"}}
=== SIGNATURE END ===
*/
====CSSCOMPILEDCONTENT====

View File

@@ -0,0 +1,6 @@
/*
=== SIGNATURE BEGIN ===
{"variables":"37c31105548fce44fecca5cb34e455c9","stylesheets":{"css-variables":"8811855899754d05cfd472f839d24342","jqueryui":"78cfafc3524dac98e61fc2460918d4e5","main":"52d8a7c5530ceb3a4d777364fa4e1eea"},"imports":[],"images":{"test\/application\/theme-handler\/copied\/testimages\/css\/ui-lightness\/images\/ui-icons_222222_256x240.png":"3a3c5468f484f07ac4a320d9e22acb8c","test\/application\/theme-handler\/copied\/testimages\/css\/ui-lightness\/images\/ui-bg_diagonals-thick_20_666666_40x40.png":"4429d568c67d8dfeb9040273ea0fb8c4","test\/application\/theme-handler\/copied\/testimages\/css\/ui-lightness\/images\/ui-icons_E87C1E_256x240.png":"7003dd36cb2aa032c8ec871ce4d4e03d","test\/application\/theme-handler\/copied\/testimages\/css\/ui-lightness\/images\/ui-icons_1c94c4_256x240.png":"dbd693dc8e0ef04e90a2f7ac7b390086","test\/application\/theme-handler\/copied\/testimages\/css\/ui-lightness\/images\/ui-icons_F26522_256x240.png":"16278ec0c07270be571f4c2e97fcc10c","test\/application\/theme-handler\/copied\/testimages\/css\/ui-lightness\/images\/ui-bg_diagonals-thick_18_b81900_40x40.png":"e460a66d4b3e093fc651e62a236267cb","test\/application\/theme-handler\/copied\/testimages\/css\/ui-lightness\/images\/ui-icons_ffffff_256x240.png":"41612b0f4a034424f8321c9f824a94da","test\/application\/theme-handler\/copied\/testimages\/css\/ui-lightness\/images\/ui-icons_ffd27a_256x240.png":"dda1b6f694b0d196aefc66a1d6d758f6","test\/application\/theme-handler\/copied\/testimages\/images\/actions_right.png":"31c8906bd25d27b83a0a2466bf903462","test\/application\/theme-handler\/copied\/testimages\/images\/ac-background.gif":"76135f3697b41a15aed787cfd77776c7","test\/application\/theme-handler\/copied\/testimages\/images\/green-square.gif":"16ea9a497d72f5e66e4e8ea9ae08024e","test\/application\/theme-handler\/copied\/testimages\/images\/tv-item.gif":"719fe2d4566108e73162fb8868d3778c","test\/application\/theme-handler\/copied\/testimages\/images\/tv-collapsable.gif":"63a3351ea0d580797c9b8c386aa4f48b","test\/application\/theme-handler\/copied\/testimages\/images\/tv-expandable.gif":"a2d1af4128e4a798a7f3390b12a28574","test\/application\/theme-handler\/copied\/testimages\/images\/tv-item-last.gif":"2ae7e1d9972ce71e5caa65a086bc5b7e","test\/application\/theme-handler\/copied\/testimages\/images\/tv-collapsable-last.gif":"71acaa9d7c2616e9e8b7131a75ca65da","test\/application\/theme-handler\/copied\/testimages\/images\/tv-expandable-last.gif":"9d51036b3a8102742709da66789fd0f7","test\/application\/theme-handler\/copied\/testimages\/images\/red-header.gif":"c73b8765f0c8c3c183cb6a0c2bb0ec69","test\/application\/theme-handler\/copied\/testimages\/images\/green-header.gif":"0e22a09bb8051b2a274b3427ede62e82","test\/application\/theme-handler\/copied\/testimages\/images\/orange-header.gif":"ce1f93f0af64431771b4cbd6c99c567b","test\/application\/theme-handler\/copied\/testimages\/images\/calendar.png":"ab56e59af3c96ca661821257d376465e","test\/application\/theme-handler\/copied\/testimages\/images\/truncated.png":"c6f91108afe8159d417b4dc556cd3b2a","test\/application\/theme-handler\/copied\/testimages\/images\/plus.gif":"f00e1e6e1161f48608bb2bbc79b9948c","test\/application\/theme-handler\/copied\/testimages\/images\/minus.gif":"6d77c0c0c2f86b6995d1cdf78274eaab","test\/application\/theme-handler\/copied\/testimages\/images\/full-screen.png":"b541fadd3f1563856a4b44aeebd9d563","test\/application\/theme-handler\/copied\/testimages\/images\/indicator.gif":"03ce3dcc84af110e9da8699a841e5200","test\/application\/theme-handler\/copied\/testimages\/images\/delete.png":"93c047549c31a270a037840277cf59d3","test\/application\/theme-handler\/copied\/testimages\/images\/bg.gif":"a315146ab814c73632480136576cd271","test\/application\/theme-handler\/copied\/testimages\/images\/desc.gif":"0f58b33929095ea17795dd53bbced5d9","test\/application\/theme-handler\/copied\/testimages\/images\/info-mini.png":"445c090ed777c5e6a08ac390fa896193","test\/application\/theme-handler\/copied\/testimages\/images\/ok.png":"f6973773335fd83d8d2875f9a3c925af","test\/application\/theme-handler\/copied\/testimages\/images\/error.png":"1af8a1041016f67669c5fd22dc88c82e","test\/application\/theme-handler\/copied\/testimages\/images\/eye-open-555.png":"9940f4e5b1248042c238e1924359fd5e","test\/application\/theme-handler\/copied\/testimages\/images\/eye-closed-555.png":"6ad3b0bae791bf61addc9d8ca80a642d","test\/application\/theme-handler\/copied\/testimages\/images\/eye-open-fff.png":"b7db2402d4d5c72314c25790a66150d4","test\/application\/theme-handler\/copied\/testimages\/images\/eye-closed-fff.png":"f9be7454dbb47b0e0bca3aa370ae7db5","test\/application\/theme-handler\/copied\/testimages\/images\/breadcrumb-separator.png":"1e7e50a8f573e230cf1e0f0399c516e8"}}
=== SIGNATURE END ===
*/
====CSSCOMPILEDCONTENT====

View File

@@ -0,0 +1,6 @@
/*
=== SIGNATURE BEGIN ===
{"variables":"37c31105548fce44fecca5cb34e455c9","stylesheets":{"css-variables":"3cfd1f47ff8d26ef555ba2bc861f89ed","jqueryui":"78cfafc3524dac98e61fc2460918d4e5","main":"63ba7dfe2a2eba40c2596ebb2a405f0b"},"imports":[],"images":{"test\/application\/theme-handler\/copied\/testimages\/css\/ui-lightness\/images\/ui-icons_222222_256x240.png":"3a3c5468f484f07ac4a320d9e22acb8c","test\/application\/theme-handler\/copied\/testimages\/css\/ui-lightness\/images\/ui-bg_diagonals-thick_20_666666_40x40.png":"4429d568c67d8dfeb9040273ea0fb8c4","test\/application\/theme-handler\/copied\/testimages\/css\/ui-lightness\/images\/ui-icons_E87C1E_256x240.png":"7003dd36cb2aa032c8ec871ce4d4e03d","test\/application\/theme-handler\/copied\/testimages\/css\/ui-lightness\/images\/ui-icons_1c94c4_256x240.png":"dbd693dc8e0ef04e90a2f7ac7b390086","test\/application\/theme-handler\/copied\/testimages\/css\/ui-lightness\/images\/ui-icons_F26522_256x240.png":"16278ec0c07270be571f4c2e97fcc10c","test\/application\/theme-handler\/copied\/testimages\/css\/ui-lightness\/images\/ui-bg_diagonals-thick_18_b81900_40x40.png":"e460a66d4b3e093fc651e62a236267cb","test\/application\/theme-handler\/copied\/testimages\/css\/ui-lightness\/images\/ui-icons_ffffff_256x240.png":"41612b0f4a034424f8321c9f824a94da","test\/application\/theme-handler\/copied\/testimages\/css\/ui-lightness\/images\/ui-icons_ffd27a_256x240.png":"dda1b6f694b0d196aefc66a1d6d758f6","test\/application\/theme-handler\/copied\/testimages\/images\/actions_right.png":"31c8906bd25d27b83a0a2466bf903462","test\/application\/theme-handler\/copied\/testimages\/images\/ac-background.gif":"76135f3697b41a15aed787cfd77776c7","test\/application\/theme-handler\/copied\/testimages\/images\/green-square.gif":"16ea9a497d72f5e66e4e8ea9ae08024e","test\/application\/theme-handler\/copied\/testimages\/images\/tv-item.gif":"719fe2d4566108e73162fb8868d3778c","test\/application\/theme-handler\/copied\/testimages\/images\/tv-collapsable.gif":"63a3351ea0d580797c9b8c386aa4f48b","test\/application\/theme-handler\/copied\/testimages\/images\/tv-expandable.gif":"a2d1af4128e4a798a7f3390b12a28574","test\/application\/theme-handler\/copied\/testimages\/images\/tv-item-last.gif":"2ae7e1d9972ce71e5caa65a086bc5b7e","test\/application\/theme-handler\/copied\/testimages\/images\/tv-collapsable-last.gif":"71acaa9d7c2616e9e8b7131a75ca65da","test\/application\/theme-handler\/copied\/testimages\/images\/tv-expandable-last.gif":"9d51036b3a8102742709da66789fd0f7","test\/application\/theme-handler\/copied\/testimages\/images\/red-header.gif":"c73b8765f0c8c3c183cb6a0c2bb0ec69","test\/application\/theme-handler\/copied\/testimages\/images\/green-header.gif":"0e22a09bb8051b2a274b3427ede62e82","test\/application\/theme-handler\/copied\/testimages\/images\/orange-header.gif":"ce1f93f0af64431771b4cbd6c99c567b","test\/application\/theme-handler\/copied\/testimages\/images\/calendar.png":"ab56e59af3c96ca661821257d376465e","test\/application\/theme-handler\/copied\/testimages\/images\/truncated.png":"c6f91108afe8159d417b4dc556cd3b2a","test\/application\/theme-handler\/copied\/testimages\/images\/plus.gif":"f00e1e6e1161f48608bb2bbc79b9948c","test\/application\/theme-handler\/copied\/testimages\/images\/minus.gif":"6d77c0c0c2f86b6995d1cdf78274eaab","test\/application\/theme-handler\/copied\/testimages\/images\/full-screen.png":"b541fadd3f1563856a4b44aeebd9d563","test\/application\/theme-handler\/copied\/testimages\/images\/indicator.gif":"03ce3dcc84af110e9da8699a841e5200","test\/application\/theme-handler\/copied\/testimages\/images\/delete.png":"93c047549c31a270a037840277cf59d3","test\/application\/theme-handler\/copied\/testimages\/images\/bg.gif":"a315146ab814c73632480136576cd271","test\/application\/theme-handler\/copied\/testimages\/images\/desc.gif":"0f58b33929095ea17795dd53bbced5d9","test\/application\/theme-handler\/copied\/testimages\/images\/info-mini.png":"445c090ed777c5e6a08ac390fa896193","test\/application\/theme-handler\/copied\/testimages\/images\/ok.png":"f6973773335fd83d8d2875f9a3c925af","test\/application\/theme-handler\/copied\/testimages\/images\/error.png":"1af8a1041016f67669c5fd22dc88c82e","test\/application\/theme-handler\/copied\/testimages\/images\/eye-open-555.png":"9940f4e5b1248042c238e1924359fd5e","test\/application\/theme-handler\/copied\/testimages\/images\/eye-closed-555.png":"6ad3b0bae791bf61addc9d8ca80a642d","test\/application\/theme-handler\/copied\/testimages\/images\/eye-open-fff.png":"b7db2402d4d5c72314c25790a66150d4","test\/application\/theme-handler\/copied\/testimages\/images\/eye-closed-fff.png":"f9be7454dbb47b0e0bca3aa370ae7db5","test\/application\/theme-handler\/copied\/testimages\/images\/breadcrumb-separator.png":"1e7e50a8f573e230cf1e0f0399c516e8"}}
=== SIGNATURE END ===
*/
====CSSCOMPILEDCONTENT====

View File

@@ -0,0 +1,6 @@
/*
=== SIGNATURE BEGIN ===
{"variables":"37c31105548fce44fecca5cb34e455c9","stylesheets":{"css-variables":"MD5SUM","jqueryui":"78cfafc3524dac98e61fc2460918d4e5","main":"52d8a7c5530ceb3a4d777364fa4e1eea"},"imports":[],"images":{"test\/application\/theme-handler\/copied\/testimages\/css\/ui-lightness\/images\/ui-icons_222222_256x240.png":"3a3c5468f484f07ac4a320d9e22acb8c","test\/application\/theme-handler\/copied\/testimages\/css\/ui-lightness\/images\/ui-bg_diagonals-thick_20_666666_40x40.png":"4429d568c67d8dfeb9040273ea0fb8c4","test\/application\/theme-handler\/copied\/testimages\/css\/ui-lightness\/images\/ui-icons_E87C1E_256x240.png":"7003dd36cb2aa032c8ec871ce4d4e03d","test\/application\/theme-handler\/copied\/testimages\/css\/ui-lightness\/images\/ui-icons_1c94c4_256x240.png":"dbd693dc8e0ef04e90a2f7ac7b390086","test\/application\/theme-handler\/copied\/testimages\/css\/ui-lightness\/images\/ui-icons_F26522_256x240.png":"16278ec0c07270be571f4c2e97fcc10c","test\/application\/theme-handler\/copied\/testimages\/css\/ui-lightness\/images\/ui-bg_diagonals-thick_18_b81900_40x40.png":"e460a66d4b3e093fc651e62a236267cb","test\/application\/theme-handler\/copied\/testimages\/css\/ui-lightness\/images\/ui-icons_ffffff_256x240.png":"41612b0f4a034424f8321c9f824a94da","test\/application\/theme-handler\/copied\/testimages\/css\/ui-lightness\/images\/ui-icons_ffd27a_256x240.png":"dda1b6f694b0d196aefc66a1d6d758f6","test\/application\/theme-handler\/copied\/testimages\/images\/actions_right.png":"31c8906bd25d27b83a0a2466bf903462","test\/application\/theme-handler\/copied\/testimages\/images\/ac-background.gif":"76135f3697b41a15aed787cfd77776c7","test\/application\/theme-handler\/copied\/testimages\/images\/green-square.gif":"16ea9a497d72f5e66e4e8ea9ae08024e","test\/application\/theme-handler\/copied\/testimages\/images\/tv-item.gif":"719fe2d4566108e73162fb8868d3778c","test\/application\/theme-handler\/copied\/testimages\/images\/tv-collapsable.gif":"63a3351ea0d580797c9b8c386aa4f48b","test\/application\/theme-handler\/copied\/testimages\/images\/tv-expandable.gif":"a2d1af4128e4a798a7f3390b12a28574","test\/application\/theme-handler\/copied\/testimages\/images\/tv-item-last.gif":"2ae7e1d9972ce71e5caa65a086bc5b7e","test\/application\/theme-handler\/copied\/testimages\/images\/tv-collapsable-last.gif":"71acaa9d7c2616e9e8b7131a75ca65da","test\/application\/theme-handler\/copied\/testimages\/images\/tv-expandable-last.gif":"9d51036b3a8102742709da66789fd0f7","test\/application\/theme-handler\/copied\/testimages\/images\/red-header.gif":"c73b8765f0c8c3c183cb6a0c2bb0ec69","test\/application\/theme-handler\/copied\/testimages\/images\/green-header.gif":"0e22a09bb8051b2a274b3427ede62e82","test\/application\/theme-handler\/copied\/testimages\/images\/orange-header.gif":"ce1f93f0af64431771b4cbd6c99c567b","test\/application\/theme-handler\/copied\/testimages\/images\/calendar.png":"ab56e59af3c96ca661821257d376465e","test\/application\/theme-handler\/copied\/testimages\/images\/truncated.png":"c6f91108afe8159d417b4dc556cd3b2a","test\/application\/theme-handler\/copied\/testimages\/images\/plus.gif":"f00e1e6e1161f48608bb2bbc79b9948c","test\/application\/theme-handler\/copied\/testimages\/images\/minus.gif":"6d77c0c0c2f86b6995d1cdf78274eaab","test\/application\/theme-handler\/copied\/testimages\/images\/full-screen.png":"b541fadd3f1563856a4b44aeebd9d563","test\/application\/theme-handler\/copied\/testimages\/images\/indicator.gif":"03ce3dcc84af110e9da8699a841e5200","test\/application\/theme-handler\/copied\/testimages\/images\/delete.png":"93c047549c31a270a037840277cf59d3","test\/application\/theme-handler\/copied\/testimages\/images\/bg.gif":"a315146ab814c73632480136576cd271","test\/application\/theme-handler\/copied\/testimages\/images\/desc.gif":"0f58b33929095ea17795dd53bbced5d9","test\/application\/theme-handler\/copied\/testimages\/images\/info-mini.png":"445c090ed777c5e6a08ac390fa896193","test\/application\/theme-handler\/copied\/testimages\/images\/ok.png":"f6973773335fd83d8d2875f9a3c925af","test\/application\/theme-handler\/copied\/testimages\/images\/error.png":"1af8a1041016f67669c5fd22dc88c82e","test\/application\/theme-handler\/copied\/testimages\/images\/eye-open-555.png":"9940f4e5b1248042c238e1924359fd5e","test\/application\/theme-handler\/copied\/testimages\/images\/eye-closed-555.png":"6ad3b0bae791bf61addc9d8ca80a642d","test\/application\/theme-handler\/copied\/testimages\/images\/eye-open-fff.png":"b7db2402d4d5c72314c25790a66150d4","test\/application\/theme-handler\/copied\/testimages\/images\/eye-closed-fff.png":"f9be7454dbb47b0e0bca3aa370ae7db5","test\/application\/theme-handler\/copied\/testimages\/images\/breadcrumb-separator.png":"1e7e50a8f573e230cf1e0f0399c516e8"}}
=== SIGNATURE END ===
*/
====CSSCOMPILEDCONTENT====

View File

@@ -0,0 +1,6 @@
/*
=== SIGNATURE BEGIN ===
{"variables":"8100523d2e76a70266f3e7110e2fe5fb","stylesheets":{"css-variables":"3cfd1f47ff8d26ef555ba2bc861f89ed","jqueryui":"78cfafc3524dac98e61fc2460918d4e5","main":"52d8a7c5530ceb3a4d777364fa4e1eea"},"imports":[],"images":{"test\/application\/theme-handler\/copied\/testimages\/css\/ui-lightness\/images\/ui-icons_222222_256x240.png":"3a3c5468f484f07ac4a320d9e22acb8c","test\/application\/theme-handler\/copied\/testimages\/css\/ui-lightness\/images\/ui-bg_diagonals-thick_20_666666_40x40.png":"4429d568c67d8dfeb9040273ea0fb8c4","test\/application\/theme-handler\/copied\/testimages\/css\/ui-lightness\/images\/ui-icons_E87C1E_256x240.png":"7003dd36cb2aa032c8ec871ce4d4e03d","test\/application\/theme-handler\/copied\/testimages\/css\/ui-lightness\/images\/ui-icons_1c94c4_256x240.png":"dbd693dc8e0ef04e90a2f7ac7b390086","test\/application\/theme-handler\/copied\/testimages\/css\/ui-lightness\/images\/ui-icons_F26522_256x240.png":"16278ec0c07270be571f4c2e97fcc10c","test\/application\/theme-handler\/copied\/testimages\/css\/ui-lightness\/images\/ui-bg_diagonals-thick_18_b81900_40x40.png":"e460a66d4b3e093fc651e62a236267cb","test\/application\/theme-handler\/copied\/testimages\/css\/ui-lightness\/images\/ui-icons_ffffff_256x240.png":"41612b0f4a034424f8321c9f824a94da","test\/application\/theme-handler\/copied\/testimages\/css\/ui-lightness\/images\/ui-icons_ffd27a_256x240.png":"dda1b6f694b0d196aefc66a1d6d758f6","test\/application\/theme-handler\/copied\/testimages\/images\/actions_right.png":"31c8906bd25d27b83a0a2466bf903462","test\/application\/theme-handler\/copied\/testimages\/images\/ac-background.gif":"76135f3697b41a15aed787cfd77776c7","test\/application\/theme-handler\/copied\/testimages\/images\/green-square.gif":"16ea9a497d72f5e66e4e8ea9ae08024e","test\/application\/theme-handler\/copied\/testimages\/images\/tv-item.gif":"719fe2d4566108e73162fb8868d3778c","test\/application\/theme-handler\/copied\/testimages\/images\/tv-collapsable.gif":"63a3351ea0d580797c9b8c386aa4f48b","test\/application\/theme-handler\/copied\/testimages\/images\/tv-expandable.gif":"a2d1af4128e4a798a7f3390b12a28574","test\/application\/theme-handler\/copied\/testimages\/images\/tv-item-last.gif":"2ae7e1d9972ce71e5caa65a086bc5b7e","test\/application\/theme-handler\/copied\/testimages\/images\/tv-collapsable-last.gif":"71acaa9d7c2616e9e8b7131a75ca65da","test\/application\/theme-handler\/copied\/testimages\/images\/tv-expandable-last.gif":"9d51036b3a8102742709da66789fd0f7","test\/application\/theme-handler\/copied\/testimages\/images\/red-header.gif":"c73b8765f0c8c3c183cb6a0c2bb0ec69","test\/application\/theme-handler\/copied\/testimages\/images\/green-header.gif":"0e22a09bb8051b2a274b3427ede62e82","test\/application\/theme-handler\/copied\/testimages\/images\/orange-header.gif":"ce1f93f0af64431771b4cbd6c99c567b","test\/application\/theme-handler\/copied\/testimages\/images\/calendar.png":"ab56e59af3c96ca661821257d376465e","test\/application\/theme-handler\/copied\/testimages\/images\/truncated.png":"c6f91108afe8159d417b4dc556cd3b2a","test\/application\/theme-handler\/copied\/testimages\/images\/plus.gif":"f00e1e6e1161f48608bb2bbc79b9948c","test\/application\/theme-handler\/copied\/testimages\/images\/minus.gif":"6d77c0c0c2f86b6995d1cdf78274eaab","test\/application\/theme-handler\/copied\/testimages\/images\/full-screen.png":"b541fadd3f1563856a4b44aeebd9d563","test\/application\/theme-handler\/copied\/testimages\/images\/indicator.gif":"03ce3dcc84af110e9da8699a841e5200","test\/application\/theme-handler\/copied\/testimages\/images\/delete.png":"93c047549c31a270a037840277cf59d3","test\/application\/theme-handler\/copied\/testimages\/images\/bg.gif":"a315146ab814c73632480136576cd271","test\/application\/theme-handler\/copied\/testimages\/images\/desc.gif":"0f58b33929095ea17795dd53bbced5d9","test\/application\/theme-handler\/copied\/testimages\/images\/info-mini.png":"445c090ed777c5e6a08ac390fa896193","test\/application\/theme-handler\/copied\/testimages\/images\/ok.png":"f6973773335fd83d8d2875f9a3c925af","test\/application\/theme-handler\/copied\/testimages\/images\/error.png":"1af8a1041016f67669c5fd22dc88c82e","test\/application\/theme-handler\/copied\/testimages\/images\/eye-open-555.png":"9940f4e5b1248042c238e1924359fd5e","test\/application\/theme-handler\/copied\/testimages\/images\/eye-closed-555.png":"6ad3b0bae791bf61addc9d8ca80a642d","test\/application\/theme-handler\/copied\/testimages\/images\/eye-open-fff.png":"b7db2402d4d5c72314c25790a66150d4","test\/application\/theme-handler\/copied\/testimages\/images\/eye-closed-fff.png":"f9be7454dbb47b0e0bca3aa370ae7db5","test\/application\/theme-handler\/copied\/testimages\/images\/breadcrumb-separator.png":"1e7e50a8f573e230cf1e0f0399c516e8"}}
=== SIGNATURE END ===
*/
====CSSCOMPILEDCONTENT====

View File

@@ -0,0 +1 @@
{"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"}}

View File

@@ -0,0 +1,39 @@
[
"css/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png",
"css/ui-lightness/images/ui-icons_ffffff_256x240.png",
"images/actions_right.png",
"images/ac-background.gif",
"images/green-square.gif",
"images/tv-item.gif",
"images/tv-collapsable.gif",
"images/tv-expandable.gif",
"images/tv-item-last.gif",
"images/tv-collapsable-last.gif",
"images/tv-expandable-last.gif",
"images/red-header.gif",
"images/green-header.gif",
"images/orange-header.gif",
"images/calendar.png",
"images/truncated.png",
"images/plus.gif",
"images/minus.gif",
"images/full-screen.png",
"images/indicator.gif",
"images/delete.png",
"images/bg.gif",
"images/desc.gif",
"images/info-mini.png",
"images/ok.png",
"images/error.png",
"images/eye-open-555.png",
"images/eye-closed-555.png",
"images/eye-open-fff.png",
"images/eye-closed-fff.png",
"css/ui-lightness/images/ui-icons_222222_256x240.png",
"images/breadcrumb-separator.png",
"css/ui-lightness/images/ui-icons_E87C1E_256x240.png",
"css/ui-lightness/images/ui-icons_1c94c4_256x240.png",
"css/ui-lightness/images/ui-icons_F26522_256x240.png",
"css/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png",
"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);