Example #1
0
 public function test_background()
 {
     $optimiser = new css_optimiser();
     $cssin = '.test {background-color: #123456;}';
     $this->assertSame($cssin, $optimiser->process($cssin));
     $this->assertDebuggingCalled('class css_optimiser is deprecated and no longer does anything, ' . 'please consider using stylelint to optimise your css.');
 }
Example #2
0
/**
 * 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.
 */
function css_store_css(theme_config $theme, $csspath, array $cssfiles) {
    global $CFG;

    // 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 = '';
        foreach ($cssfiles as $file) {
            $css .= file_get_contents($file)."\n";
        }
        $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_minify_css($cssfiles));
    }

    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);
    if ($fp = fopen($csspath.'.tmp', 'xb')) {
        fwrite($fp, $css);
        fclose($fp);
        rename($csspath.'.tmp', $csspath);
        @chmod($csspath, $CFG->filepermissions);
        @unlink($csspath.'.tmp'); // just in case anything fails
    }
    ignore_user_abort(false);
    if (connection_aborted()) {
        die;
    }
}
Example #3
0
    /**
     * This function tests some of the broken crazy CSS we have in Moodle.
     * For each of these things the value needs to be corrected if we can be 100%
     * certain what is going wrong, Or it needs to be left as is.
     *
     * @param css_optimiser $optimiser
     */
    public function try_broken_css_found_in_moodle(css_optimiser $optimiser) {
        // Notice how things are out of order here but that they get corrected
        $cssin = '.test {background:url([[pix:theme|pageheaderbgred]]) top center no-repeat}';
        $cssout = '.test{background:url([[pix:theme|pageheaderbgred]]) no-repeat top center;}';
        $this->assertEqual($cssout, $optimiser->process($cssin));

        // Cursor hand isn't valid
        $cssin  = '.test {cursor: hand;}';
        $cssout = '.test{cursor:hand;}';
        $this->assertEqual($cssout, $optimiser->process($cssin));

        // Zoom property isn't valid
        $cssin  = '.test {zoom: 1;}';
        $cssout = '.test{zoom:1;}';
        $this->assertEqual($cssout, $optimiser->process($cssin));

        // Left isn't a valid position property
        $cssin  = '.test {position: left;}';
        $cssout = '.test{position:left;}';
        $this->assertEqual($cssout, $optimiser->process($cssin));

        // The dark red color isn't a valid HTML color but has a standardised
        // translation of #8B0000
        $cssin  = '.test {color: darkred;}';
        $cssout = '.test{color:#8B0000;}';
        $this->assertEqual($cssout, $optimiser->process($cssin));

        // You can't use argb colours as border colors
        $cssin  = '.test {border-bottom: 1px solid rgba(0,0,0,0.25);}';
        $cssout = '.test{border-bottom:1px solid rgba(0,0,0,0.25);}';
        $this->assertEqual($cssout, $optimiser->process($cssin));

        // Opacity with annoying IE equivilants....
        $cssin  = '.test {opacity: 0.5; -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; filter: alpha(opacity=50);}';
        $cssout = '.test{opacity:0.5;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";filter:alpha(opacity=50);}';
        $this->assertEqual($cssout, $optimiser->process($cssin));
    }
 /**
  * Get the theme designer css markup,
  * the parameters are coming from css_urls().
  *
  * NOTE: this method is not expected to be used from any addons.
  *
  * @param string $type
  * @param string $subtype
  * @param string $sheet
  * @return string CSS markup
  */
 public function get_css_content_debug($type, $subtype, $sheet)
 {
     global $CFG;
     require_once $CFG->dirroot . '/lib/csslib.php';
     // The LESS file of the theme is requested.
     if ($type === 'less') {
         $csscontent = $this->get_css_content_from_less(true);
         if ($csscontent !== false) {
             return $csscontent;
         }
         return '';
     }
     $optimiser = null;
     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();
     }
     $cssfiles = array();
     $css = $this->get_css_files(true);
     if ($type === 'ie') {
         // IE is a sloppy browser with weird limits, sorry.
         if ($subtype === 'plugins') {
             $cssfiles = $css['plugins'];
         } else {
             if ($subtype === 'parents') {
                 if (empty($sheet)) {
                     // Do not bother with the empty parent here.
                 } else {
                     // Build up the CSS for that parent so we can serve it as one file.
                     foreach ($css[$subtype][$sheet] as $parent => $css) {
                         $cssfiles[] = $css;
                     }
                 }
             } else {
                 if ($subtype === 'theme') {
                     $cssfiles = $css['theme'];
                     foreach ($cssfiles as $key => $value) {
                         if ($this->lessfile && $key === $this->lessfile) {
                             // Remove the LESS file from the theme CSS files.
                             // The LESS files use the type 'less', not 'ie'.
                             unset($cssfiles[$key]);
                         }
                     }
                 }
             }
         }
     } else {
         if ($type === 'plugin') {
             if (isset($css['plugins'][$subtype])) {
                 $cssfiles[] = $css['plugins'][$subtype];
             }
         } else {
             if ($type === 'parent') {
                 if (isset($css['parents'][$subtype][$sheet])) {
                     $cssfiles[] = $css['parents'][$subtype][$sheet];
                 }
             } else {
                 if ($type === 'theme') {
                     if (isset($css['theme'][$sheet])) {
                         $cssfiles[] = $css['theme'][$sheet];
                     }
                 }
             }
         }
     }
     $csscontent = '';
     foreach ($cssfiles as $file) {
         $contents = file_get_contents($file);
         $contents = $this->post_process($contents);
         $comment = "/** Path: {$type} {$subtype} {$sheet}.' **/\n";
         $stats = '';
         if ($optimiser) {
             $contents = $optimiser->process($contents);
             if (!empty($CFG->cssoptimiserstats)) {
                 $stats = $optimiser->output_stats_css();
             }
         }
         $csscontent .= $comment . $stats . $contents . "\n\n";
     }
     return $csscontent;
 }
