Generate CSS on saving custom CSS
authorjoshuaruesweg <ruesweg@woltlab.com>
Fri, 14 Aug 2020 13:13:00 +0000 (15:13 +0200)
committerjoshuaruesweg <ruesweg@woltlab.com>
Fri, 14 Aug 2020 14:10:46 +0000 (16:10 +0200)
wcfsetup/install/files/lib/acp/form/StyleAddForm.class.php
wcfsetup/install/files/lib/acp/form/StyleEditForm.class.php
wcfsetup/install/files/lib/acp/form/StyleGlobalValuesForm.class.php
wcfsetup/install/files/lib/system/style/StyleCompiler.class.php

index fea550318bcbe44c1ead05fb8528f4525c5041c6..acd1900c2d35b116dbd7a2b720ff16205fef8c9c 100644 (file)
@@ -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();
index 49c34a834c5e6b47e242642e5dbd22ab1fe52989..ece7586affa62980b8a882308c62d96388058bd2 100644 (file)
@@ -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');
                
index e7c34efe113f8779a360b1177201f965db393eb4..fbf945e453e5d3a0391722048f8029497705943f 100644 (file)
@@ -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);
        }
        
index e34b3d4542e86841ad1facfc4809467c74f485f8..486a237a611102bdf04ff8f7c20032bf06553e5b 100644 (file)
@@ -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;
        }
 }