Merge pull request #154 from Combodo/revert-136-feature/fast-theme-compilation
Revert "Faster compilation of themes"
@@ -25,6 +25,7 @@ require_once (__DIR__.DIRECTORY_SEPARATOR.'update.classes.inc.php');
|
||||
/** @var \FileVersionUpdater[] $aFilesUpdaters */
|
||||
$aFilesUpdaters = array(
|
||||
new iTopVersionFileUpdater(),
|
||||
new CssVariablesFileUpdater(),
|
||||
new DatamodelsModulesFiles(),
|
||||
);
|
||||
|
||||
|
||||
@@ -89,6 +89,26 @@ 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;
|
||||
|
||||
@@ -25,9 +25,6 @@
|
||||
*/
|
||||
class ThemeHandler
|
||||
{
|
||||
const IMAGE_EXTENSIONS = array('png', 'gif', 'jpg', 'jpeg');
|
||||
|
||||
private static $oCompileCSSService;
|
||||
/**
|
||||
* Return default theme name and parameters
|
||||
*
|
||||
@@ -111,7 +108,7 @@ class ThemeHandler
|
||||
SetupUtils::builddir($sDefaultThemeDirPath);
|
||||
}
|
||||
|
||||
static::CompileTheme($sThemeId, false, $aDefaultTheme['parameters']);
|
||||
static::CompileTheme($sThemeId, $aDefaultTheme['parameters']);
|
||||
}
|
||||
|
||||
// Return absolute url to theme compiled css
|
||||
@@ -119,29 +116,17 @@ class ThemeHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
* Compile the $sThemeId theme
|
||||
*
|
||||
* @param string $sThemeId
|
||||
* @param mix $sSetupCompilationTimestamp : when setup context this is compilation timestamp. otherwise false.
|
||||
* @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
|
||||
*/
|
||||
public static function CompileTheme($sThemeId, $sSetupCompilationTimestamp=false, $aThemeParameters = null, $aImportsPaths = null, $sWorkingPath = null)
|
||||
public static function CompileTheme($sThemeId, $aThemeParameters = null, $aImportsPaths = null, $sWorkingPath = null)
|
||||
{
|
||||
if ($sSetupCompilationTimestamp===false)
|
||||
{
|
||||
$sSetupCompilationTimestamp = microtime(true);
|
||||
$bSetup = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
$bSetup = true;
|
||||
}
|
||||
// Default working path
|
||||
if($sWorkingPath === null)
|
||||
{
|
||||
@@ -163,7 +148,6 @@ class ThemeHandler
|
||||
// Save parameters if passed... (typically during DM compilation)
|
||||
if(is_array($aThemeParameters))
|
||||
{
|
||||
$aThemeParameters = self::PrepareThemeParameterBeforeSavingAndCompiling($aThemeParameters, $sWorkingPath, $sThemeFolderPath, $sSetupCompilationTimestamp);
|
||||
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)
|
||||
@@ -180,626 +164,27 @@ 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;
|
||||
$iImportLastModified = @filemtime($sWorkingPath.$sImport);
|
||||
$iStyleLastModified = $iStyleLastModified < $iImportLastModified ? $iImportLastModified : $iStyleLastModified;
|
||||
}
|
||||
foreach ($aThemeParameters['stylesheets'] as $sStylesheet)
|
||||
{
|
||||
$sTmpThemeScssContent .= '@import "'.$sStylesheet.'";'."\n";
|
||||
|
||||
$sFile = static::FindStylesheetFile($sStylesheet, $aImportsPaths);
|
||||
$iStylesheetLastModified = @filemtime($sFile);
|
||||
$aStylesheetFiles[] = $sFile;
|
||||
$iStylesheetLastModified = @filemtime($sWorkingPath.$sStylesheet);
|
||||
$iStyleLastModified = $iStyleLastModified < $iStylesheetLastModified ? $iStylesheetLastModified : $iStyleLastModified;
|
||||
}
|
||||
|
||||
$aIncludedImages=static::GetIncludedImages($aThemeParameters['variables'], $aStylesheetFiles, $sThemeFolderPath, $aImportsPaths);
|
||||
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);
|
||||
$bVarSignatureChanged=false;
|
||||
if ($bFileExists && $bSetup)
|
||||
if (!file_exists($sThemeCssPath) || (is_writable($sThemeFolderPath) && (@filemtime($sThemeCssPath) < $iStyleLastModified)))
|
||||
{
|
||||
$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);
|
||||
}
|
||||
$sTmpThemeCssContent = utils::CompileCSSFromSASS($sTmpThemeScssContent, $aImportsPaths, $aThemeParameters['variables']);
|
||||
file_put_contents($sThemeCssPath, $sTmpThemeCssContent);
|
||||
}
|
||||
|
||||
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
|
||||
$aThemeParameters = self::PrepareThemeParameterBeforeSavingAndCompiling($aThemeParameters, $sWorkingPath, $sThemeFolderPath, $sSetupCompilationTimestamp);
|
||||
$sTmpThemeCssContent = static::$oCompileCSSService->CompileCSSFromSASS($sTmpThemeScssContent, $aImportsPaths,
|
||||
$aThemeParameters['variables']);
|
||||
file_put_contents($sThemeFolderPath.'/theme-parameters.json', json_encode($aThemeParameters));
|
||||
file_put_contents($sThemeCssPath, $sSignatureComment.$sTmpThemeCssContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the signature of a theme defined by its theme parameters. The signature is a JSON structure of
|
||||
* 1) one MD5 of all the variables/values (JSON encoded)
|
||||
* 2) the MD5 of each stylesheet file
|
||||
* 3) the MD5 of each import file
|
||||
*
|
||||
* @param string[] $aThemeParameters
|
||||
* @param string[] $aImportsPaths
|
||||
* @param string[] $aIncludedImages
|
||||
*
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
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)
|
||||
{
|
||||
$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))
|
||||
{
|
||||
$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, $aImportsPaths)
|
||||
{
|
||||
$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();
|
||||
$aAllImagesMap = false;
|
||||
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;
|
||||
}
|
||||
|
||||
$sFilePath=realpath($sThemeFolderPath.DIRECTORY_SEPARATOR.$sImg);
|
||||
if ($sFilePath!==false)
|
||||
{
|
||||
$aImages[$sImg]=$sFilePath;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($aAllImagesMap===false)
|
||||
{
|
||||
//$aAllImagesMap = static::GetAllImagesMap($aImportsPaths);
|
||||
$aAllImagesMap = static::ScanFolderImages(APPROOT);
|
||||
}
|
||||
|
||||
$sFileName = basename($sImg);
|
||||
if (array_key_exists($sFileName, $aAllImagesMap))
|
||||
{
|
||||
foreach ($aAllImagesMap[$sFileName] as $sFilePath)
|
||||
{
|
||||
echo "=========== \n $sUri \n => $sFilePath\n".strpos($sFilePath, $sUri)."\n";
|
||||
if (strpos($sFilePath, $sUri)!==false)
|
||||
{
|
||||
$aImages[$sImg]=$sFilePath;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo "!!!!!!!!!!!\n";
|
||||
}
|
||||
}
|
||||
|
||||
return array_values($aImages);
|
||||
}
|
||||
|
||||
public static function GetAllImagesMap($aImportsPaths)
|
||||
{
|
||||
$aImagesMap = [];
|
||||
foreach ($aImportsPaths as $sFolder)
|
||||
{
|
||||
if (is_dir($sFolder))
|
||||
{
|
||||
$aImagesMap = array_merge($aImagesMap, static::ScanFolderImages($sFolder));
|
||||
}
|
||||
}
|
||||
return $aImagesMap;
|
||||
}
|
||||
|
||||
|
||||
public static function ScanFolderImages($sFolder)
|
||||
{
|
||||
//var_dump($sFolder);
|
||||
$aImagesMap = [];
|
||||
foreach (scandir($sFolder) as $sFile)
|
||||
{
|
||||
if (in_array($sFile,array(".","..")))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
$sFilePath = $sFolder . DIRECTORY_SEPARATOR . $sFile;
|
||||
if (is_dir($sFilePath))
|
||||
{
|
||||
$aImagesMap = array_merge($aImagesMap, static::ScanFolderImages($sFilePath));
|
||||
}
|
||||
else if (static::HasImageExtension($sFilePath))
|
||||
{
|
||||
if (array_key_exists($sFile, $aImagesMap))
|
||||
{
|
||||
$aImagesMap[$sFile][] = $sFilePath;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aImagesMap[$sFile] = [ $sFilePath ] ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $aImagesMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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))
|
||||
{
|
||||
$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 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
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* main work is to store compilation timestamp as $version variable
|
||||
* that way each time there is compilation, images and others will be loaded again on client browser side (apart from cache)
|
||||
* @param $aThemeParameters
|
||||
* @param $sWorkingPath
|
||||
* @param $sThemeFolderPath
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function PrepareThemeParameterBeforeSavingAndCompiling($aThemeParameters, $sWorkingPath, $sThemeFolderPath, $bSetupCompilationTimestamp)
|
||||
{
|
||||
if (array_key_exists('variables', $aThemeParameters))
|
||||
{
|
||||
if (is_array($aThemeParameters['variables']))
|
||||
{
|
||||
$aThemeParameters['variables']['$version'] = $bSetupCompilationTimestamp;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$aThemeParameters['variables']['$version'] = $bSetupCompilationTimestamp;
|
||||
}
|
||||
|
||||
if (!is_dir($sThemeFolderPath))
|
||||
{
|
||||
mkdir($sWorkingPath.'/branding/');
|
||||
mkdir($sWorkingPath.'/branding/themes/');
|
||||
}
|
||||
|
||||
return $aThemeParameters;
|
||||
}
|
||||
}
|
||||
|
||||
class CompileCSSService
|
||||
{
|
||||
/**
|
||||
* CompileCSSService constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public function CompileCSSFromSASS($sSassContent, $aImportPaths = array(), $aVariables = array()){
|
||||
return utils::CompileCSSFromSASS($sSassContent, $aImportPaths, $aVariables);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1860,13 +1860,17 @@ 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)
|
||||
{
|
||||
return static::GetCacheBusterTimestamp();
|
||||
$aModulesInfo = GetModulesInfo();
|
||||
if (array_key_exists($sModuleName, $aModulesInfo))
|
||||
{
|
||||
return $aModulesInfo[$sModuleName]['version'];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,7 +16,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.8.0";
|
||||
$approot-relative: "../../../../../" !default; // relative to env-***/branding/themes/***/main.css
|
||||
|
||||
@@ -8384,7 +8384,6 @@
|
||||
<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>
|
||||
@@ -8400,7 +8399,6 @@
|
||||
<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>
|
||||
|
||||
@@ -2671,7 +2671,6 @@ EOF;
|
||||
'variables' => array(),
|
||||
'imports' => array(),
|
||||
'stylesheets' => array(),
|
||||
'precompiled_stylesheet' => '',
|
||||
);
|
||||
|
||||
/** @var \DOMNodeList $oVariables */
|
||||
@@ -2697,7 +2696,7 @@ EOF;
|
||||
$sStylesheetId = $oStylesheet->getAttribute('id');
|
||||
$aThemeParameters['stylesheets'][$sStylesheetId] = $oStylesheet->GetText();
|
||||
}
|
||||
$aThemeParameters['precompiled_stylesheet'] = $oTheme->GetChildText('precompiled_stylesheet', '');
|
||||
|
||||
$aThemes[$sThemeId] = $aThemeParameters;
|
||||
}
|
||||
|
||||
@@ -2709,7 +2708,6 @@ EOF;
|
||||
}
|
||||
|
||||
// Compile themes
|
||||
$fStart = microtime(true);
|
||||
foreach($aThemes as $sThemeId => $aThemeParameters)
|
||||
{
|
||||
$sThemeDir = $sThemesDir.$sThemeId;
|
||||
@@ -2717,21 +2715,10 @@ EOF;
|
||||
{
|
||||
SetupUtils::builddir($sThemeDir);
|
||||
}
|
||||
// Check if a precompiled version of the theme is supplied
|
||||
$sPrecompiledFile = $sTempTargetDir.$aThemeParameters['precompiled_stylesheet'];
|
||||
if (file_exists($sPrecompiledFile))
|
||||
{
|
||||
copy($sPrecompiledFile, $sThemeDir.'/main.css');
|
||||
// Make sure that the copy of the precompiled file is older than any other files to force a validation of the signature
|
||||
touch($sThemeDir.'/main.css', 1577836800 /* 2020-01-01 00:00:00 */);
|
||||
}
|
||||
else if ($sPrecompiledFile != '')
|
||||
{
|
||||
$this->Log("Precompiled file not found: '$sPrecompiledFile'");
|
||||
}
|
||||
ThemeHandler::CompileTheme($sThemeId, true, $aThemeParameters, $aImportsPaths, $sTempTargetDir);
|
||||
|
||||
|
||||
ThemeHandler::CompileTheme($sThemeId, $aThemeParameters, $aImportsPaths, $sTempTargetDir);
|
||||
}
|
||||
$this->Log(sprintf('Themes compilation took: %.3f ms for %d themes.', (microtime(true) - $fStart)*1000.0, count($aThemes)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,596 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopTestCase;
|
||||
|
||||
/**
|
||||
* @runTestsInSeparateProcesses
|
||||
* @preserveGlobalState disabled
|
||||
* @backupGlobals disabled
|
||||
* @covers utils
|
||||
*/
|
||||
class ThemeHandlerTest extends ItopTestCase
|
||||
{
|
||||
const PATTERN = '|\\\/var[^"]+testimages|';
|
||||
|
||||
private $compileCSSServiceMock;
|
||||
private $cssPath;
|
||||
private $jsonThemeParamFile;
|
||||
private $tmpDir;
|
||||
private $aDirsToCleanup=array();
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
require_once(APPROOT.'application/themehandler.class.inc.php');
|
||||
require_once(APPROOT.'setup/modelfactory.class.inc.php');
|
||||
|
||||
$this->compileCSSServiceMock = $this->createMock('CompileCSSService');
|
||||
ThemeHandler::mockCompileCSSService($this->compileCSSServiceMock);
|
||||
|
||||
$this->tmpDir=$this->tmpdir();
|
||||
$aDirsToCleanup[] = $this->tmpDir;
|
||||
|
||||
$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))
|
||||
{
|
||||
unlink($tmpfile);
|
||||
}
|
||||
mkdir($tmpfile);
|
||||
if (is_dir($tmpfile))
|
||||
{
|
||||
return $tmpfile;
|
||||
}
|
||||
|
||||
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
|
||||
* @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, $aImportsPaths);
|
||||
$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":"1d4b4ae2a6fba3db101f8dd1cecab082","jqueryui":"78cfafc3524dac98e61fc2460918d4e5","main":"52d8a7c5530ceb3a4d777364fa4e1eea"},"imports":[],"images":[]}
|
||||
JSON;
|
||||
|
||||
$this->assertEquals($expect_sig,$sig);
|
||||
}
|
||||
|
||||
public function testGetVarSignature()
|
||||
{
|
||||
$sig=<<<JSON
|
||||
{"variables":"37c31105548fce44fecca5cb34e455c9","stylesheets":{"css-variables":"934888ebb4991d4c76555be6b6d1d5cc","jqueryui":"78cfafc3524dac98e61fc2460918d4e5","main":"52d8a7c5530ceb3a4d777364fa4e1eea"},"imports":[]}
|
||||
JSON;
|
||||
$var_sig = ThemeHandler::GetVarSignature($sig);
|
||||
|
||||
$this->assertEquals("37c31105548fce44fecca5cb34e455c9",$var_sig);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $readFromParamAttributeFromJson
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @dataProvider CompileThemesProviderWithoutCss
|
||||
*/
|
||||
public function testCompileThemeWithoutCssFile_FocusOnParamAttribute($readFromParamAttributeFromJson=false)
|
||||
{
|
||||
$expectJsonFilePath = APPROOT.'test/application/theme-handler/expected/themes/basque-red/theme-parameters.json';
|
||||
$expectedThemeParamJson = file_get_contents($expectJsonFilePath);
|
||||
$aThemeParameters = json_decode($expectedThemeParamJson, true);
|
||||
if (is_file($this->jsonThemeParamFile))
|
||||
{
|
||||
unlink($this->jsonThemeParamFile);
|
||||
}
|
||||
if (is_file($this->cssPath))
|
||||
{
|
||||
unlink($this->cssPath);
|
||||
}
|
||||
|
||||
$this->compileCSSServiceMock->expects($this->exactly(1))
|
||||
->method("CompileCSSFromSASS")
|
||||
->willReturn("====CSSCOMPILEDCONTENT====");
|
||||
|
||||
if($readFromParamAttributeFromJson)
|
||||
{
|
||||
copy($expectJsonFilePath, $this->jsonThemeParamFile);
|
||||
ThemeHandler::CompileTheme('basque-red', true, null, array($this->tmpDir.'/branding/themes/'), $this->tmpDir);
|
||||
}
|
||||
else
|
||||
{
|
||||
ThemeHandler::CompileTheme('basque-red', true, $aThemeParameters, array($this->tmpDir.'/branding/themes/'), $this->tmpDir);
|
||||
}
|
||||
$this->assertTrue(is_file($this->cssPath));
|
||||
$this->assertEquals($expectedThemeParamJson, file_get_contents($this->jsonThemeParamFile));
|
||||
$this->assertEquals(file_get_contents(APPROOT . 'test/application/theme-handler/expected/themes/basque-red/main.css'), file_get_contents($this->cssPath));
|
||||
}
|
||||
|
||||
public function CompileThemesProviderWithoutCss()
|
||||
{
|
||||
return array(
|
||||
"pass ParamAttributes and Save them in Json" => array(false),
|
||||
"use them from saved json" => array(true)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $ThemeParametersJson
|
||||
*
|
||||
* @param int $CompileCount
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @dataProvider CompileThemesProviderEmptyArray
|
||||
*/
|
||||
public function testCompileThemesEmptyArray($ThemeParametersJson, $CompileCount=0)
|
||||
{
|
||||
$cssPath = $this->tmpDir . '/branding/themes/basque-red/main.css';
|
||||
copy(APPROOT . 'test/application/theme-handler/expected/themes/basque-red/main.css', $cssPath);
|
||||
|
||||
$this->compileCSSServiceMock->expects($this->exactly($CompileCount))
|
||||
->method("CompileCSSFromSASS")
|
||||
->willReturn("====CSSCOMPILEDCONTENT====");
|
||||
|
||||
ThemeHandler::CompileTheme('basque-red', true, json_decode($ThemeParametersJson, true), array($this->tmpDir.'/branding/themes/'), $this->tmpDir);
|
||||
}
|
||||
|
||||
public function CompileThemesProviderEmptyArray()
|
||||
{
|
||||
$emptyImports = '{"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"}}';
|
||||
$emptyStyleSheets='{"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":[]}';
|
||||
$emptyVars='{"variables":[],"imports":{"css-variables":"..\/css\/css-variables.scss"},"stylesheets":{"jqueryui":"..\/css\/ui-lightness\/jqueryui.scss","main":"..\/css\/light-grey.scss"}}';
|
||||
return array(
|
||||
"empty imports" => array($emptyImports),
|
||||
"empty styles" => array($emptyStyleSheets),
|
||||
"empty vars" => array($emptyVars, 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 array(
|
||||
"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($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($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 $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->tmpDir.'/'.$sFileToTest))
|
||||
{
|
||||
$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);
|
||||
|
||||
//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)
|
||||
{
|
||||
$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->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", [APPROOT.'css']);
|
||||
|
||||
$aExpectedImages = json_decode(file_get_contents(APPROOT.'test/application/theme-handler/getimages/expected-getimages.json'), true);
|
||||
$this->assertEquals($aExpectedImages, $aIncludedImages);
|
||||
}
|
||||
|
||||
public function testGetAllImages()
|
||||
{
|
||||
$aFolders = [
|
||||
APPROOT.'test/application/theme-handler/getimages/imagefolder1',
|
||||
APPROOT.'test/application/theme-handler/getimages/imagefolder2'
|
||||
];
|
||||
$aAllImagesMap = ThemeHandler::GetAllImagesMap($aFolders);
|
||||
$aExpectedImagesMap = [
|
||||
"titi.jpeg" => [ APPROOT."test/application/theme-handler/getimages/imagefolder1/subdir/titi.jpeg"],
|
||||
"toto.jpg" => [ APPROOT."test/application/theme-handler/getimages/imagefolder1/toto.jpg"],
|
||||
"tete.gif" => [ APPROOT."test/application/theme-handler/getimages/imagefolder2/subdir/tete.gif"],
|
||||
"tutu.png" => [ APPROOT."test/application/theme-handler/getimages/imagefolder2/subdir/tutu.png", APPROOT."test/application/theme-handler/getimages/imagefolder2/tutu.png"],
|
||||
];
|
||||
$this->assertEquals($aExpectedImagesMap, $aAllImagesMap);
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 418 B |
|
Before Width: | Height: | Size: 312 B |
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 61 B |
|
Before Width: | Height: | Size: 240 B |
|
Before Width: | Height: | Size: 64 B |
|
Before Width: | Height: | Size: 324 B |
|
Before Width: | Height: | Size: 676 B |
|
Before Width: | Height: | Size: 842 B |
|
Before Width: | Height: | Size: 54 B |
|
Before Width: | Height: | Size: 543 B |
|
Before Width: | Height: | Size: 274 B |
|
Before Width: | Height: | Size: 237 B |
|
Before Width: | Height: | Size: 355 B |
|
Before Width: | Height: | Size: 327 B |
|
Before Width: | Height: | Size: 1011 B |
|
Before Width: | Height: | Size: 56 B |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 850 B |
|
Before Width: | Height: | Size: 139 B |
|
Before Width: | Height: | Size: 643 B |
|
Before Width: | Height: | Size: 122 B |
|
Before Width: | Height: | Size: 142 B |
|
Before Width: | Height: | Size: 122 B |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 85 B |
|
Before Width: | Height: | Size: 781 B |
|
Before Width: | Height: | Size: 89 B |
|
Before Width: | Height: | Size: 787 B |
|
Before Width: | Height: | Size: 65 B |
|
Before Width: | Height: | Size: 750 B |
@@ -1,123 +0,0 @@
|
||||
/*!
|
||||
* 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;
|
||||
|
Before Width: | Height: | Size: 418 B |
|
Before Width: | Height: | Size: 312 B |
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 61 B |
|
Before Width: | Height: | Size: 240 B |
|
Before Width: | Height: | Size: 64 B |
|
Before Width: | Height: | Size: 324 B |
|
Before Width: | Height: | Size: 676 B |
|
Before Width: | Height: | Size: 842 B |
|
Before Width: | Height: | Size: 54 B |
|
Before Width: | Height: | Size: 543 B |
|
Before Width: | Height: | Size: 274 B |
|
Before Width: | Height: | Size: 237 B |
|
Before Width: | Height: | Size: 355 B |
|
Before Width: | Height: | Size: 327 B |
|
Before Width: | Height: | Size: 1011 B |
|
Before Width: | Height: | Size: 120 B |
|
Before Width: | Height: | Size: 56 B |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 850 B |
|
Before Width: | Height: | Size: 139 B |
|
Before Width: | Height: | Size: 643 B |
|
Before Width: | Height: | Size: 122 B |
|
Before Width: | Height: | Size: 142 B |
|
Before Width: | Height: | Size: 122 B |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 85 B |
|
Before Width: | Height: | Size: 781 B |
|
Before Width: | Height: | Size: 89 B |
|
Before Width: | Height: | Size: 787 B |
|
Before Width: | Height: | Size: 65 B |
|
Before Width: | Height: | Size: 750 B |
@@ -1,6 +0,0 @@
|
||||
/*
|
||||
=== SIGNATURE BEGIN ===
|
||||
{"variables":"37c31105548fce44fecca5cb34e455c9","stylesheets":{"css-variables":"1d4b4ae2a6fba3db101f8dd1cecab082","jqueryui":"78cfafc3524dac98e61fc2460918d4e5","main":"52d8a7c5530ceb3a4d777364fa4e1eea"},"imports":[],"images":[]}
|
||||
=== SIGNATURE END ===
|
||||
*/
|
||||
====CSSCOMPILEDCONTENT====
|
||||
@@ -1 +0,0 @@
|
||||
{"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"}}
|
||||
@@ -1,42 +0,0 @@
|
||||
[
|
||||
"/var/www/html/iTop/css/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png",
|
||||
"/var/www/html/iTop/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/itop-logo-2.png",
|
||||
"../images/splitter-bkg.png",
|
||||
"../images/plus.gif",
|
||||
"../images/minus.gif",
|
||||
"../images/full-screen.png",
|
||||
"../images/indicator.gif",
|
||||
"../images/delete.png",
|
||||
"../images/bg.gif",
|
||||
"../images/desc.gif",
|
||||
"../images/asc.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",
|
||||
"/var/www/html/iTop/css/ui-lightness/images/ui-icons_222222_256x240.png",
|
||||
"../images/breadcrumb-separator.png",
|
||||
"/var/www/html/iTop/css/ui-lightness/images/ui-icons_E87C1E_256x240.png",
|
||||
"/var/www/html/iTop/css/ui-lightness/images/ui-icons_1c94c4_256x240.png",
|
||||
"/var/www/html/iTop/css/ui-lightness/images/ui-icons_F26522_256x240.png",
|
||||
"/var/www/html/iTop/css/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png",
|
||||
"/var/www/html/iTop/css/ui-lightness/images/ui-icons_ffd27a_256x240.png"
|
||||
]
|
||||
@@ -1,9 +0,0 @@
|
||||
$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);
|
||||