From 5c7386a01b127b4c2d6d7359b1a527fb9e34dc80 Mon Sep 17 00:00:00 2001 From: joshuaruesweg Date: Fri, 14 Aug 2020 15:13:00 +0200 Subject: [PATCH] Generate CSS on saving custom CSS --- .../files/lib/acp/form/StyleAddForm.class.php | 25 ++++++- .../lib/acp/form/StyleEditForm.class.php | 19 ++++- .../acp/form/StyleGlobalValuesForm.class.php | 32 ++++++-- .../lib/system/style/StyleCompiler.class.php | 75 +++++++++---------- 4 files changed, 100 insertions(+), 51 deletions(-) diff --git a/wcfsetup/install/files/lib/acp/form/StyleAddForm.class.php b/wcfsetup/install/files/lib/acp/form/StyleAddForm.class.php index fea550318b..acd1900c2d 100644 --- a/wcfsetup/install/files/lib/acp/form/StyleAddForm.class.php +++ b/wcfsetup/install/files/lib/acp/form/StyleAddForm.class.php @@ -21,6 +21,7 @@ use wcf\system\style\FontManager; use wcf\system\WCF; use wcf\util\ArrayUtil; use wcf\util\DateUtil; +use wcf\util\FileUtil; use wcf\util\StringUtil; /** @@ -188,6 +189,11 @@ class StyleAddForm extends AbstractForm { */ public $tmpHash = ''; + /** + * @var string + */ + public $styleTestFileDir; + /** * list of variables and their value * @var string[] @@ -465,11 +471,14 @@ class StyleAddForm extends AbstractForm { public function validateIndividualScss() { $variables = array_merge(StyleCompiler::getDefaultVariables(), $this->variables); - $result = StyleCompiler::getInstance()->testStyle($this->apiVersion, false, $variables); + $this->styleTestFileDir = FileUtil::getTemporaryFilename('style_'); + FileUtil::makePath($this->styleTestFileDir); - if ($result !== true) { + $result = StyleCompiler::getInstance()->testStyle($this->styleTestFileDir, $this->apiVersion, false, $variables); + + if ($result !== null) { throw new UserInputException('individualScss', [ - 'message' => $result, + 'message' => $result->getMessage(), ]); } } @@ -788,6 +797,15 @@ class StyleAddForm extends AbstractForm { 'styleDescription' => 'wcf.style.styleDescription'.$style->styleID ]); + // save compiled style + if ($this->styleTestFileDir && file_exists($this->styleTestFileDir . '/style.css') && file_exists($this->styleTestFileDir . '/style-rtl.css')) { + $styleFilename = StyleCompiler::getFilenameForStyle($style); + rename($this->styleTestFileDir . '/style.css', $styleFilename . '.css'); + rename($this->styleTestFileDir . '/style-rtl.css', $styleFilename . '-rtl.css'); + + rmdir($this->styleTestFileDir); + } + // call saved event $this->saved(); @@ -797,6 +815,7 @@ class StyleAddForm extends AbstractForm { $this->setDefaultValues(); $this->isTainted = true; $this->templateGroupID = 0; + $this->styleTestFilename = null; $this->rebuildUploadFields(); I18nHandler::getInstance()->reset(); diff --git a/wcfsetup/install/files/lib/acp/form/StyleEditForm.class.php b/wcfsetup/install/files/lib/acp/form/StyleEditForm.class.php index 49c34a834c..ece7586aff 100644 --- a/wcfsetup/install/files/lib/acp/form/StyleEditForm.class.php +++ b/wcfsetup/install/files/lib/acp/form/StyleEditForm.class.php @@ -12,6 +12,7 @@ use wcf\system\file\upload\UploadHandler; use wcf\system\language\I18nHandler; use wcf\system\style\StyleCompiler; use wcf\system\WCF; +use wcf\util\FileUtil; /** * Shows the style edit form. @@ -78,11 +79,14 @@ class StyleEditForm extends StyleAddForm { $variables = array_merge(StyleCompiler::getDefaultVariables(), $variables); - $result = StyleCompiler::getInstance()->testStyle($this->apiVersion, $this->style->imagePath, $variables); + $this->styleTestFileDir = FileUtil::getTemporaryFilename('style_'); + FileUtil::makePath($this->styleTestFileDir); - if ($result !== true) { + $result = StyleCompiler::getInstance()->testStyle($this->styleTestFileDir, $this->apiVersion, false, $variables); + + if ($result !== null) { throw new UserInputException('individualScss', [ - 'message' => $result, + 'message' => $result->getMessage(), ]); } } @@ -249,6 +253,15 @@ class StyleEditForm extends StyleAddForm { ]); $this->objectAction->executeAction(); + // save compiled style + if ($this->styleTestFileDir && file_exists($this->styleTestFileDir . '/style.css') && file_exists($this->styleTestFileDir . '/style-rtl.css')) { + $styleFilename = StyleCompiler::getFilenameForStyle($this->style); + rename($this->styleTestFileDir . '/style.css', $styleFilename . '.css'); + rename($this->styleTestFileDir . '/style-rtl.css', $styleFilename . '-rtl.css'); + + rmdir($this->styleTestFileDir); + } + // save description I18nHandler::getInstance()->save('styleDescription', $this->style->styleDescription, 'wcf.style'); diff --git a/wcfsetup/install/files/lib/acp/form/StyleGlobalValuesForm.class.php b/wcfsetup/install/files/lib/acp/form/StyleGlobalValuesForm.class.php index e7c34efe11..fbf945e453 100644 --- a/wcfsetup/install/files/lib/acp/form/StyleGlobalValuesForm.class.php +++ b/wcfsetup/install/files/lib/acp/form/StyleGlobalValuesForm.class.php @@ -35,6 +35,11 @@ class StyleGlobalValuesForm extends AbstractForm { */ public $stylesScrollOffset = 0; + /** + * @var string + */ + public $styleTestFileDir; + /** * @inheritDoc */ @@ -73,12 +78,17 @@ class StyleGlobalValuesForm extends AbstractForm { // Due to performance issues we can only compile the default style and check, // whether there are syntax issues. $defaultStyle = StyleHandler::getInstance()->getDefaultStyle(); - $errorMessage = StyleCompiler::getInstance()->testStyle($defaultStyle->apiVersion, $defaultStyle->imagePath, $defaultStyle->getVariables(), $tmpFile); - - if ($errorMessage !== true) { - throw new UserInputException('styles', [ - 'message' => $errorMessage, - ]); + if ($defaultStyle !== null) { + $this->styleTestFileDir = FileUtil::getTemporaryFilename('style_'); + FileUtil::makePath($this->styleTestFileDir); + + $errorMessage = StyleCompiler::getInstance()->testStyle($this->styleTestFileDir, $defaultStyle->apiVersion, $defaultStyle->imagePath, $defaultStyle->getVariables(), $tmpFile); + + if ($errorMessage !== null) { + throw new UserInputException('styles', [ + 'message' => $errorMessage->getMessage(), + ]); + } } } finally { @@ -112,6 +122,16 @@ class StyleGlobalValuesForm extends AbstractForm { // reset stylesheets StyleHandler::resetStylesheets(false); + // save compiled style + $defaultStyle = StyleHandler::getInstance()->getDefaultStyle(); + if ($defaultStyle !== null && $this->styleTestFileDir && file_exists($this->styleTestFileDir . '/style.css') && file_exists($this->styleTestFileDir . '/style-rtl.css')) { + $styleFilename = StyleCompiler::getFilenameForStyle($defaultStyle); + rename($this->styleTestFileDir . '/style.css', $styleFilename . '.css'); + rename($this->styleTestFileDir . '/style-rtl.css', $styleFilename . '-rtl.css'); + + rmdir($this->styleTestFileDir); + } + WCF::getTPL()->assign('success', true); } diff --git a/wcfsetup/install/files/lib/system/style/StyleCompiler.class.php b/wcfsetup/install/files/lib/system/style/StyleCompiler.class.php index e34b3d4542..486a237a61 100644 --- a/wcfsetup/install/files/lib/system/style/StyleCompiler.class.php +++ b/wcfsetup/install/files/lib/system/style/StyleCompiler.class.php @@ -91,16 +91,17 @@ class StyleCompiler extends SingletonFactory { /** * Test a style with the given apiVersion, imagePath and variables. If the style is valid and does not throw an - * error, true is returned. Otherwise a string with the error message. + * error, null is returned. Otherwise the exception is returned (!). * - * @param string $apiVersion - * @param string $imagePath - * @param string[] $variables - * @param string|null $customCustomSCSSFile - * @return bool|string + * @param string $testFileDir + * @param string $apiVersion + * @param string $imagePath + * @param string[] $variables + * @param string|null $customCustomSCSSFile + * @return null|\Exception * @since 5.3 */ - public function testStyle($apiVersion, $imagePath, array $variables, $customCustomSCSSFile = null) { + public function testStyle($testFileDir, $apiVersion, $imagePath, array $variables, $customCustomSCSSFile = null) { $individualScss = ''; if (isset($variables['individualScss'])) { $individualScss = $variables['individualScss']; @@ -145,20 +146,21 @@ class StyleCompiler extends SingletonFactory { } try { - $this->compileStylesheetToString( + $this->compileStylesheet( + FileUtil::addTrailingSlash($testFileDir) . 'style', $files, $variables, $individualScss . (!empty($parameters['scss']) ? "\n" . $parameters['scss'] : ''), function($content) { - return $content; + return "/* stylesheet, generated on ".gmdate('r')." -- DO NOT EDIT */\n\n" . $content; } ); } catch (\Exception $e) { - return $e->getMessage(); + return $e; } - return true; + return null; } /** @@ -242,7 +244,7 @@ class StyleCompiler extends SingletonFactory { EventHandler::getInstance()->fireAction($this, 'compile', $parameters); $this->compileStylesheet( - WCF_DIR.'style/style-'.$style->styleID, + $this->getFilenameForStyle($style), $this->getFiles(), $variables, $individualScss . (!empty($parameters['scss']) ? "\n" . $parameters['scss'] : ''), @@ -451,28 +453,6 @@ EOT; * @param callable $callback */ protected function compileStylesheet($filename, array $files, array $variables, $individualScss, callable $callback) { - $content = $this->compileStylesheetToString($files, $variables, $individualScss, $callback); - - // write stylesheet - file_put_contents($filename.'.css', $content['ltr']); - FileUtil::makeWritable($filename.'.css'); - - // write stylesheet for RTL - file_put_contents($filename.'-rtl.css', $content['rtl']); - FileUtil::makeWritable($filename.'-rtl.css'); - } - - /** - * Compiles SCSS stylesheets and returns them as an array for the `ltr` and the `rtl` version. - * - * @param string[] $files - * @param string[] $variables - * @param string $individualScss - * @param callable $callback - * @return String[] - * @since 5.3 - */ - public function compileStylesheetToString(array $files, array $variables, $individualScss, callable $callback) { foreach ($variables as &$value) { if (StringUtil::startsWith($value, '../')) { $value = '~"'.$value.'"'; @@ -531,15 +511,32 @@ EOT; throw new SystemException("Could not compile SCSS: ".$e->getMessage(), 0, '', $e); } - $data['ltr'] = $callback($content); + $content = $callback($content); + + // write stylesheet + file_put_contents($filename.'.css', $content); + FileUtil::makeWritable($filename.'.css'); // convert stylesheet to RTL - $data['rtl'] = StyleUtil::convertCSSToRTL($content); + $content = StyleUtil::convertCSSToRTL($content); // force code boxes to be always LTR - $data['rtl'] .= "\n/* RTL fix for code boxes */\n"; - $data['rtl'] .= '.codeBox > div > ol > li > span:last-child, .redactor-layer pre { direction: ltr; text-align: left; } .codeBox > div > ol > li > span:last-child { display: block; }'; + $content .= "\n/* RTL fix for code boxes */\n"; + $content .= '.codeBox > div > ol > li > span:last-child, .redactor-layer pre { direction: ltr; text-align: left; } .codeBox > div > ol > li > span:last-child { display: block; }'; - return $data; + // write stylesheet for RTL + file_put_contents($filename.'-rtl.css', $content); + FileUtil::makeWritable($filename.'-rtl.css'); + } + + /** + * Returns the name of the CSS file for a specific style. + * + * @param Style $style + * @return string + * @since 5.3 + */ + public static function getFilenameForStyle(Style $style) { + return WCF_DIR.'style/style-'.$style->styleID; } } -- 2.20.1