app = $app; $this->lessCompiler = new LessCompiler(); $this->pubPath = $app->getBootstrapDirectory(); $this->collect(); } /** * Collect Web 2 and module LESS files and add them to the LESS compiler */ protected function collect() { foreach (self::$lessFiles as $lessFile) { $this->lessCompiler->addLessFile($this->pubPath . '/' . $lessFile); } $mm = $this->app->getModuleManager(); foreach ($mm->getLoadedModules() as $moduleName => $module) { if ($module->hasCss()) { foreach ($module->getCssFiles() as $lessFilePath) { $this->lessCompiler->addModuleLessFile($moduleName, $lessFilePath); } } } $themingConfig = $this->app->getConfig()->getSection('themes'); $defaultTheme = $themingConfig->get('default'); $theme = null; if ($defaultTheme !== null && $defaultTheme !== self::DEFAULT_THEME) { $theme = $defaultTheme; } if (! (bool) $themingConfig->get('disabled', false)) { $auth = Auth::getInstance(); if ($auth->isAuthenticated()) { $userTheme = $auth->getUser()->getPreferences()->getValue('icingaweb', 'theme'); if ($userTheme !== null) { $theme = $userTheme; } } } if ($theme && $theme !== self::DEFAULT_THEME) { if (($pos = strpos($theme, '/')) !== false) { $moduleName = substr($theme, 0, $pos); $theme = substr($theme, $pos + 1); if ($mm->hasLoaded($moduleName)) { $module = $mm->getModule($moduleName); $this->lessCompiler->setTheme($module->getCssDir() . '/themes/' . $theme . '.less'); } } else { $this->lessCompiler->setTheme($this->pubPath . '/css/themes/' . $theme . '.less'); } } } /** * Get the stylesheet for PDF export * * @return $this */ public static function forPdf() { $styleSheet = new self(); $styleSheet->lessCompiler->addLessFile($styleSheet->pubPath . '/css/pdf/pdfprint.less'); // TODO(el): Caching return $styleSheet; } /** * Render the stylesheet * * @param bool $minified Whether to compress the stylesheet * * @return string CSS */ public function render($minified = false) { if ($minified) { $this->lessCompiler->compress(); } return $this->lessCompiler->render(); } /** * Send the stylesheet to the client * * Does not cache the stylesheet if the HTTP header Cache-Control or Pragma is set to no-cache. * * @param bool $minified Whether to compress the stylesheet */ public static function send($minified = false) { $styleSheet = new self(); $request = $styleSheet->app->getRequest(); $response = $styleSheet->app->getResponse(); $response->setHeader('Cache-Control', 'public', true); $noCache = $request->getHeader('Cache-Control') === 'no-cache' || $request->getHeader('Pragma') === 'no-cache'; if (! $noCache && FileCache::etagMatchesFiles($styleSheet->lessCompiler->getLessFiles())) { $response ->setHttpResponseCode(304) ->sendHeaders(); return; } $etag = FileCache::etagForFiles($styleSheet->lessCompiler->getLessFiles()); $response->setHeader('ETag', $etag, true) ->setHeader('Content-Type', 'text/css', true); $cacheFile = 'icinga-' . $etag . ($minified ? '.min' : '') . '.css'; $cache = FileCache::instance(); if (! $noCache && $cache->has($cacheFile)) { $response->setBody($cache->get($cacheFile)); } else { $css = $styleSheet->render($minified); $response->setBody($css); $cache->store($cacheFile, $css); } $response->sendResponse(); } /** * Render the stylesheet * * @return string */ public function __toString() { try { return $this->render(); } catch (Exception $e) { Logger::error($e); return IcingaException::describe($e); } } }