artabro/wire/modules/AdminTheme/AdminThemeUikit/AdminThemeUikitCss.php
2024-08-27 11:35:37 +02:00

267 lines
8.6 KiB
PHP

<?php namespace ProcessWire;
/**
* AdminThemeUikit CSS
*
* Manages selection of CSS file and determines when CSS file to be recompiled from LESS
* source files.
*
* @property bool $upgrade Set to true when upgrading core Uikit version. (default=false)
* @property string $frameworkLessFile Full disk path to LESS file that includes the framework/Uikit.
* @property array $baseStyles Base style options (default=[ 'reno', 'rock' ])
* @property string $defaultStyle Default style (default='reno')
* @property string $defaultCssFile Core CSS file to create when upgrading (relative to module root)
* @property string $styleDir Directory where base .less files are located (relative to module root)
* @property array $replacements Array of [find=>replace] for compiled CSS file.
*
* @property string $configPhpHash Hash used internally to detect changes to $config->AdminThemeUikit settings.
* @property string $configPhpName Name of property in $config that holds custom settings (default='AdminThemeUikit').
* @property int $requireCssVersion
* @property int $cssVersion
*
* Settings that may be specified in $config->AdminThemeUikit array:
*
* @property string $style Configured style name to use, one of blank (for default), 'reno' or 'rock'.
* @property bool $recompile Recompile all LESS to CSS now? (set to true for 1 request only)
* @property bool $compress Compress compiled CSS? (default=true)
* @property array $customLessFiles Custom .less file(s) to include, relative to PW root.
* @property string $customCssFile Custom target .css file to compile custom .less file(s) to, relative to PW root.
* @property array $vars LESS variables to be used when compiling. Eg ['rock-primary' => '#FF0000']
* @property string $parse LESS string to parse, eg "@rock-primary: #FF0000;"
*
* @since 3.0.179
*
*/
class AdminThemeUikitCss extends WireData {
/**
* @var AdminTheme|AdminThemeUikit
*
*/
protected $adminTheme;
/**
* Construct
*
* @param AdminTheme $adminTheme
* @param array $options
*
*/
public function __construct(AdminTheme $adminTheme, array $options = array()) {
$this->adminTheme = $adminTheme;
$adminTheme->wire($this);
$this->setArray(array_merge($this->getDefaults(), $options));
parent::__construct();
}
/**
* @return array
*
*/
public function getDefaults() {
return array(
'baseStyles' => array('reno', 'rock'),
'defaultStyle' => 'reno',
'frameworkLessFile' => __DIR__ . '/uikit-pw/pw.less',
'defaultCssFile' => 'uikit-pw/pw.min.css',
'styleDir' => 'uikit-pw/styles/',
'style' => '',
'upgrade' => false,
'recompile' => false,
'compress' => true,
'customLessFiles' => array('/site/templates/admin.less'),
'customCssFile' => '/site/assets/admin.css',
'configPhpName' => $this->adminTheme->className(),
'configPhpHash' => $this->adminTheme->get('configPhpHash'),
'replacements' => array(),
'cssVersion' => (int) $this->adminTheme->get('cssVersion'),
'requireCssVersion' => 0,
'vars' => array(),
'parse' => '',
);
}
/**
* Get the primary Uikit CSS file URL to use (whether default or custom)
*
* @param bool $getPath Get disk path rather than URL?
* @return string
*
*/
public function getCssFile($getPath = false) {
$modules = $this->wire()->modules;
if(!$modules->isInstalled('Less')) return $this->getDefaultCssFile($getPath);
if($this->upgrade) {
$cssFile = $this->getDefaultCssFile(true);
$cssTime = filemtime($cssFile);
$lessFiles = array();
$recompile = true;
} else {
$lessFiles = array();
$lessTime = 0;
foreach($this->customLessFiles as $file) {
$file = $this->customFile($file, 'less');
if(!$file || !is_file($file)) continue;
$lessFiles[] = $file;
$mtime = filemtime($file);
if($mtime > $lessTime) $lessTime = $mtime;
}
if(!count($lessFiles) && ($this->style === '' || $this->style === $this->defaultStyle)) {
return $this->getDefaultCssFile($getPath);
}
$cssFile = $this->customFile($this->customCssFile, 'css');
if(!$cssFile) return $this->getDefaultCssFile($getPath);
$cssTime = is_file($cssFile) ? (int) filemtime($cssFile) : 0;
$recompile = $this->recompile || $lessTime > $cssTime || $this->cssVersion < $this->requireCssVersion;
if(!$recompile && $this->configPhpSettingsChanged()) $recompile = true;
}
if($recompile) try {
/** @var AdminThemeUikitLessInterface $less */
$less = $modules->get('Less');
$less->setOption('compress', $this->compress);
$less->addFile($this->frameworkLessFile);
$less->addFile($this->getAdminStyleLessFile());
$less->addFiles($lessFiles);
if(!empty($this->vars)) $less->parser()->ModifyVars($this->vars);
if(!empty($this->parse)) $less->parser()->parse($this->parse);
$options = array('replacements' => $this->replacements);
if(!$less->saveCss($cssFile, $options)) throw new WireException("Compile error: $cssFile");
$messages = array(sprintf($this->_('Compiled: %s'), $cssFile));
$cssTime = filemtime($cssFile);
if($this->cssVersion < $this->requireCssVersion) {
$messages[] = "(core CSS v$this->cssVersion => v$this->requireCssVersion)";
$modules->saveConfig($this->adminTheme, 'cssVersion', $this->requireCssVersion);
$this->adminTheme->set('cssVersion', $this->requireCssVersion);
}
$this->message(implode(' ', $messages), Notice::noGroup | Notice::superuser);
} catch(\Exception $e) {
$this->error('LESS - ' . $e->getMessage(), Notice::noGroup | Notice::superuser);
}
return $getPath ? $cssFile : $this->fileToUrl($cssFile) . "?v=$cssTime";
}
/**
* Get URL for given full path/file
*
* @param string $file
* @return string
*
*/
protected function fileToUrl($file) {
$config = $this->wire()->config;
return $config->urls->root . substr($file, strlen($config->paths->root));
}
/**
* Get default Uikit CSS file URL or disk path
*
* @param bool $getPath
* @return string
*
*/
public function getDefaultCssFile($getPath = false) {
$config = $this->wire()->config;
$file = $this->defaultCssFile;
$path = $config->paths($this->adminTheme) . $file;
if($getPath) return $path;
$v = $config->debug ? filemtime($path) : $config->version;
$url = $config->urls($this->adminTheme) . "$file?v=$v" ;
return $url;
}
/**
* Have the $config->AdminThemeUikit settings changed?
*
* @return bool
* @throws WireException
*
*/
public function configPhpSettingsChanged() {
$settings = $this->wire()->config->get($this->configPhpName);
unset($settings['recompile']); // recompile is runtime only setting
$hashNow = md5(print_r($settings, true));
$hashThen = $this->get('configPhpHash');
if($hashNow === $hashThen) return false;
$this->wire()->modules->saveConfig($this->adminTheme, 'configPhpHash', $hashNow);
return true;
}
/**
* Apply custom file/path replacements
*
* @param string $file
* @param string $requireExtension Extension to require on given file
* @return string
*
*/
protected function customFile($file, $requireExtension = '') {
$paths = $this->wire()->config->paths;
$file = $this->wire()->files->unixFileName($file);
$replacements = array(
'/site/assets/' => $paths->assets,
'/site/templates/' => $paths->templates,
'/site/modules/' => $paths->siteModules,
);
if($requireExtension) {
$ext = pathinfo($file, PATHINFO_EXTENSION);
if(strtolower($ext) !== strtolower($requireExtension)) return '';
}
foreach($replacements as $find => $replace) {
if(strpos($file, $find) === 0) $file = str_replace($find, $replace, $file);
}
if($file && strpos($file, $paths->root) !== 0) {
$file = $paths->root . ltrim($file, '/');
}
return $file;
}
/**
* Get admin base style file to use
*
* @return string
*
*/
public function getAdminStyleLessFile() {
$config = $this->wire()->config;
$files = $this->wire()->files;
$path = $config->paths($this->adminTheme) . $this->styleDir;
$defaultFile = $path . $this->defaultStyle . '.less';
$baseStyle = $this->upgrade ? $this->defaultStyle : $this->style;
if(empty($baseStyle) || $baseStyle === $this->defaultStyle) return $defaultFile;
if(stripos($baseStyle, '.')) {
// style is file name relative to installation root path
$file = $this->customFile($baseStyle, 'less');
if($file && is_file($file) && $files->allowPath($file, $config->paths->root)) return $file;
}
$file = $path . basename($baseStyle) . '.less';
if(in_array($baseStyle, $this->baseStyles) || is_file($file)) return $file;
$this->warning(
"config.{$this->configPhpName}[style]: " .
sprintf($this->_('Admin base style - file not found: %s'), $file),
Notice::debug
);
return $defaultFile;
}
}