function css($file, $media = null) { /* If file is CSS, check if there is a LESS file */ if (preg_match('/\\.css$/i', $file)) { $less = preg_replace('/\\.css$/i', '.less', $file); if (is_file(Director::getAbsFile($less))) { $file = $less; } } /* If less file, then check/compile it */ if (preg_match('/\\.less$/i', $file)) { $compiler = 'checkedCompile'; $out = preg_replace('/\\.less$/i', '.css', $file); /* Force recompile if ?flush */ if (isset($_GET['flush'])) { $compiler = 'compileFile'; } /* Create instance */ $less = new lessc(); /* Automatically compress if in live mode */ if (DIRECTOR::isLive()) { $less->setFormatter("compressed"); } try { $less->{$compiler}(Director::getAbsFile($file), Director::getAbsFile($out)); } catch (Exception $ex) { trigger_error("lessphp fatal error: " . $ex->getMessage(), E_USER_ERROR); } $file = $out; } /* Return css path */ return parent::css($file, $media); }
public function combine_files($combinedFileName, $files, $media = null) { foreach ($files as $i => $fileName) { $files[$i] = $this->collectFile($fileName); } return parent::combine_files($combinedFileName, $files, $media); }
protected function path_for_file($fileOrUrl) { if (preg_match('{^//|http[s]?}', $fileOrUrl)) { return $fileOrUrl; } else { if (!Director::fileExists($fileOrUrl)) { $fileOrUrl = MODULES_DIR . '/' . $fileOrUrl; } return parent::path_for_file($fileOrUrl); } }
/** * Default includeInHTML strips out CSS if the file doesn't exist. We need to add it back in if it's a redirected rewrite * @see sapphire/core/Requirements_Backend#includeInHTML($templateFile, $content) */ function includeInHTML($templateFile, $content) { $content = parent::includeInHTML($templateFile, $content); $requirements = ''; foreach (array_diff_key($this->css, $this->blocked) as $file => $params) { $path = self::path_for_file($file); if (!$path && ($path = self::path_for_file('assets/' . basename($file)))) { $media = isset($params['media']) && !empty($params['media']) ? " media=\"{$params['media']}\"" : ""; $requirements .= "<link rel=\"stylesheet\" type=\"text/css\"{$media} href=\"{$file}\" />\n"; } } return preg_replace("/(<\\/head[^>]*>)/i", $requirements . "\\1", $content); }
function css($file, $media = null) { /** * Only initiate automatically if: * - webiste is in dev mode * - or a ?flush is called */ if (preg_match('/\\.less$/i', $file) || Director::isDev() || isset($_GET['flush'])) { /* If file is CSS, check if there is a LESS file */ if (preg_match('/\\.css$/i', $file)) { $less = preg_replace('/\\.css$/i', '.less', $file); if (is_file(Director::getAbsFile($less))) { $file = $less; } } /* If less file exists, then check/compile it */ if (preg_match('/\\.less$/i', $file)) { $out = preg_replace('/\\.less$/i', '.css', $file); $css_file = Director::getAbsFile($out); $options = array(); /* Automatically compress if in live mode */ if (Director::isLive()) { $options['compress'] = true; } try { /* Force recompile & only write to css if updated */ if (isset($_GET['flush']) || !Director::isLive()) { /* Create instance */ $parser = new Less_Parser($options); if (!empty(self::$variables)) { $parser->ModifyVars(self::$variables); } /* calculate the LESS file's parent URL */ $css_dir = rtrim(Director::baseURL(), '/') . Director::makeRelative(dirname(Director::getAbsFile($file)) . '/'); $parser->parseFile(Director::getAbsFile($file), $css_dir); $css = $parser->getCss(); if (!is_file($css_file) || md5_file($css_file) != md5($css)) { file_put_contents($css_file, $css); } } } catch (Exception $ex) { trigger_error("Less.php fatal error: " . $ex->getMessage(), E_USER_ERROR); } $file = $out; } } /* Return css path */ return parent::css($file, $media); }
/** * Finds the path for specified file * * @param string $fileOrUrl * @return string|bool */ protected function path_for_file($fileOrUrl) { // only handle files in themes folder if (!Controller::has_curr() || is_a(Controller::curr(), 'LeftAndMain')) { return parent::path_for_file($fileOrUrl); } else { if (preg_match('{^//|http[s]?}', $fileOrUrl)) { return $fileOrUrl; } elseif (Director::fileExists($fileOrUrl)) { $filePath = preg_replace('/\\?.*/', '', Director::baseFolder() . '/' . $fileOrUrl); $baseurl = Director::baseURL(); // url parameters if (strpos($fileOrUrl, '?') !== false) { $parameters = '?' . substr($fileOrUrl, strpos($fileOrUrl, '?') + 1); $fileOrUrl = substr($fileOrUrl, 0, strpos($fileOrUrl, '?')); } else { $parameters = ''; } // get base path $baseFolder = Director::baseFolder(); // get combined files folder $combinedFilesFolder = rtrim($this->getCombinedFilesFolder(), '/'); if (!file_exists($baseFolder . '/' . $combinedFilesFolder)) { Filesystem::makeFolder($baseFolder . '/' . $combinedFilesFolder); } // get file name $fileName = substr($filePath, strrpos($filePath, '/') + 1); // get prefix $prefix = filemtime($filePath) . '-'; $prefixedFilePath = $baseFolder . '/' . $combinedFilesFolder . '/' . $prefix . $fileName; // clean up and create file if (!file_exists($prefixedFilePath)) { // remove old prefixed files foreach (glob($baseFolder . '/' . $combinedFilesFolder . '/' . '[0-9]*-' . $fileName) as $file) { unlink($file); } // copy standard file to prefixed file copy($filePath, $prefixedFilePath); } return "{$baseurl}{$combinedFilesFolder}/{$prefix}{$fileName}{$parameters}"; } else { return false; } } }
protected function path_for_file($fileOrUrl) { // Debug::message($fileOrUrl . " " . (Controller::curr()->IsCMS() ? "Admin" : "FrontEnd")); return parent::path_for_file($fileOrUrl); }
function testRequirementsBackend() { $backend = new Requirements_Backend(); $backend->javascript(SAPPHIRE_DIR . '/tests/forms/a.js'); $this->assertTrue(count($backend->get_javascript()) == 1, "There should be only 1 file included in required javascript."); $this->assertTrue(in_array(SAPPHIRE_DIR . '/tests/forms/a.js', $backend->get_javascript()), "/test/forms/a.js should be included in required javascript."); $backend->javascript(SAPPHIRE_DIR . '/tests/forms/b.js'); $this->assertTrue(count($backend->get_javascript()) == 2, "There should be 2 files included in required javascript."); $backend->block(SAPPHIRE_DIR . '/tests/forms/a.js'); $this->assertTrue(count($backend->get_javascript()) == 1, "There should be only 1 file included in required javascript."); $this->assertFalse(in_array(SAPPHIRE_DIR . '/tests/forms/a.js', $backend->get_javascript()), "/test/forms/a.js should not be included in required javascript after it has been blocked."); $this->assertTrue(in_array(SAPPHIRE_DIR . '/tests/forms/b.js', $backend->get_javascript()), "/test/forms/b.js should be included in required javascript."); $backend->css(SAPPHIRE_DIR . '/tests/forms/a.css'); $this->assertTrue(count($backend->get_css()) == 1, "There should be only 1 file included in required css."); $this->assertArrayHasKey(SAPPHIRE_DIR . '/tests/forms/a.css', $backend->get_css(), "/tests/forms/a.css should be in required css."); $backend->block(SAPPHIRE_DIR . '/tests/forms/a.css'); $this->assertTrue(count($backend->get_css()) == 0, "There should be nothing in required css after file has been blocked."); }
public function testProcessOnlyIncludesRequirementsOnce() { $template = new SSViewer(array('SSViewerTestProcess')); $basePath = dirname($this->getCurrentRelativePath()) . '/forms'; $backend = new Requirements_Backend(); $backend->set_combined_files_enabled(false); $backend->combine_files('RequirementsTest_ab.css', array($basePath . '/RequirementsTest_a.css', $basePath . '/RequirementsTest_b.css')); Requirements::set_backend($backend); $this->assertEquals(1, substr_count($template->process(array()), "a.css")); $this->assertEquals(1, substr_count($template->process(array()), "b.css")); // if we disable the requirements then we should get nothing $template->includeRequirements(false); $this->assertEquals(0, substr_count($template->process(array()), "a.css")); $this->assertEquals(0, substr_count($template->process(array()), "b.css")); }
public function include_in_response(\SS_HTTPResponse $response) { if ($this->Eventful()) { $this->Eventful()->fire('assets:beforeProcessResponse', $response); } $this->assets(); parent::include_in_response($response); if (Director::is_ajax()) { $this->_response = $response; } $this->attachCustomScriptsToResponse(); if ($this->Eventful()) { $this->Eventful()->fire('assets:afterProcessResponse', $response); } }
public function testSuffix() { $template = '<html><head></head><body><header>My header</header><p>Body</p></body></html>'; $basePath = $this->getCurrentRelativePath(); $basePath = 'framework' . substr($basePath, strlen(FRAMEWORK_DIR)); $backend = new Requirements_Backend(); $backend->javascript($basePath . '/RequirementsTest_a.js'); $backend->javascript($basePath . '/RequirementsTest_b.js?foo=bar&bla=blubb'); $backend->css($basePath . '/RequirementsTest_a.css'); $backend->css($basePath . '/RequirementsTest_b.css?foo=bar&bla=blubb'); $backend->set_suffix_requirements(true); $html = $backend->includeInHTML(false, $template); $this->assertRegexp('/RequirementsTest_a\\.js\\?m=[\\d]*"/', $html); $this->assertRegexp('/RequirementsTest_b\\.js\\?m=[\\d]*&foo=bar&bla=blubb"/', $html); $this->assertRegexp('/RequirementsTest_a\\.css\\?m=[\\d]*"/', $html); $this->assertRegexp('/RequirementsTest_b\\.css\\?m=[\\d]*&foo=bar&bla=blubb"/', $html); $backend->set_suffix_requirements(false); $html = $backend->includeInHTML(false, $template); $this->assertNotContains('RequirementsTest_a.js=', $html); $this->assertNotRegexp('/RequirementsTest_a\\.js\\?m=[\\d]*"/', $html); $this->assertNotRegexp('/RequirementsTest_b\\.js\\?m=[\\d]*&foo=bar&bla=blubb"/', $html); $this->assertNotRegexp('/RequirementsTest_a\\.css\\?m=[\\d]*"/', $html); $this->assertNotRegexp('/RequirementsTest_b\\.css\\?m=[\\d]*&foo=bar&bla=blubb"/', $html); }
function css($file, $media = null) { /* Only initiate if webiste is in dev mode or a ?flush is called */ if (preg_match('/\\.less$/i', $file) || Director::isDev() || isset($_GET['flush'])) { /* If file is CSS, check if there is a LESS file */ if (preg_match('/\\.css$/i', $file)) { $less = preg_replace('/\\.css$/i', '.less', $file); if (is_file(Director::getAbsFile($less))) { $file = $less; } } /* If less file exists, then check/compile it */ if (preg_match('/\\.less$/i', $file)) { $out = preg_replace('/\\.less$/i', '.css', $file); $css_file = Director::getAbsFile($out); $options = array(); /* Automatically compress if in live mode */ if (Director::isLive()) { $options['compress'] = true; } try { /* Force recompile & only write to css if updated */ if (isset($_GET['flush']) || !Director::isLive()) { /* Force deleting of all cache files on flush */ if (file_exists(self::$cacheDir) && isset($_GET['flush']) && !self::$already_flushed) { $paths = new RecursiveIteratorIterator(new RecursiveDirectoryIterator(self::$cacheDir, FilesystemIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST); foreach ($paths as $path) { $path->isDir() && !$path->isLink() ? rmdir($path->getPathname()) : unlink($path->getPathname()); } /* make sure we only flush once per request and not for each *.less */ self::$already_flushed = true; } /* Set cache directory */ $options['cache_dir'] = self::$cacheDir; /* Set cache method */ $options['cache_method'] = self::$cacheMethod; /* Calculate the LESS file's parent URL */ $css_dir = dirname(Director::baseURL() . $file) . '/'; /* Generate and return cached file path */ $cached_file = self::$cacheDir . '/' . Less_Cache::Get(array(Director::getAbsFile($file) => $css_dir), $options, self::$variables); /* check cache vs. css and overwrite if necessary */ if (!is_file($css_file) || md5_file($css_file) != md5_file($cached_file)) { copy($cached_file, $css_file); } } } catch (Exception $ex) { trigger_error('Less.php fatal error: ' . $ex->getMessage(), E_USER_ERROR); } $file = $out; } } /* Return css file path */ return parent::css($file, $media); }
/** * Update the given HTML content with the appropriate include tags for the registered * requirements. Needs to receive a valid HTML/XHTML template in the $content parameter, * including a <head> tag. The requirements will insert before the closing <head> tag automatically. * * @todo Calculate $prefix properly * * @param string $templateFilePath Absolute path for the *.ss template file * @param string $content HTML content that has already been parsed from the $templateFilePath through {@link SSViewer}. * @return string HTML content thats augumented with the requirements before the closing <head> tag. */ function includeInHTML($templateFile, $content) { if ($this->isBackendController()) { //currently, it's not loading tinymce otherwise return parent::includeInHTML($templateFile, $content); } $hasHead = strpos($content, '</head>') !== false || strpos($content, '</head ') !== false; $hasRequirements = $this->css || $this->javascript || $this->customCSS || $this->customScript || $this->customHeadTags; if (!$hasHead || !$hasRequirements) { return $content; } $script_template = "<script type=\"text/javascript\">\n//<![CDATA[\n" . "%s" . "\n//]]>\n</script>\n"; $ready_template = "head.ready(function() {\n" . "%s" . "\n});\n"; $readyRequirements = $cleanRequirements = ''; // Include HeadJS, the only script in your head $headerRequirements = "<script type=\"text/javascript\" src=\"" . self::getHeadJsUrl() . "\"></script>\n"; // Combine files - updates $this->javascript and $this->css $this->process_combined_files(); $named_files = self::getNamedFiles(); // add the css requirements, even allow a callback $cssFiles = array_diff_key($this->css, $this->blocked); if (!empty($cssFiles)) { foreach ($cssFiles as $file => $params) { $name = str_replace('-', '', basename($file, '.css')); $path = Convert::raw2xml($this->path_for_file($file)); if ($path) { if (isset($params['media']) && !empty($params['media'])) { $headerRequirements .= "<link rel=\"stylesheet\" type=\"text/css\" media=\"{$params['media']}\" href=\"{$path}\" />\n"; } else { $path = str_replace('&', '&', $path); if ($named_files) { $path = $name . '":"' . $path; } $callback = $this->get_callback($file) ? ", function(){" . $this->get_callback($file) . "}" : ""; $readyRequirements .= "head.load(\"" . $path . "\"{$callback});\n"; } } } } $jsFiles = array_diff_key($this->javascript, $this->blocked); if (!empty($jsFiles)) { foreach ($jsFiles as $file => $dummy) { $name = str_replace('-', '', basename($file, '.js')); $path = Convert::raw2xml($this->path_for_file($file)); $path = str_replace('&', '&', $path); if ($path) { if ($named_files) { $path = $name . '":"' . $path; } $callback = $this->get_callback($file) ? ", function(){" . $this->get_callback($file) . "}" : ""; $readyRequirements .= "head.load(\"" . $path . "\"{$callback});\n"; } } } // @todo: store all css files in arrays with media param as the key, then do a headjs test for each mediaparam // store all "custom" css in the header, because this may have critical styling to fix FOUC issues $customCSS = array_diff_key($this->customCSS, $this->blocked); if (!empty($customCSS)) { foreach ($customCSS as $css) { $headerRequirements .= "<style type=\"text/css\">\n{$css}\n</style>\n"; } } // add all inline javascript *after* including external files which // they might rely on $customJS = array_diff_key($this->customScript, $this->blocked); if (!empty($customJS)) { foreach ($customJS as $script) { if (in_array($script, self::$do_not_wrap)) { $cleanRequirements .= $script; } else { $readyRequirements .= $script; } } } foreach (array_diff_key($this->customHeadTags, $this->blocked) as $customHeadTag) { $headerRequirements .= "{$customHeadTag}\n"; } // put the core js just before the </head> $content = preg_replace("/(<\\/head>)/i", $headerRequirements . "\\1", $content); $readyRequirements = $readyRequirements == "" ? "" : sprintf($ready_template, $readyRequirements); $readyRequirements = sprintf($script_template, $readyRequirements . $cleanRequirements); if ($this->force_js_to_bottom) { // Remove all newlines from code to preserve layout $readyRequirements = preg_replace('/>\\n*/', '>', $readyRequirements); // We put script tags into the body, for performance. // We forcefully put it at the bottom instead of before // the first script-tag occurence $content = preg_replace("/(<\\/body[^>]*>)/i", $readyRequirements . "\\1", $content); } elseif ($this->write_js_to_body) { // Remove all newlines from code to preserve layout $readyRequirements = preg_replace('/>\\n*/', '>', $readyRequirements); // We put script tags into the body, for performance. // If your template already has script tags in the body, then we put our script // tags just before those. Otherwise, we put it at the bottom. $p2 = stripos($content, '<body'); $p1 = stripos($content, '<script', $p2); // @todo - should we move the inline script tags below everything else? // we could do this with regex, or a while loop... if ($p1 !== false) { $content = substr($content, 0, $p1) . $readyRequirements . substr($content, $p1); } else { $content = preg_replace("/(<\\/body[^>]*>)/i", $readyRequirements . "\\1", $content); } } else { $content = preg_replace("/(<\\/head>)/i", $readyRequirements . "\\1", $content); } return $content; }
/** * Get files of the given type from the backend * * @param Requirements_Backend $backend * @param string $type js or css * @return array */ protected function getBackendFiles($backend, $type) { $type = strtolower($type); switch (strtolower($type)) { case 'css': return $backend->getCSS(); case 'js': case 'javascript': case 'script': $scripts = $backend->getJavascript(); return array_combine(array_values($scripts), array_values($scripts)); } return array(); }
function testRequirementsBackend() { $basePath = $this->getCurrentRelativePath(); $backend = new Requirements_Backend(); $backend->javascript($basePath . '/a.js'); $this->assertTrue(count($backend->get_javascript()) == 1, "There should be only 1 file included in required javascript."); $this->assertTrue(in_array($basePath . '/a.js', $backend->get_javascript()), "a.js should be included in required javascript."); $backend->javascript($basePath . '/b.js'); $this->assertTrue(count($backend->get_javascript()) == 2, "There should be 2 files included in required javascript."); $backend->block($basePath . '/a.js'); $this->assertTrue(count($backend->get_javascript()) == 1, "There should be only 1 file included in required javascript."); $this->assertFalse(in_array($basePath . '/a.js', $backend->get_javascript()), "a.js should not be included in required javascript after it has been blocked."); $this->assertTrue(in_array($basePath . '/b.js', $backend->get_javascript()), "b.js should be included in required javascript."); $backend->css($basePath . '/a.css'); $this->assertTrue(count($backend->get_css()) == 1, "There should be only 1 file included in required css."); $this->assertArrayHasKey($basePath . '/a.css', $backend->get_css(), "a.css should be in required css."); $backend->block($basePath . '/a.css'); $this->assertTrue(count($backend->get_css()) == 0, "There should be nothing in required css after file has been blocked."); }
function testBlockedInRender() { $backend = new Requirements_Backend(); $backend->js('tests/phpunit/data/RequirementsTest_a.js'); $backend->js('tests/phpunit/data/RequirementsTest_a.css'); $backend->js('tests/phpunit/data/RequirementsTest_b.js'); $backend->js('tests/phpunit/data/RequirementsTest_b.css'); $backend->js('tests/phpunit/data/RequirementsTest_c.js'); $backend->js('tests/phpunit/data/RequirementsTest_c.css'); $backend->block('tests/phpunit/data/RequirementsTest_a.js'); $backend->block('tests/phpunit/data/RequirementsTest_a.css'); $backend->block('RequirementsTest_b.js'); $backend->block('RequirementsTest_b.css'); $html = $backend->render(); $this->assertNotContains('tests/phpunit/data/RequirementsTest_a.js', $html, 'RequirementsTest_a.js was blocked and should not appear in rendered HTML'); $this->assertNotContains('tests/phpunit/data/RequirementsTest_a.css', $html, 'RequirementsTest_a.css was blocked and should not appear in rendered HTML'); $this->assertNotContains('tests/phpunit/data/RequirementsTest_b.js', $html, 'RequirementsTest_b.js was blocked and should not appear in rendered HTML'); $this->assertNotContains('tests/phpunit/data/RequirementsTest_b.css', $html, 'RequirementsTest_b.css was blocked and should not appear in rendered HTML'); $this->assertContains('tests/phpunit/data/RequirementsTest_c.js', $html, 'RequirementsTest_c.js should appear in rendered HTML'); $this->assertContains('tests/phpunit/data/RequirementsTest_c.css', $html, 'RequirementsTest_c.css should appear in rendered HTML'); }
public function testJsWriteToBody() { $backend = new Requirements_Backend(); $backend->javascript('http://www.mydomain.com/test.js'); // Test matching with HTML5 <header> tags as well $template = '<html><head></head><body><header>My header</header><p>Body</p></body></html>'; $backend->set_write_js_to_body(false); $html = $backend->includeInHTML(false, $template); $this->assertContains('<head><script', $html); $backend->set_write_js_to_body(true); $html = $backend->includeInHTML(false, $template); $this->assertNotContains('<head><script', $html); $this->assertContains('</script></body>', $html); }