mirror of
https://github.com/moodle/moodle.git
synced 2025-08-08 10:26:40 +02:00
Merge branch 'w10_MDL-44358_m27_stylesrefact' of https://github.com/skodak/moodle
This commit is contained in:
commit
d129de3761
5 changed files with 282 additions and 283 deletions
|
@ -15,10 +15,12 @@
|
||||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This file contains CSS related class, and function for the CSS optimiser
|
* This file contains CSS related class, and function for the CSS optimiser.
|
||||||
*
|
*
|
||||||
* Please see the {@link css_optimiser} class for greater detail.
|
* Please see the {@link css_optimiser} class for greater detail.
|
||||||
*
|
*
|
||||||
|
* NOTE: these functions are not expected to be used from any addons.
|
||||||
|
*
|
||||||
* @package core
|
* @package core
|
||||||
* @subpackage cssoptimiser
|
* @subpackage cssoptimiser
|
||||||
* @copyright 2012 Sam Hemelryk
|
* @copyright 2012 Sam Hemelryk
|
||||||
|
@ -27,6 +29,12 @@
|
||||||
|
|
||||||
defined('MOODLE_INTERNAL') || die();
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
|
if (!defined('THEME_DESIGNER_CACHE_LIFETIME')) {
|
||||||
|
// This can be also set in config.php file,
|
||||||
|
// it needs to be higher than the time it takes to generate all CSS content.
|
||||||
|
define('THEME_DESIGNER_CACHE_LIFETIME', 10);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores CSS in a file at the given path.
|
* Stores CSS in a file at the given path.
|
||||||
*
|
*
|
||||||
|
@ -34,50 +42,15 @@ defined('MOODLE_INTERNAL') || die();
|
||||||
*
|
*
|
||||||
* @param theme_config $theme The theme that the CSS belongs to.
|
* @param theme_config $theme The theme that the CSS belongs to.
|
||||||
* @param string $csspath The path to store the CSS at.
|
* @param string $csspath The path to store the CSS at.
|
||||||
* @param array $cssfiles The CSS files to store.
|
* @param string $csscontent the complete CSS in one string
|
||||||
* @param bool $chunk If set to true these files will be chunked to ensure
|
* @param bool $chunk If set to true these files will be chunked to ensure
|
||||||
* that no one file contains more than 4095 selectors.
|
* 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
|
* @param string $chunkurl If the CSS is be chunked then we need to know the URL
|
||||||
* to use for the chunked files.
|
* to use for the chunked files.
|
||||||
*/
|
*/
|
||||||
function css_store_css(theme_config $theme, $csspath, array $cssfiles, $chunk = false, $chunkurl = null) {
|
function css_store_css(theme_config $theme, $csspath, $csscontent, $chunk = false, $chunkurl = null) {
|
||||||
global $CFG;
|
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();
|
clearstatcache();
|
||||||
if (!file_exists(dirname($csspath))) {
|
if (!file_exists(dirname($csspath))) {
|
||||||
@mkdir(dirname($csspath), $CFG->directorypermissions, true);
|
@mkdir(dirname($csspath), $CFG->directorypermissions, true);
|
||||||
|
@ -88,11 +61,11 @@ function css_store_css(theme_config $theme, $csspath, array $cssfiles, $chunk =
|
||||||
ignore_user_abort(true);
|
ignore_user_abort(true);
|
||||||
|
|
||||||
// First up write out the single file for all those using decent browsers.
|
// First up write out the single file for all those using decent browsers.
|
||||||
css_write_file($csspath, $css);
|
css_write_file($csspath, $csscontent);
|
||||||
|
|
||||||
if ($chunk) {
|
if ($chunk) {
|
||||||
// If we need to chunk the CSS for browsers that are sub-par.
|
// If we need to chunk the CSS for browsers that are sub-par.
|
||||||
$css = css_chunk_by_selector_count($css, $chunkurl);
|
$css = css_chunk_by_selector_count($csscontent, $chunkurl);
|
||||||
$files = count($css);
|
$files = count($css);
|
||||||
$count = 1;
|
$count = 1;
|
||||||
foreach ($css as $content) {
|
foreach ($css as $content) {
|
||||||
|
@ -240,6 +213,32 @@ function css_send_cached_css($csspath, $etag) {
|
||||||
die;
|
die;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a cached CSS content
|
||||||
|
*
|
||||||
|
* @param string $csscontent The actual CSS markup.
|
||||||
|
* @param string $etag The revision to make sure we utilise any caches.
|
||||||
|
*/
|
||||||
|
function css_send_cached_css_content($csscontent, $etag) {
|
||||||
|
// 60 days only - the revision may get incremented quite often.
|
||||||
|
$lifetime = 60*60*24*60;
|
||||||
|
|
||||||
|
header('Etag: "'.$etag.'"');
|
||||||
|
header('Content-Disposition: inline; filename="styles.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() + $lifetime) .' GMT');
|
||||||
|
header('Pragma: ');
|
||||||
|
header('Cache-Control: public, max-age='.$lifetime);
|
||||||
|
header('Accept-Ranges: none');
|
||||||
|
header('Content-Type: text/css; charset=utf-8');
|
||||||
|
if (!min_enable_zlib_compression()) {
|
||||||
|
header('Content-Length: '.strlen($csscontent));
|
||||||
|
}
|
||||||
|
|
||||||
|
echo($csscontent);
|
||||||
|
die;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends CSS directly without caching it.
|
* Sends CSS directly without caching it.
|
||||||
*
|
*
|
||||||
|
|
|
@ -55,6 +55,11 @@ function theme_reset_all_caches() {
|
||||||
|
|
||||||
set_config('themerev', $next); // time is unique even when you reset/switch database
|
set_config('themerev', $next); // time is unique even when you reset/switch database
|
||||||
|
|
||||||
|
if (!empty($CFG->themedesignermode)) {
|
||||||
|
$cache = cache::make_from_params(cache_store::MODE_APPLICATION, 'core', 'themedesigner');
|
||||||
|
$cache->purge();
|
||||||
|
}
|
||||||
|
|
||||||
if ($PAGE) {
|
if ($PAGE) {
|
||||||
$PAGE->reload_theme();
|
$PAGE->reload_theme();
|
||||||
}
|
}
|
||||||
|
@ -66,8 +71,9 @@ function theme_reset_all_caches() {
|
||||||
* @param bool $state
|
* @param bool $state
|
||||||
*/
|
*/
|
||||||
function theme_set_designer_mod($state) {
|
function theme_set_designer_mod($state) {
|
||||||
theme_reset_all_caches();
|
|
||||||
set_config('themedesignermode', (int)!empty($state));
|
set_config('themedesignermode', (int)!empty($state));
|
||||||
|
// Reset caches after switching mode so that any designer mode caches get purged too.
|
||||||
|
theme_reset_all_caches();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -594,14 +600,12 @@ class theme_config {
|
||||||
/**
|
/**
|
||||||
* Returns the content of the CSS to be used in editor content
|
* Returns the content of the CSS to be used in editor content
|
||||||
*
|
*
|
||||||
* @return string
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function editor_css_files() {
|
public function editor_css_files() {
|
||||||
global $CFG;
|
|
||||||
|
|
||||||
$files = array();
|
$files = array();
|
||||||
|
|
||||||
// first editor plugins
|
// First editor plugins.
|
||||||
$plugins = core_component::get_plugin_list('editor');
|
$plugins = core_component::get_plugin_list('editor');
|
||||||
foreach ($plugins as $plugin=>$fulldir) {
|
foreach ($plugins as $plugin=>$fulldir) {
|
||||||
$sheetfile = "$fulldir/editor_styles.css";
|
$sheetfile = "$fulldir/editor_styles.css";
|
||||||
|
@ -609,8 +613,8 @@ class theme_config {
|
||||||
$files['plugin_'.$plugin] = $sheetfile;
|
$files['plugin_'.$plugin] = $sheetfile;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// then parent themes
|
// Then parent themes - base first, the immediate parent last.
|
||||||
foreach (array_reverse($this->parent_configs) as $parent_config) { // base first, the immediate parent last
|
foreach (array_reverse($this->parent_configs) as $parent_config) {
|
||||||
if (empty($parent_config->editor_sheets)) {
|
if (empty($parent_config->editor_sheets)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -621,7 +625,7 @@ class theme_config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// finally this theme
|
// Finally this theme.
|
||||||
if (!empty($this->editor_sheets)) {
|
if (!empty($this->editor_sheets)) {
|
||||||
foreach ($this->editor_sheets as $sheet) {
|
foreach ($this->editor_sheets as $sheet) {
|
||||||
$sheetfile = "$this->dir/style/$sheet.css";
|
$sheetfile = "$this->dir/style/$sheet.css";
|
||||||
|
@ -635,10 +639,10 @@ class theme_config {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the stylesheet URL of this theme
|
* Get the stylesheet URL of this theme.
|
||||||
*
|
*
|
||||||
* @param moodle_page $page Not used... deprecated?
|
* @param moodle_page $page Not used... deprecated?
|
||||||
* @return array of moodle_url
|
* @return moodle_url[]
|
||||||
*/
|
*/
|
||||||
public function css_urls(moodle_page $page) {
|
public function css_urls(moodle_page $page) {
|
||||||
global $CFG;
|
global $CFG;
|
||||||
|
@ -679,50 +683,7 @@ class theme_config {
|
||||||
$urls[] = $url;
|
$urls[] = $url;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// find out the current CSS and cache it now for 5 seconds
|
$css = $this->get_css_files(true);
|
||||||
// the point is to construct the CSS only once and pass it through the
|
|
||||||
// dataroot to the script that actually serves the sheets
|
|
||||||
if (!defined('THEME_DESIGNER_CACHE_LIFETIME')) {
|
|
||||||
define('THEME_DESIGNER_CACHE_LIFETIME', 4); // this can be also set in config.php
|
|
||||||
}
|
|
||||||
$candidatedir = "$CFG->cachedir/theme/$this->name";
|
|
||||||
if ($svg) {
|
|
||||||
$candidatesheet = "$candidatedir/designer.ser";
|
|
||||||
} else {
|
|
||||||
$candidatesheet = "$candidatedir/designer_nosvg.ser";
|
|
||||||
}
|
|
||||||
$rebuild = true;
|
|
||||||
if (file_exists($candidatesheet) and filemtime($candidatesheet) > time() - THEME_DESIGNER_CACHE_LIFETIME) {
|
|
||||||
if ($css = file_get_contents($candidatesheet)) {
|
|
||||||
$css = unserialize($css);
|
|
||||||
if (is_array($css)) {
|
|
||||||
$rebuild = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($rebuild) {
|
|
||||||
// Prepare the CSS optimiser if it is to be used,
|
|
||||||
// please note that it may be very slow and is therefore strongly discouraged in theme designer mode.
|
|
||||||
$optimiser = null;
|
|
||||||
if (!empty($CFG->enablecssoptimiser) && $this->supportscssoptimisation) {
|
|
||||||
require_once($CFG->dirroot.'/lib/csslib.php');
|
|
||||||
$optimiser = new css_optimiser;
|
|
||||||
}
|
|
||||||
$css = $this->css_content($optimiser);
|
|
||||||
|
|
||||||
// We do not want any errors here because this may fail easily because of the concurrent access.
|
|
||||||
$prevabort = ignore_user_abort(true);
|
|
||||||
check_dir_exists($candidatedir);
|
|
||||||
$tempfile = tempnam($candidatedir, 'tmpdesigner');
|
|
||||||
file_put_contents($tempfile, serialize($css));
|
|
||||||
$reporting = error_reporting(0);
|
|
||||||
chmod($tempfile, $CFG->filepermissions);
|
|
||||||
unlink($candidatesheet); // Do not rely on rename() deleting original, they may decide to change it at any time as usually.
|
|
||||||
rename($tempfile, $candidatesheet);
|
|
||||||
error_reporting($reporting);
|
|
||||||
ignore_user_abort($prevabort);
|
|
||||||
}
|
|
||||||
|
|
||||||
$baseurl = new moodle_url($CFG->httpswwwroot.'/theme/styles_debug.php');
|
$baseurl = new moodle_url($CFG->httpswwwroot.'/theme/styles_debug.php');
|
||||||
if (!$svg) {
|
if (!$svg) {
|
||||||
// We add an SVG param so that we know not to serve SVG images.
|
// We add an SVG param so that we know not to serve SVG images.
|
||||||
|
@ -730,10 +691,10 @@ class theme_config {
|
||||||
$baseurl->param('svg', '0');
|
$baseurl->param('svg', '0');
|
||||||
}
|
}
|
||||||
if (core_useragent::is_ie()) {
|
if (core_useragent::is_ie()) {
|
||||||
// lalala, IE does not allow more than 31 linked CSS files from main document
|
// Lalala, IE does not allow more than 31 linked CSS files from main document.
|
||||||
$urls[] = new moodle_url($baseurl, array('theme'=>$this->name, 'type'=>'ie', 'subtype'=>'plugins'));
|
$urls[] = new moodle_url($baseurl, array('theme'=>$this->name, 'type'=>'ie', 'subtype'=>'plugins'));
|
||||||
foreach ($css['parents'] as $parent=>$sheets) {
|
foreach ($css['parents'] as $parent=>$sheets) {
|
||||||
// We need to serve parents individually otherwise we may easily exceed the style limit IE imposes (4096)
|
// We need to serve parents individually otherwise we may easily exceed the style limit IE imposes (4096).
|
||||||
$urls[] = new moodle_url($baseurl, array('theme'=>$this->name,'type'=>'ie', 'subtype'=>'parents', 'sheet'=>$parent));
|
$urls[] = new moodle_url($baseurl, array('theme'=>$this->name,'type'=>'ie', 'subtype'=>'parents', 'sheet'=>$parent));
|
||||||
}
|
}
|
||||||
$urls[] = new moodle_url($baseurl, array('theme'=>$this->name, 'type'=>'ie', 'subtype'=>'theme'));
|
$urls[] = new moodle_url($baseurl, array('theme'=>$this->name, 'type'=>'ie', 'subtype'=>'theme'));
|
||||||
|
@ -748,7 +709,8 @@ class theme_config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
foreach ($css['theme'] as $sheet=>$unused) {
|
foreach ($css['theme'] as $sheet=>$unused) {
|
||||||
$urls[] = new moodle_url($baseurl, array('sheet'=>$sheet, 'theme'=>$this->name, 'type'=>'theme')); // sheet first in order to make long urls easier to read
|
// Sheet first in order to make long urls easier to read.
|
||||||
|
$urls[] = new moodle_url($baseurl, array('sheet'=>$sheet, 'theme'=>$this->name, 'type'=>'theme'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -757,14 +719,169 @@ class theme_config {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an array of organised CSS files required for this output
|
* Get the whole css stylesheet for production mode.
|
||||||
*
|
*
|
||||||
* @return array
|
* NOTE: this method is not expected to be used from any addons.
|
||||||
|
*
|
||||||
|
* @return string CSS markup, already optimised and compressed
|
||||||
*/
|
*/
|
||||||
public function css_files() {
|
public function get_css_content() {
|
||||||
|
global $CFG;
|
||||||
|
require_once($CFG->dirroot.'/lib/csslib.php');
|
||||||
|
|
||||||
|
$csscontent = '';
|
||||||
|
foreach ($this->get_css_files(false) as $value) {
|
||||||
|
foreach ($value as $val) {
|
||||||
|
if (is_array($val)) {
|
||||||
|
foreach ($val as $v) {
|
||||||
|
$csscontent .= file_get_contents($v) . "\n";
|
||||||
|
}
|
||||||
|
} 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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');
|
||||||
|
|
||||||
|
$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'];
|
||||||
|
}
|
||||||
|
|
||||||
|
} 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the whole css stylesheet for editor iframe.
|
||||||
|
*
|
||||||
|
* NOTE: this method is not expected to be used from any addons.
|
||||||
|
*
|
||||||
|
* @return string CSS markup
|
||||||
|
*/
|
||||||
|
public function get_css_content_editor() {
|
||||||
|
// Do not bother to optimise anything here, just very basic stuff.
|
||||||
|
$cssfiles = $this->editor_css_files();
|
||||||
|
$css = '';
|
||||||
|
foreach ($cssfiles as $file) {
|
||||||
|
$css .= file_get_contents($file)."\n";
|
||||||
|
}
|
||||||
|
return $this->post_process($css);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array of organised CSS files required for this output.
|
||||||
|
*
|
||||||
|
* @param bool $themedesigner
|
||||||
|
* @return array nested array of file paths
|
||||||
|
*/
|
||||||
|
protected function get_css_files($themedesigner) {
|
||||||
|
global $CFG;
|
||||||
|
|
||||||
|
$cache = null;
|
||||||
|
if ($themedesigner) {
|
||||||
|
require_once($CFG->dirroot.'/lib/csslib.php');
|
||||||
|
// We need some kind of caching here because otherwise the page navigation becomes
|
||||||
|
// way too slow in theme designer mode. Feel free to create full cache definition later...
|
||||||
|
$cache = cache::make_from_params(cache_store::MODE_APPLICATION, 'core', 'themedesigner', array('theme' => $this->name));
|
||||||
|
if ($files = $cache->get('cssfiles')) {
|
||||||
|
if ($files['created'] > time() - THEME_DESIGNER_CACHE_LIFETIME) {
|
||||||
|
unset($files['created']);
|
||||||
|
return $files;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$cssfiles = array('plugins'=>array(), 'parents'=>array(), 'theme'=>array());
|
$cssfiles = array('plugins'=>array(), 'parents'=>array(), 'theme'=>array());
|
||||||
|
|
||||||
// get all plugin sheets
|
// Get all plugin sheets.
|
||||||
$excludes = $this->resolve_excludes('plugins_exclude_sheets');
|
$excludes = $this->resolve_excludes('plugins_exclude_sheets');
|
||||||
if ($excludes !== true) {
|
if ($excludes !== true) {
|
||||||
foreach (core_component::get_plugin_types() as $type=>$unused) {
|
foreach (core_component::get_plugin_types() as $type=>$unused) {
|
||||||
|
@ -802,10 +919,10 @@ class theme_config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// find out wanted parent sheets
|
// Find out wanted parent sheets.
|
||||||
$excludes = $this->resolve_excludes('parents_exclude_sheets');
|
$excludes = $this->resolve_excludes('parents_exclude_sheets');
|
||||||
if ($excludes !== true) {
|
if ($excludes !== true) {
|
||||||
foreach (array_reverse($this->parent_configs) as $parent_config) { // base first, the immediate parent last
|
foreach (array_reverse($this->parent_configs) as $parent_config) { // Base first, the immediate parent last.
|
||||||
$parent = $parent_config->name;
|
$parent = $parent_config->name;
|
||||||
if (empty($parent_config->sheets) || (!empty($excludes[$parent]) and $excludes[$parent] === true)) {
|
if (empty($parent_config->sheets) || (!empty($excludes[$parent]) and $excludes[$parent] === true)) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -823,7 +940,7 @@ class theme_config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// current theme sheets
|
// Current theme sheets.
|
||||||
if (is_array($this->sheets)) {
|
if (is_array($this->sheets)) {
|
||||||
foreach ($this->sheets as $sheet) {
|
foreach ($this->sheets as $sheet) {
|
||||||
$sheetfile = "$this->dir/style/$sheet.css";
|
$sheetfile = "$this->dir/style/$sheet.css";
|
||||||
|
@ -833,57 +950,15 @@ class theme_config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($cache) {
|
||||||
|
$files = $cssfiles;
|
||||||
|
$files['created'] = time();
|
||||||
|
$cache->set('cssfiles', $files);
|
||||||
|
}
|
||||||
|
|
||||||
return $cssfiles;
|
return $cssfiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the content of the one huge CSS merged from all style sheets.
|
|
||||||
*
|
|
||||||
* @param css_optimiser|null $optimiser A CSS optimiser to use during on the content. Null = don't optimise
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function css_content(css_optimiser $optimiser = null) {
|
|
||||||
$files = array_merge($this->css_files(), array('editor'=>$this->editor_css_files()));
|
|
||||||
$css = $this->css_files_get_contents($files, array(), $optimiser);
|
|
||||||
return $css;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given an array of file paths or a single file path loads the contents of
|
|
||||||
* the CSS file, processes it then returns it in the same structure it was given.
|
|
||||||
*
|
|
||||||
* Can be used recursively on the results of {@link css_files}
|
|
||||||
*
|
|
||||||
* @param array|string $file An array of file paths or a single file path
|
|
||||||
* @param array $keys An array of previous array keys [recursive addition]
|
|
||||||
* @param css_optimiser|null $optimiser A CSS optimiser to use during on the content. Null = don't optimise
|
|
||||||
* @return The converted array or the contents of the single file ($file type)
|
|
||||||
*/
|
|
||||||
protected function css_files_get_contents($file, array $keys, css_optimiser $optimiser = null) {
|
|
||||||
global $CFG;
|
|
||||||
if (is_array($file)) {
|
|
||||||
// We use a separate array to keep everything in the exact same order.
|
|
||||||
$return = array();
|
|
||||||
foreach ($file as $key=>$f) {
|
|
||||||
$return[clean_param($key, PARAM_SAFEDIR)] = $this->css_files_get_contents($f, array_merge($keys, array($key)), $optimiser);
|
|
||||||
}
|
|
||||||
return $return;
|
|
||||||
} else {
|
|
||||||
$contents = file_get_contents($file);
|
|
||||||
$contents = $this->post_process($contents);
|
|
||||||
$comment = '/** Path: '.implode(' ', $keys).' **/'."\n";
|
|
||||||
$stats = '';
|
|
||||||
if (!is_null($optimiser)) {
|
|
||||||
$contents = $optimiser->process($contents);
|
|
||||||
if (!empty($CFG->cssoptimiserstats)) {
|
|
||||||
$stats = $optimiser->output_stats_css();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $comment.$stats.$contents;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a URL to the file that serves theme JavaScript files.
|
* Generate a URL to the file that serves theme JavaScript files.
|
||||||
*
|
*
|
||||||
|
@ -1359,7 +1434,6 @@ class theme_config {
|
||||||
/**
|
/**
|
||||||
* Return true if we should look for SVG images as well.
|
* Return true if we should look for SVG images as well.
|
||||||
*
|
*
|
||||||
* @staticvar bool $svg
|
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function use_svg_icons() {
|
public function use_svg_icons() {
|
||||||
|
|
|
@ -22,20 +22,14 @@
|
||||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Disable moodle specific debug messages and any errors in output,
|
||||||
// disable moodle specific debug messages and any errors in output,
|
|
||||||
// comment out when debugging or better look into error log!
|
// comment out when debugging or better look into error log!
|
||||||
define('NO_DEBUG_DISPLAY', true);
|
define('NO_DEBUG_DISPLAY', true);
|
||||||
|
|
||||||
// we need just the values from config.php and minlib.php
|
|
||||||
define('ABORT_AFTER_CONFIG', true);
|
define('ABORT_AFTER_CONFIG', true);
|
||||||
require('../config.php'); // this stops immediately at the beginning of lib/setup.php
|
require('../config.php');
|
||||||
require_once($CFG->dirroot.'/lib/csslib.php');
|
require_once($CFG->dirroot.'/lib/csslib.php');
|
||||||
|
|
||||||
if (!defined('THEME_DESIGNER_CACHE_LIFETIME')) {
|
|
||||||
define('THEME_DESIGNER_CACHE_LIFETIME', 4); // this can be also set in config.php
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($slashargument = min_get_slash_argument()) {
|
if ($slashargument = min_get_slash_argument()) {
|
||||||
$slashargument = ltrim($slashargument, '/');
|
$slashargument = ltrim($slashargument, '/');
|
||||||
if (substr_count($slashargument, '/') < 2) {
|
if (substr_count($slashargument, '/') < 2) {
|
||||||
|
@ -43,7 +37,7 @@ if ($slashargument = min_get_slash_argument()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strpos($slashargument, '_s/') === 0) {
|
if (strpos($slashargument, '_s/') === 0) {
|
||||||
// Can't use SVG
|
// Can't use SVG.
|
||||||
$slashargument = substr($slashargument, 3);
|
$slashargument = substr($slashargument, 3);
|
||||||
$usesvg = false;
|
$usesvg = false;
|
||||||
} else {
|
} else {
|
||||||
|
@ -79,9 +73,9 @@ if ($type === 'editor') {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file_exists("$CFG->dirroot/theme/$themename/config.php")) {
|
if (file_exists("$CFG->dirroot/theme/$themename/config.php")) {
|
||||||
// exists
|
// The theme exists in standard location - ok.
|
||||||
} else if (!empty($CFG->themedir) and file_exists("$CFG->themedir/$themename/config.php")) {
|
} else if (!empty($CFG->themedir) and file_exists("$CFG->themedir/$themename/config.php")) {
|
||||||
// exists
|
// Alternative theme location contains this theme - ok.
|
||||||
} else {
|
} else {
|
||||||
header('HTTP/1.0 404 not found');
|
header('HTTP/1.0 404 not found');
|
||||||
die('Theme was not found, sorry.');
|
die('Theme was not found, sorry.');
|
||||||
|
@ -105,19 +99,18 @@ $etag = sha1($etag);
|
||||||
|
|
||||||
if (file_exists($candidatesheet)) {
|
if (file_exists($candidatesheet)) {
|
||||||
if (!empty($_SERVER['HTTP_IF_NONE_MATCH']) || !empty($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
|
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
|
// We do not actually need to verify the etag value because our files
|
||||||
// never change in cache because we increment the rev parameter
|
// never change in cache because we increment the rev counter.
|
||||||
css_send_unmodified(filemtime($candidatesheet), $etag);
|
css_send_unmodified(filemtime($candidatesheet), $etag);
|
||||||
}
|
}
|
||||||
css_send_cached_css($candidatesheet, $etag);
|
css_send_cached_css($candidatesheet, $etag);
|
||||||
}
|
}
|
||||||
|
|
||||||
//=================================================================================
|
// Ok, now we need to start normal moodle script, we need to load all libs and $DB.
|
||||||
// ok, now we need to start normal moodle script, we need to load all libs and $DB
|
|
||||||
define('ABORT_AFTER_CONFIG_CANCEL', true);
|
define('ABORT_AFTER_CONFIG_CANCEL', true);
|
||||||
|
|
||||||
define('NO_MOODLE_COOKIES', true); // Session not used here
|
define('NO_MOODLE_COOKIES', true); // Session not used here.
|
||||||
define('NO_UPGRADE_CHECK', true); // Ignore upgrade check
|
define('NO_UPGRADE_CHECK', true); // Ignore upgrade check.
|
||||||
|
|
||||||
require("$CFG->dirroot/lib/setup.php");
|
require("$CFG->dirroot/lib/setup.php");
|
||||||
|
|
||||||
|
@ -131,7 +124,7 @@ if ($themerev <= 0 or $themerev != $rev) {
|
||||||
$rev = $themerev;
|
$rev = $themerev;
|
||||||
$cache = false;
|
$cache = false;
|
||||||
|
|
||||||
$candidatedir = "$CFG->cachedir/theme/$rev/$themename/css";
|
$candidatedir = "$CFG->localcachedir/theme/$rev/$themename/css";
|
||||||
$etag = "$rev/$themename/$type";
|
$etag = "$rev/$themename/$type";
|
||||||
$candidatename = $type;
|
$candidatename = $type;
|
||||||
if (!$usesvg) {
|
if (!$usesvg) {
|
||||||
|
@ -151,12 +144,13 @@ if ($themerev <= 0 or $themerev != $rev) {
|
||||||
make_localcache_directory('theme', false);
|
make_localcache_directory('theme', false);
|
||||||
|
|
||||||
if ($type === 'editor') {
|
if ($type === 'editor') {
|
||||||
$cssfiles = $theme->editor_css_files();
|
$csscontent = $theme->get_css_content_editor();
|
||||||
css_store_css($theme, "$candidatedir/editor.css", $cssfiles, false);
|
css_store_css($theme, "$candidatedir/editor.css", $csscontent, false);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Older IEs require smaller chunks.
|
// Older IEs require smaller chunks.
|
||||||
$css = $theme->css_files();
|
$csscontent = $theme->get_css_content();
|
||||||
|
|
||||||
$relroot = preg_replace('|^http.?://[^/]+|', '', $CFG->wwwroot);
|
$relroot = preg_replace('|^http.?://[^/]+|', '', $CFG->wwwroot);
|
||||||
if (!empty($slashargument)) {
|
if (!empty($slashargument)) {
|
||||||
if ($usesvg) {
|
if ($usesvg) {
|
||||||
|
@ -171,36 +165,19 @@ if ($type === 'editor') {
|
||||||
$chunkurl = "{$relroot}/theme/styles.php?theme={$themename}&rev={$rev}&type=all&svg=0";
|
$chunkurl = "{$relroot}/theme/styles.php?theme={$themename}&rev={$rev}&type=all&svg=0";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$cssfiles = array();
|
css_store_css($theme, "$candidatedir/all.css", $csscontent, true, $chunkurl);
|
||||||
foreach ($css as $key => $value) {
|
|
||||||
foreach ($value as $val) {
|
|
||||||
if (is_array($val)) {
|
|
||||||
foreach ($val as $k=>$v) {
|
|
||||||
$cssfiles[] = $v;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$cssfiles[] = $val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
css_store_css($theme, "$candidatedir/all.css", $cssfiles, true, $chunkurl);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify nothing failed in cache file creation
|
if (!$cache) {
|
||||||
clearstatcache();
|
// Do not pollute browser caches if invalid revision requested,
|
||||||
if (!file_exists($candidatesheet)) {
|
// let's ignore legacy IE breakage here too.
|
||||||
// We need to send at least something, IE does not get it chunked properly but who cares.
|
css_send_uncached_css($csscontent);
|
||||||
$css = '';
|
|
||||||
foreach ($cssfiles as $file) {
|
|
||||||
$css .= file_get_contents($file)."\n";
|
|
||||||
}
|
|
||||||
css_send_uncached_css($css, false);
|
|
||||||
|
|
||||||
} else if (!$cache) {
|
} else if ($chunk and file_exists($candidatesheet)) {
|
||||||
// Do not pollute browser caches if invalid revision requested.
|
// Greetings stupid legacy IEs!
|
||||||
css_send_uncached_css(file_get_contents($candidatesheet), false);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// This is the expected result!
|
|
||||||
css_send_cached_css($candidatesheet, $etag);
|
css_send_cached_css($candidatesheet, $etag);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Real browsers - this is the expected result!
|
||||||
|
css_send_cached_css_content($csscontent, $etag);
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,100 +25,47 @@
|
||||||
// Disable moodle specific debug messages and any errors in output,
|
// Disable moodle specific debug messages and any errors in output,
|
||||||
// comment out when debugging or better look into error log!
|
// comment out when debugging or better look into error log!
|
||||||
define('NO_DEBUG_DISPLAY', true);
|
define('NO_DEBUG_DISPLAY', true);
|
||||||
|
define('NO_UPGRADE_CHECK', true);
|
||||||
|
define('NO_MOODLE_COOKIES', true);
|
||||||
|
|
||||||
define('ABORT_AFTER_CONFIG', true);
|
require('../config.php');
|
||||||
require('../config.php'); // this stops immediately at the beginning of lib/setup.php
|
|
||||||
require_once($CFG->dirroot.'/lib/csslib.php');
|
require_once($CFG->dirroot.'/lib/csslib.php');
|
||||||
|
|
||||||
$themename = min_optional_param('theme', 'standard', 'SAFEDIR');
|
$themename = optional_param('theme', 'standard', PARAM_SAFEDIR);
|
||||||
$type = min_optional_param('type', '', 'SAFEDIR');
|
$type = optional_param('type', '', PARAM_SAFEDIR);
|
||||||
$subtype = min_optional_param('subtype', '', 'SAFEDIR');
|
$subtype = optional_param('subtype', '', PARAM_SAFEDIR);
|
||||||
$sheet = min_optional_param('sheet', '', 'SAFEDIR');
|
$sheet = optional_param('sheet', '', PARAM_SAFEDIR);
|
||||||
$usesvg = (bool)min_optional_param('svg', '1', 'INT');
|
$usesvg = optional_param('svg', 1, PARAM_BOOL);
|
||||||
|
|
||||||
if (!defined('THEME_DESIGNER_CACHE_LIFETIME')) {
|
|
||||||
define('THEME_DESIGNER_CACHE_LIFETIME', 4); // this can be also set in config.php
|
|
||||||
}
|
|
||||||
|
|
||||||
if (file_exists("$CFG->dirroot/theme/$themename/config.php")) {
|
if (file_exists("$CFG->dirroot/theme/$themename/config.php")) {
|
||||||
// exists
|
// The theme exists in standard location - ok.
|
||||||
} else if (!empty($CFG->themedir) and file_exists("$CFG->themedir/$themename/config.php")) {
|
} else if (!empty($CFG->themedir) and file_exists("$CFG->themedir/$themename/config.php")) {
|
||||||
// exists
|
// Alternative theme location contains this theme - ok.
|
||||||
} else {
|
} else {
|
||||||
css_send_css_not_found();
|
css_send_css_not_found();
|
||||||
}
|
}
|
||||||
|
|
||||||
// no gzip compression when debugging
|
|
||||||
|
|
||||||
if ($usesvg) {
|
|
||||||
$candidatesheet = "$CFG->cachedir/theme/$themename/designer.ser";
|
|
||||||
} else {
|
|
||||||
// Add to the sheet name, one day we'll be able to just drop this.
|
|
||||||
$candidatesheet = "$CFG->cachedir/theme/$themename/designer_nosvg.ser";
|
|
||||||
}
|
|
||||||
|
|
||||||
$css = false;
|
|
||||||
if (is_readable($candidatesheet) and filemtime($candidatesheet) > time() - THEME_DESIGNER_CACHE_LIFETIME) {
|
|
||||||
$css = @unserialize(file_get_contents($candidatesheet));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_array($css)) {
|
|
||||||
// Ok, 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);
|
$theme = theme_config::load($themename);
|
||||||
$css = $theme->css_content();
|
$theme->force_svg_use($usesvg);
|
||||||
}
|
|
||||||
|
|
||||||
if ($type === 'editor') {
|
if ($type === 'editor') {
|
||||||
if (isset($css['editor'])) {
|
$csscontent = $theme->get_css_content_editor();
|
||||||
css_send_uncached_css($css['editor']);
|
css_send_uncached_css($csscontent);
|
||||||
}
|
|
||||||
} else if ($type === 'ie') {
|
|
||||||
// IE is a sloppy browser with weird limits, sorry
|
|
||||||
if ($subtype === 'plugins') {
|
|
||||||
css_send_uncached_css($css['plugins']);
|
|
||||||
|
|
||||||
} else if ($subtype === 'parents') {
|
|
||||||
$sendcss = array();
|
|
||||||
if (empty($sheet)) {
|
|
||||||
// If not specific parent has been specified as $sheet then build a
|
|
||||||
// collection of @import statements into this one sheet.
|
|
||||||
// We shouldn't ever actually get here, but none the less we'll deal
|
|
||||||
// with it incase we ever do.
|
|
||||||
// @import statements arn't processed until after concurrent CSS requests
|
|
||||||
// making them slightly evil.
|
|
||||||
foreach (array_keys($css['parents']) as $sheet) {
|
|
||||||
$sendcss[] = "@import url(styles_debug.php?theme=$themename&type=$type&subtype=$subtype&sheet=$sheet);";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Build up the CSS for that parent so we can serve it as one file.
|
|
||||||
foreach ($css[$subtype][$sheet] as $parent=>$css) {
|
|
||||||
$sendcss[] = $css;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
css_send_uncached_css($sendcss);
|
|
||||||
} else if ($subtype === 'theme') {
|
|
||||||
css_send_uncached_css($css['theme']);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if ($type === 'plugin') {
|
// We need some kind of caching here because otherwise the page navigation becomes
|
||||||
if (isset($css['plugins'][$subtype])) {
|
// way too slow in theme designer mode. Feel free to create full cache definition later...
|
||||||
css_send_uncached_css($css['plugins'][$subtype]);
|
$key = "$type $subtype $sheet $usesvg";
|
||||||
|
$cache = cache::make_from_params(cache_store::MODE_APPLICATION, 'core', 'themedesigner', array('theme' => $themename));
|
||||||
|
if ($content = $cache->get($key)) {
|
||||||
|
if ($content['created'] > time() - THEME_DESIGNER_CACHE_LIFETIME) {
|
||||||
|
$csscontent = $content['data'];
|
||||||
|
css_send_uncached_css($csscontent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if ($type === 'parent') {
|
$csscontent = $theme->get_css_content_debug($type, $subtype, $sheet);
|
||||||
if (isset($css['parents'][$subtype][$sheet])) {
|
|
||||||
css_send_uncached_css($css['parents'][$subtype][$sheet]);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if ($type === 'theme') {
|
$cache->set($key, array('data' => $csscontent, 'created' => time()));
|
||||||
if (isset($css['theme'][$sheet])) {
|
|
||||||
css_send_uncached_css($css['theme'][$sheet]);
|
css_send_uncached_css($csscontent);
|
||||||
}
|
|
||||||
}
|
|
||||||
css_send_css_not_found();
|
|
||||||
|
|
|
@ -3,6 +3,8 @@ information provided here is intended especially for theme designer.
|
||||||
|
|
||||||
=== 2.7 ===
|
=== 2.7 ===
|
||||||
|
|
||||||
|
* CSS related functions in theme class and csslib.php were refactored, addons that are
|
||||||
|
using this private API need to be updated
|
||||||
* Please update your css to use 'tr:nth-of-type(odd/even)' instead of '.ro/.r1' to apply tr specific css for various tables.
|
* Please update your css to use 'tr:nth-of-type(odd/even)' instead of '.ro/.r1' to apply tr specific css for various tables.
|
||||||
These classes are deprecated now and will be removed in Moodle 2.9. This has done to better support styling tables that will be altered by JavaScript.
|
These classes are deprecated now and will be removed in Moodle 2.9. This has done to better support styling tables that will be altered by JavaScript.
|
||||||
Here is an example to update your css:
|
Here is an example to update your css:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue