public function test_js_files() { global $CFG; $testfile1 = "{$CFG->tempdir}/test1.js"; $testfile2 = "{$CFG->tempdir}/test2.js"; $testfile3 = "{$CFG->tempdir}/test3.js"; $js1 = "\nfunction hm()\n{\n}\n"; $js2 = "function oh(){}"; file_put_contents($testfile1, $js1); file_put_contents($testfile2, $js2); $files = array($testfile1, $testfile2); $this->assertSame("function hm(){};\nfunction oh(){}", core_minify::js_files($files)); $files = array($testfile1, $testfile2, $testfile3); $this->assertStringStartsWith("function hm(){};\nfunction oh(){};\n\n\n// Cannot read JS file ", @core_minify::js_files($files)); unlink($testfile1); unlink($testfile2); }
/** * Minify CSS files. * * @deprecated since 2.6 * * @param array $files * @return string */ function css_minify_css($files) { debugging('css_minify_css() is deprecated, use core_minify::css_files() or core_minify::css() instead.'); return core_minify::css_files($files); }
/** * Get the whole css stylesheet for production mode. * * NOTE: this method is not expected to be used from any addons. * * @return string CSS markup, already optimised and compressed */ public function get_css_content() { global $CFG; require_once $CFG->dirroot . '/lib/csslib.php'; $csscontent = ''; foreach ($this->get_css_files(false) as $type => $value) { foreach ($value as $identifier => $val) { if (is_array($val)) { foreach ($val as $v) { $csscontent .= file_get_contents($v) . "\n"; } } else { if ($type === 'theme' && $identifier === $this->lessfile) { // We need the content from LESS because this is the LESS file from the theme. $csscontent .= $this->get_css_content_from_less(false); } else { $csscontent .= file_get_contents($val) . "\n"; } } } } $csscontent = $this->post_process($csscontent); if (!empty($CFG->enablecssoptimiser) && $this->supportscssoptimisation) { // This is an experimental feature introduced in Moodle 2.3 // The CSS optimiser organises the CSS in order to reduce the overall number // of rules and styles being sent to the client. It does this by collating // the CSS before it is cached removing excess styles and rules and stripping // out any extraneous content such as comments and empty rules. $optimiser = new css_optimiser(); $csscontent = $optimiser->process($csscontent); } else { $csscontent = core_minify::css($csscontent); } return $csscontent; }
/** * Allow setting of the config function described in {@see set_config_function} from a file. * The contents of this file are then passed to set_config_function. * * When jsrev is positive, the function is minified and stored in a MUC cache for subsequent uses. * * @param $file The path to the JavaScript function used for YUI configuration. * @return String the name of the function to use in the group pattern configuration. */ public function set_config_source($file) { global $CFG; $cache = cache::make('core', 'yuimodules'); // Attempt to get the metadata from the cache. $keyname = 'configfn_' . $file; $fullpath = $CFG->dirroot . '/' . $file; if (!isset($CFG->jsrev) || $CFG->jsrev == -1) { $cache->delete($keyname); $configfn = file_get_contents($fullpath); } else { $configfn = $cache->get($keyname); if ($configfn === false) { require_once $CFG->libdir . '/jslib.php'; $configfn = core_minify::js_files(array($fullpath)); $cache->set($keyname, $configfn); } } return $this->set_config_function($configfn); }
if ($rev > 0 and file_exists($candidate)) { if (!empty($_SERVER['HTTP_IF_NONE_MATCH']) || !empty($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { // we do not actually need to verify the etag value because our files // never change in cache because we increment the rev parameter js_send_unmodified(filemtime($candidate), $etag); } js_send_cached($candidate, $etag); } //================================================================================= // ok, now we need to start normal moodle script, we need to load all libs and $DB define('ABORT_AFTER_CONFIG_CANCEL', true); define('NO_MOODLE_COOKIES', true); // Session not used here define('NO_UPGRADE_CHECK', true); // Ignore upgrade check require "{$CFG->dirroot}/lib/setup.php"; $theme = theme_config::load($themename); $themerev = theme_get_revision(); if ($themerev <= 0 or $rev != $themerev) { // Do not send caching headers if they do not request current revision, // we do not want to pollute browser caches with outdated JS. js_send_uncached($theme->javascript_content($type)); } make_localcache_directory('theme', false); js_write_cache_file_content($candidate, core_minify::js_files($theme->javascript_files($type))); // Verify nothing failed in cache file creation. clearstatcache(); if (file_exists($candidate)) { js_send_cached($candidate, $etag); } js_send_uncached($theme->javascript_content($type));
/** * Get the whole css stylesheet for production mode. * * NOTE: this method is not expected to be used from any addons. * * @return string CSS markup compressed */ public function get_css_content() { $csscontent = ''; foreach ($this->get_css_files(false) as $type => $value) { foreach ($value as $identifier => $val) { if (is_array($val)) { foreach ($val as $v) { $csscontent .= file_get_contents($v) . "\n"; } } else { if ($type === 'theme' && $identifier === $this->scssfile) { // We need the content from SCSS because this is the SCSS file from the theme. $csscontent .= $this->get_css_content_from_scss(false); } else { if ($type === 'theme' && $identifier === $this->lessfile) { // We need the content from LESS because this is the LESS file from the theme. $csscontent .= $this->get_css_content_from_less(false); } else { $csscontent .= file_get_contents($val) . "\n"; } } } } } $csscontent = $this->post_process($csscontent); $csscontent = core_minify::css($csscontent); return $csscontent; }
// If-Modified-Since requests so there is no need to handle them specially. header('Expires: ' . date('r', time() + 365 * 24 * 3600)); header('Cache-Control: max-age=' . 365 * 24 * 3600); // Pragma is set to no-cache by default so must be overridden. header('Pragma:'); } // Get the right MIME type. $mimetype = mimeinfo('type', $file); // For JS files, these can be minified and stored in cache. if ($mimetype === 'application/x-javascript' && $allowcache) { // The cached file is stored without version number etc. This is okay // because $CFG->cachedir is cleared each time there is a plugin update, // such as a new version of a tinymce plugin. // Flatten filename and include cache location. $cache = $CFG->cachedir . '/editor_tinymce/pluginjs'; $cachefile = $cache . '/' . $tinymceplugin . str_replace('/', '_', $innerpath); // If it doesn't exist, minify it and save to that location. if (!file_exists($cachefile)) { $content = core_minify::js_files(array($file)); js_write_cache_file_content($cachefile, $content); } $file = $cachefile; } else { if ($mimetype === 'text/html') { header('X-UA-Compatible: IE=edge'); } } // Serve file. header('Content-Length: ' . filesize($file)); header('Content-Type: ' . $mimetype); readfile($file);
$jsfiles[] = $jsfile; } if (!$jsfiles) { // bad luck - no valid files die; } $etag = sha1($rev . implode(',', $jsfiles)); // Use the caching only for meaningful revision numbers which prevents future cache poisoning. if ($rev > 0 and $rev < time() + 60 * 60) { $candidate = $CFG->localcachedir . '/js/' . $etag; if (file_exists($candidate)) { if (!empty($_SERVER['HTTP_IF_NONE_MATCH']) || !empty($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { // we do not actually need to verify the etag value because our files // never change in cache because we increment the rev parameter js_send_unmodified(filemtime($candidate), $etag); } js_send_cached($candidate, $etag); } else { js_write_cache_file_content($candidate, core_minify::js_files($jsfiles)); // verify nothing failed in cache file creation clearstatcache(); if (file_exists($candidate)) { js_send_cached($candidate, $etag); } } } $content = ''; foreach ($jsfiles as $jsfile) { $content .= file_get_contents($jsfile) . "\n"; } js_send_uncached($content, $etag);
/** * Stores CSS in a file at the given path. * * This function either succeeds or throws an exception. * * @param theme_config $theme The theme that the CSS belongs to. * @param string $csspath The path to store the CSS at. * @param array $cssfiles The CSS files to store. * @param bool $chunk If set to true these files will be chunked to ensure * that no one file contains more than 4095 selectors. * @param string $chunkurl If the CSS is be chunked then we need to know the URL * to use for the chunked files. */ function css_store_css(theme_config $theme, $csspath, array $cssfiles, $chunk = false, $chunkurl = null) { global $CFG; $css = ''; foreach ($cssfiles as $file) { $css .= file_get_contents($file) . "\n"; } // Check if both the CSS optimiser is enabled and the theme supports it. if (!empty($CFG->enablecssoptimiser) && $theme->supportscssoptimisation) { // This is an experimental feature introduced in Moodle 2.3 // The CSS optimiser organises the CSS in order to reduce the overall number // of rules and styles being sent to the client. It does this by collating // the CSS before it is cached removing excess styles and rules and stripping // out any extraneous content such as comments and empty rules. $optimiser = new css_optimiser(); $css = $theme->post_process($css); $css = $optimiser->process($css); // If cssoptimisestats is set then stats from the optimisation are collected // and output at the beginning of the CSS. if (!empty($CFG->cssoptimiserstats)) { $css = $optimiser->output_stats_css() . $css; } } else { // This is the default behaviour. // The cssoptimise setting was introduced in Moodle 2.3 and will hopefully // in the future be changed from an experimental setting to the default. // The css_minify_css will method will use the Minify library remove // comments, additional whitespace and other minor measures to reduce the // the overall CSS being sent. // However it has the distinct disadvantage of having to minify the CSS // before running the post process functions. Potentially things may break // here if theme designers try to push things with CSS post processing. $css = $theme->post_process($css); $css = core_minify::css($css); } clearstatcache(); if (!file_exists(dirname($csspath))) { @mkdir(dirname($csspath), $CFG->directorypermissions, true); } // Prevent serving of incomplete file from concurrent request, // the rename() should be more atomic than fwrite(). ignore_user_abort(true); // First up write out the single file for all those using decent browsers. css_write_file($csspath, $css); if ($chunk) { // If we need to chunk the CSS for browsers that are sub-par. $css = css_chunk_by_selector_count($css, $chunkurl); $files = count($css); $count = 1; foreach ($css as $content) { if ($count === $files) { // If there is more than one file and this IS the last file. $filename = preg_replace('#\\.css$#', '.0.css', $csspath); } else { // If there is more than one file and this is not the last file. $filename = preg_replace('#\\.css$#', '.' . $count . '.css', $csspath); } $count++; css_write_file($filename, $content); } } ignore_user_abort(false); if (connection_aborted()) { die; } }