. * */ namespace OC\Template; use Leafo\ScssPhp\Compiler; use Leafo\ScssPhp\Exception\ParserException; use Leafo\ScssPhp\Formatter\Crunched; use Leafo\ScssPhp\Formatter\Expanded; use OC\SystemConfig; use OCP\Files\IAppData; use OCP\Files\NotFoundException; use OCP\Files\SimpleFS\ISimpleFolder; use OCP\ILogger; use OCP\IURLGenerator; class SCSSCacher { /** @var ILogger */ protected $logger; /** @var IAppData */ protected $appData; /** @var IURLGenerator */ protected $urlGenerator; /** @var SystemConfig */ protected $systemConfig; /** * @param ILogger $logger * @param IAppData $appData * @param IURLGenerator $urlGenerator * @param SystemConfig $systemConfig */ public function __construct(ILogger $logger, IAppData $appData, IURLGenerator $urlGenerator, SystemConfig $systemConfig) { $this->logger = $logger; $this->appData = $appData; $this->urlGenerator = $urlGenerator; $this->systemConfig = $systemConfig; } /** * Process the caching process if needed * @param string $root Root path to the nextcloud installation * @param string $file * @return boolean */ public function process($root, $file) { $path = explode('/', $root . '/' . $file); $fileNameSCSS = array_pop($path); $fileNameCSS = str_replace('.scss', '.css', $fileNameSCSS); $path = implode('/', $path); $webDir = explode('/', $file); array_pop($webDir); $webDir = implode('/', $webDir); try { $folder = $this->appData->getFolder('core'); } catch(NotFoundException $e) { // creating css appdata folder $folder = $this->appData->newFolder('core'); } if($this->isCached($fileNameCSS, $fileNameSCSS, $folder, $path)) { return true; } else { return $this->cache($path, $fileNameCSS, $fileNameSCSS, $folder, $webDir); } } /** * Check if the file is cached or not * @param string $fileNameCSS * @param string $fileNameSCSS * @param ISimpleFolder $folder * @param string $path * @return boolean */ private function isCached($fileNameCSS, $fileNameSCSS, ISimpleFolder $folder, $path) { try{ $cachedFile = $folder->getFile($fileNameCSS); if( $cachedFile->getMTime() > filemtime($path.'/'.$fileNameSCSS) && $cachedFile->getSize() > 0 ) { return true; } } catch(NotFoundException $e) { return false; } return false; } /** * Cache the file with AppData * @param string $path * @param string $fileNameCSS * @param string $fileNameSCSS * @param ISimpleFolder $folder * @param string $webDir * @return boolean */ private function cache($path, $fileNameCSS, $fileNameSCSS, ISimpleFolder $folder, $webDir) { $scss = new Compiler(); $scss->setImportPaths($path); if($this->systemConfig->getValue('debug')) { // Debug mode $scss->setFormatter(Expanded::class); $scss->setLineNumberStyle(Compiler::LINE_COMMENTS); } else { // Compression $scss->setFormatter(Crunched::class); } try { $cachedfile = $folder->getFile($fileNameCSS); } catch(NotFoundException $e) { $cachedfile = $folder->newFile($fileNameCSS); } // Compile try { $compiledScss = $scss->compile('@import "'.$fileNameSCSS.'";'); } catch(ParserException $e) { $this->logger->error($e, ['app' => 'core']); return false; } try { $cachedfile->putContent($this->rebaseUrls($compiledScss, $webDir)); $this->logger->debug($webDir.'/'.$fileNameSCSS.' compiled and successfully cached', ['app' => 'core']); return true; } catch(NotFoundException $e) { return false; } } /** * Add the correct uri prefix to make uri valid again * @param string $css * @param string $webDir * @return string */ private function rebaseUrls($css, $webDir) { $re = '/url\([\'"]([\.\w?=\/-]*)[\'"]\)/x'; $subst = 'url(\'../../../'.$webDir.'/$1\')'; return preg_replace($re, $subst, $css); } /** * Return the cached css file uri * @param string $appName the app name * @param string $fileName * @return string */ public function getCachedSCSS($appName, $fileName) { $tmpfileLoc = explode('/', $fileName); $fileName = array_pop($tmpfileLoc); $fileName = str_replace('.scss', '.css', $fileName); return substr($this->urlGenerator->linkToRoute('core.Css.getCss', array('fileName' => $fileName, 'appName' => $appName)), strlen(\OC::$WEBROOT) + 1); } }