Example #5
0
/**
 * Sends CSS directly without caching it.
 *
 * This function takes a raw CSS string, optimises it if required, and then
 * serves it.
 * Turning both themedesignermode and CSS optimiser on at the same time is aweful
 * for performance because of the optimiser running here. However it was done so
 * that theme designers could utilise the optimised output during development to
 * help them optimise their CSS... not that they should write lazy CSS.
 *
 * @param string CSS
 */
function css_send_uncached_css($css)
{
    global $CFG;
    header('Content-Disposition: inline; filename="styles_debug.php"');
    header('Last-Modified: ' . gmdate('D, d M Y H:i:s', time()) . ' GMT');
    header('Expires: ' . gmdate('D, d M Y H:i:s', time() + THEME_DESIGNER_CACHE_LIFETIME) . ' GMT');
    header('Pragma: ');
    header('Accept-Ranges: none');
    header('Content-Type: text/css; charset=utf-8');
    if (is_array($css)) {
        $css = implode("\n\n", $css);
    }
    if (!empty($CFG->enablecssoptimiser)) {
        $css = str_replace("\n", "\r\n", $css);
        $optimiser = new css_optimiser();
        $css = $optimiser->process($css);
        if (!empty($CFG->cssoptimiserstats)) {
            $css = $optimiser->output_stats_css() . $css;
        }
    }
    echo $css;
    die;
}
Example #6
0
 /**
  * Test media declarations.
  */
 public function test_media_rules()
 {
     $optimiser = new css_optimiser();
     $cssin = "@media print {\n  .test{background-color:#333;}\n}";
     $cssout = "@media print { .test{background-color:#333;} }";
     $this->assertSame($cssout, $optimiser->process($cssin));
     $cssin = "@media screen and (min-width:30px) {\n  #region-main-box{left: 30px;float: left;}\n}";
     $cssout = "@media screen and (min-width:30px) { #region-main-box{left:30px;float:left;} }";
     $this->assertSame($cssout, $optimiser->process($cssin));
     $cssin = "@media all and (min-width:500px) {\n  #region-main-box{left:30px;float:left;}\n}";
     $cssout = "@media all and (min-width:500px) { #region-main-box{left:30px;float:left;} }";
     $this->assertSame($cssout, $optimiser->process($cssin));
     $cssin = "@media (min-width:500px) {\n  #region-main-box{left:30px;float:left;}\n}";
     $cssout = "@media (min-width:500px) { #region-main-box{left:30px;float:left;} }";
     $this->assertSame($cssout, $optimiser->process($cssin));
     $cssin = "@media screen and (color), projection and (color) {\n  #region-main-box{left:30px;float:left;}\n}";
     $cssout = "@media screen and (color),projection and (color) { #region-main-box{left:30px;float:left;} }";
     $this->assertSame($cssout, $optimiser->process($cssin));
     $cssin = "@media print {\n  .test{background-color:#000;}\n}@media print {\n  .test{background-color:#FFF;}\n}";
     $cssout = "@media print { .test{background-color:#FFF;} }";
     $this->assertSame($cssout, $optimiser->process($cssin));
     $cssin = "@media screen and (min-width:30px) {\n  #region-main-box{background-color:#000;}\n}\n@media screen and (min-width:30px) {\n  #region-main-box{background-color:#FFF;}\n}";
     $cssout = "@media screen and (min-width:30px) { #region-main-box{background-color:#FFF;} }";
     $this->assertSame($cssout, $optimiser->process($cssin));
     $cssin = "@media screen and (min-width:30px) {\n  #region-main-box{background-color:#000;}\n}\n@media screen and (min-width:31px) {\n  #region-main-box{background-color:#FFF;}\n}";
     $cssout = "@media screen and (min-width:30px) { #region-main-box{background-color:#000;} }\n@media screen and (min-width:31px) { #region-main-box{background-color:#FFF;} }";
     $this->assertSame($cssout, $optimiser->process($cssin));
     $cssin = "@media (min-width: 768px) and (max-width: 979px) {\n*{*zoom:1;}}";
     $cssout = "@media (min-width: 768px) and (max-width: 979px) { *{*zoom:1;} }";
     $this->assertSame($cssout, $optimiser->process($cssin));
     $cssin = "#test {min-width:1200px;}@media (min-width: 768px) {#test {min-width: 1024px;}}";
     $cssout = "#test{min-width:1200px;} \n@media (min-width: 768px) { #test{min-width:1024px;} }";
     $this->assertSame($cssout, $optimiser->process($cssin));
     $cssin = "@media(min-width:768px){#page-calender-view .container fluid{min-width:1024px}}.section_add_menus{text-align:right}";
     $cssout = ".section_add_menus{text-align:right;} \n@media (min-width:768px) { #page-calender-view .container fluid{min-width:1024px;} }";
     $this->assertSame($cssout, $optimiser->process($cssin));
     $cssin = "@-ms-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}";
     $cssout = "@-ms-keyframes progress-bar-stripes {from{background-position:40px 0;}to{background-position:0 0;}}";
     $this->assertSame($cssout, $optimiser->process($cssin));
 }
Example #7
0
/**
 * 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;
    }
}