Moodle  2.2.1
http://www.collinsharper.com
C:/xampp/htdocs/moodle/lib/outputlib.php
Go to the documentation of this file.
00001 <?php
00002 
00003 // This file is part of Moodle - http://moodle.org/
00004 //
00005 // Moodle is free software: you can redistribute it and/or modify
00006 // it under the terms of the GNU General Public License as published by
00007 // the Free Software Foundation, either version 3 of the License, or
00008 // (at your option) any later version.
00009 //
00010 // Moodle is distributed in the hope that it will be useful,
00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013 // GNU General Public License for more details.
00014 //
00015 // You should have received a copy of the GNU General Public License
00016 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
00017 
00030 defined('MOODLE_INTERNAL') || die();
00031 
00032 require_once($CFG->libdir.'/outputcomponents.php');
00033 require_once($CFG->libdir.'/outputactions.php');
00034 require_once($CFG->libdir.'/outputfactories.php');
00035 require_once($CFG->libdir.'/outputrenderers.php');
00036 require_once($CFG->libdir.'/outputrequirementslib.php');
00037 
00042 function theme_reset_all_caches() {
00043     global $CFG;
00044     require_once("$CFG->libdir/filelib.php");
00045 
00046     set_config('themerev', empty($CFG->themerev) ? 1 : $CFG->themerev+1);
00047     fulldelete("$CFG->cachedir/theme");
00048 }
00049 
00055 function theme_set_designer_mod($state) {
00056     theme_reset_all_caches();
00057     set_config('themedesignermode', (int)!empty($state));
00058 }
00059 
00064 function theme_get_revision() {
00065     global $CFG;
00066 
00067     if (empty($CFG->themedesignermode)) {
00068         if (empty($CFG->themerev)) {
00069             return -1;
00070         } else {
00071             return $CFG->themerev;
00072         }
00073 
00074     } else {
00075         return -1;
00076     }
00077 }
00078 
00079 
00099 class theme_config {
00103     const DEFAULT_THEME = 'standard';
00104 
00115     public $parents;
00116 
00123     public $sheets = array();
00124 
00132     public $parents_exclude_sheets = null;
00133 
00140     public $plugins_exclude_sheets = null;
00141 
00148     public $editor_sheets = array();
00149 
00156     public $javascripts = array();
00157 
00164     public $javascripts_footer = array();
00165 
00173     public $parents_exclude_javascripts = null;
00174 
00225     public $layouts = array();
00226 
00245     public $rendererfactory = 'standard_renderer_factory';
00246 
00256     public $csspostprocess = null;
00257 
00268     public $rarrow = null;
00269 
00280     public $larrow = null;
00281 
00286     public $enablecourseajax = true;
00287 
00288     //==Following properties are not configurable from theme config.php==
00289 
00295     public $name;
00296 
00302     public $dir;
00303 
00309     public $setting = null;
00310 
00316     public $enable_dock = false;
00317 
00323     public $hidefromselector = false;
00324 
00330     protected $rf = null;
00331 
00336     protected $parent_configs = array();
00337 
00345     public static function load($themename) {
00346         global $CFG;
00347 
00348         // load theme settings from db
00349         try {
00350             $settings = get_config('theme_'.$themename);
00351         } catch (dml_exception $e) {
00352             // most probably moodle tables not created yet
00353             $settings = new stdClass();
00354         }
00355 
00356         if ($config = theme_config::find_theme_config($themename, $settings)) {
00357             return new theme_config($config);
00358 
00359         } else if ($themename == theme_config::DEFAULT_THEME) {
00360             throw new coding_exception('Default theme '.theme_config::DEFAULT_THEME.' not available or broken!');
00361 
00362         } else {
00363             // bad luck, the requested theme has some problems - admin see details in theme config
00364             return new theme_config(theme_config::find_theme_config(theme_config::DEFAULT_THEME, $settings));
00365         }
00366     }
00367 
00378     public static function diagnose($themename) {
00379         //TODO: MDL-21108
00380         return array();
00381     }
00382 
00387     private function __construct($config) {
00388         global $CFG; //needed for included lib.php files
00389 
00390         $this->settings = $config->settings;
00391         $this->name     = $config->name;
00392         $this->dir      = $config->dir;
00393 
00394         if ($this->name != 'base') {
00395             $baseconfig = theme_config::find_theme_config('base', $this->settings);
00396         } else {
00397             $baseconfig = $config;
00398         }
00399 
00400         $configurable = array('parents', 'sheets', 'parents_exclude_sheets', 'plugins_exclude_sheets', 'javascripts', 'javascripts_footer',
00401                               'parents_exclude_javascripts', 'layouts', 'enable_dock', 'enablecourseajax',
00402                               'rendererfactory', 'csspostprocess', 'editor_sheets', 'rarrow', 'larrow', 'hidefromselector');
00403 
00404         foreach ($config as $key=>$value) {
00405             if (in_array($key, $configurable)) {
00406                 $this->$key = $value;
00407             }
00408         }
00409 
00410         // verify all parents and load configs and renderers
00411         foreach ($this->parents as $parent) {
00412             if ($parent == 'base') {
00413                 $parent_config = $baseconfig;
00414             } else if (!$parent_config = theme_config::find_theme_config($parent, $this->settings)) {
00415                 // this is not good - better exclude faulty parents
00416                 continue;
00417             }
00418             $libfile = $parent_config->dir.'/lib.php';
00419             if (is_readable($libfile)) {
00420                 // theme may store various function here
00421                 include_once($libfile);
00422             }
00423             $renderersfile = $parent_config->dir.'/renderers.php';
00424             if (is_readable($renderersfile)) {
00425                 // may contain core and plugin renderers and renderer factory
00426                 include_once($renderersfile);
00427             }
00428             $this->parent_configs[$parent] = $parent_config;
00429             $rendererfile = $parent_config->dir.'/renderers.php';
00430             if (is_readable($rendererfile)) {
00431                  // may contain core and plugin renderers and renderer factory
00432                 include_once($rendererfile);
00433             }
00434         }
00435         $libfile = $this->dir.'/lib.php';
00436         if (is_readable($libfile)) {
00437             // theme may store various function here
00438             include_once($libfile);
00439         }
00440         $rendererfile = $this->dir.'/renderers.php';
00441         if (is_readable($rendererfile)) {
00442             // may contain core and plugin renderers and renderer factory
00443             include_once($rendererfile);
00444         }
00445 
00446         // cascade all layouts properly
00447         foreach ($baseconfig->layouts as $layout=>$value) {
00448             if (!isset($this->layouts[$layout])) {
00449                 foreach ($this->parent_configs as $parent_config) {
00450                     if (isset($parent_config->layouts[$layout])) {
00451                         $this->layouts[$layout] = $parent_config->layouts[$layout];
00452                         continue 2;
00453                     }
00454                 }
00455                 $this->layouts[$layout] = $value;
00456             }
00457         }
00458 
00459         //fix arrows if needed
00460         $this->check_theme_arrows();
00461     }
00462 
00463     /*
00464      * Checks if arrows $THEME->rarrow, $THEME->larrow have been set (theme/-/config.php).
00465      * If not it applies sensible defaults.
00466      *
00467      * Accessibility: right and left arrow Unicode characters for breadcrumb, calendar,
00468      * search forum block, etc. Important: these are 'silent' in a screen-reader
00469      * (unlike &gt; &raquo;), and must be accompanied by text.
00470      */
00471     private function check_theme_arrows() {
00472         if (!isset($this->rarrow) and !isset($this->larrow)) {
00473             // Default, looks good in Win XP/IE 6, Win/Firefox 1.5, Win/Netscape 8...
00474             // Also OK in Win 9x/2K/IE 5.x
00475             $this->rarrow = '&#x25BA;';
00476             $this->larrow = '&#x25C4;';
00477             if (empty($_SERVER['HTTP_USER_AGENT'])) {
00478                 $uagent = '';
00479             } else {
00480                 $uagent = $_SERVER['HTTP_USER_AGENT'];
00481             }
00482             if (false !== strpos($uagent, 'Opera')
00483                 || false !== strpos($uagent, 'Mac')) {
00484                 // Looks good in Win XP/Mac/Opera 8/9, Mac/Firefox 2, Camino, Safari.
00485                 // Not broken in Mac/IE 5, Mac/Netscape 7 (?).
00486                 $this->rarrow = '&#x25B6;';
00487                 $this->larrow = '&#x25C0;';
00488             }
00489             elseif (false !== strpos($uagent, 'Konqueror')) {
00490                 $this->rarrow = '&rarr;';
00491                 $this->larrow = '&larr;';
00492             }
00493             elseif (isset($_SERVER['HTTP_ACCEPT_CHARSET'])
00494                 && false === stripos($_SERVER['HTTP_ACCEPT_CHARSET'], 'utf-8')) {
00495                 // (Win/IE 5 doesn't set ACCEPT_CHARSET, but handles Unicode.)
00496                 // To be safe, non-Unicode browsers!
00497                 $this->rarrow = '&gt;';
00498                 $this->larrow = '&lt;';
00499             }
00500 
00502             if (right_to_left()) {
00503                 $t = $this->rarrow;
00504                 $this->rarrow = $this->larrow;
00505                 $this->larrow = $t;
00506             }
00507         }
00508     }
00509 
00515     public function renderer_prefixes() {
00516         global $CFG; // just in case the included files need it
00517 
00518         $prefixes = array('theme_'.$this->name);
00519 
00520         foreach ($this->parent_configs as $parent) {
00521             $prefixes[] = 'theme_'.$parent->name;
00522         }
00523 
00524         return $prefixes;
00525     }
00526 
00532     public function editor_css_url($encoded=true) {
00533         global $CFG;
00534 
00535         $rev = theme_get_revision();
00536 
00537         if ($rev > -1) {
00538             $params = array('theme'=>$this->name,'rev'=>$rev, 'type'=>'editor');
00539             return new moodle_url($CFG->httpswwwroot.'/theme/styles.php', $params);
00540         } else {
00541             $params = array('theme'=>$this->name, 'type'=>'editor');
00542             return new moodle_url($CFG->httpswwwroot.'/theme/styles_debug.php', $params);
00543         }
00544     }
00545 
00550     public function editor_css_files() {
00551         global $CFG;
00552 
00553         $files = array();
00554 
00555         // first editor plugins
00556         $plugins = get_plugin_list('editor');
00557         foreach ($plugins as $plugin=>$fulldir) {
00558             $sheetfile = "$fulldir/editor_styles.css";
00559             if (is_readable($sheetfile)) {
00560                 $files['plugin_'.$plugin] = $sheetfile;
00561             }
00562         }
00563         // then parent themes
00564         foreach (array_reverse($this->parent_configs) as $parent_config) { // base first, the immediate parent last
00565             if (empty($parent_config->editor_sheets)) {
00566                 continue;
00567             }
00568             foreach ($parent_config->editor_sheets as $sheet) {
00569                 $sheetfile = "$parent_config->dir/style/$sheet.css";
00570                 if (is_readable($sheetfile)) {
00571                     $files['parent_'.$parent_config->name.'_'.$sheet] = $sheetfile;
00572                 }
00573             }
00574         }
00575         // finally this theme
00576         if (!empty($this->editor_sheets)) {
00577             foreach ($this->editor_sheets as $sheet) {
00578                 $sheetfile = "$this->dir/style/$sheet.css";
00579                 if (is_readable($sheetfile)) {
00580                     $files['theme_'.$sheet] = $sheetfile;
00581                 }
00582             }
00583         }
00584 
00585         return $files;
00586     }
00587 
00593     public function css_urls(moodle_page $page) {
00594         global $CFG;
00595 
00596         $rev = theme_get_revision();
00597 
00598         $urls = array();
00599 
00600         if ($rev > -1) {
00601             if (check_browser_version('MSIE', 5)) {
00602                 // We need to split the CSS files for IE
00603                 $urls[] = new moodle_url($CFG->httpswwwroot.'/theme/styles.php', array('theme'=>$this->name,'rev'=>$rev, 'type'=>'plugins'));
00604                 $urls[] = new moodle_url($CFG->httpswwwroot.'/theme/styles.php', array('theme'=>$this->name,'rev'=>$rev, 'type'=>'parents'));
00605                 $urls[] = new moodle_url($CFG->httpswwwroot.'/theme/styles.php', array('theme'=>$this->name,'rev'=>$rev, 'type'=>'theme'));
00606             } else {
00607                 $urls[] = new moodle_url($CFG->httpswwwroot.'/theme/styles.php', array('theme'=>$this->name,'rev'=>$rev));
00608             }
00609         } else {
00610             // find out the current CSS and cache it now for 5 seconds
00611             // the point is to construct the CSS only once and pass it through the
00612             // dataroot to the script that actually serves the sheets
00613             if (!defined('THEME_DESIGNER_CACHE_LIFETIME')) {
00614                 define('THEME_DESIGNER_CACHE_LIFETIME', 4); // this can be also set in config.php
00615             }
00616             $candidatesheet = "$CFG->cachedir/theme/$this->name/designer.ser";
00617             if (!file_exists($candidatesheet)) {
00618                 $css = $this->css_content();
00619                 check_dir_exists(dirname($candidatesheet));
00620                 file_put_contents($candidatesheet, serialize($css));
00621 
00622             } else if (filemtime($candidatesheet) > time() - THEME_DESIGNER_CACHE_LIFETIME) {
00623                 if ($css = file_get_contents($candidatesheet)) {
00624                     $css = unserialize($css);
00625                 } else {
00626                     unlink($candidatesheet);
00627                     $css = $this->css_content();
00628                 }
00629 
00630             } else {
00631                 unlink($candidatesheet);
00632                 $css = $this->css_content();
00633                 file_put_contents($candidatesheet, serialize($css));
00634             }
00635 
00636             $baseurl = $CFG->httpswwwroot.'/theme/styles_debug.php';
00637 
00638             if (check_browser_version('MSIE', 5)) {
00639                 // lalala, IE does not allow more than 31 linked CSS files from main document
00640                 $urls[] = new moodle_url($baseurl, array('theme'=>$this->name, 'type'=>'ie', 'subtype'=>'plugins'));
00641                 foreach ($css['parents'] as $parent=>$sheets) {
00642                     // We need to serve parents individually otherwise we may easily exceed the style limit IE imposes (4096)
00643                     $urls[] = new moodle_url($baseurl, array('theme'=>$this->name,'type'=>'ie', 'subtype'=>'parents', 'sheet'=>$parent));
00644                 }
00645                 $urls[] = new moodle_url($baseurl, array('theme'=>$this->name, 'type'=>'ie', 'subtype'=>'theme'));
00646 
00647             } else {
00648                 foreach ($css['plugins'] as $plugin=>$unused) {
00649                     $urls[] = new moodle_url($baseurl, array('theme'=>$this->name,'type'=>'plugin', 'subtype'=>$plugin));
00650                 }
00651                 foreach ($css['parents'] as $parent=>$sheets) {
00652                     foreach ($sheets as $sheet=>$unused2) {
00653                         $urls[] = new moodle_url($baseurl, array('theme'=>$this->name,'type'=>'parent', 'subtype'=>$parent, 'sheet'=>$sheet));
00654                     }
00655                 }
00656                 foreach ($css['theme'] as $sheet=>$unused) {
00657                     $urls[] = new moodle_url($baseurl, array('sheet'=>$sheet, 'theme'=>$this->name, 'type'=>'theme')); // sheet first in order to make long urls easier to read
00658                 }
00659             }
00660         }
00661 
00662         return $urls;
00663     }
00664 
00669     public function css_files() {
00670         $cssfiles = array('plugins'=>array(), 'parents'=>array(), 'theme'=>array());
00671 
00672         // get all plugin sheets
00673         $excludes = $this->resolve_excludes('plugins_exclude_sheets');
00674         if ($excludes !== true) {
00675             foreach (get_plugin_types() as $type=>$unused) {
00676                 if ($type === 'theme' || (!empty($excludes[$type]) and $excludes[$type] === true)) {
00677                     continue;
00678                 }
00679                 $plugins = get_plugin_list($type);
00680                 foreach ($plugins as $plugin=>$fulldir) {
00681                     if (!empty($excludes[$type]) and is_array($excludes[$type])
00682                         and in_array($plugin, $excludes[$type])) {
00683                         continue;
00684                     }
00685 
00686                     $plugincontent = '';
00687                     $sheetfile = "$fulldir/styles.css";
00688                     if (is_readable($sheetfile)) {
00689                         $cssfiles['plugins'][$type.'_'.$plugin] = $sheetfile;
00690                     }
00691                     $sheetthemefile = "$fulldir/styles_{$this->name}.css";
00692                     if (is_readable($sheetthemefile)) {
00693                         $cssfiles['plugins'][$type.'_'.$plugin.'_'.$this->name] = $sheetthemefile;
00694                     }
00695                     }
00696                 }
00697             }
00698 
00699         // find out wanted parent sheets
00700         $excludes = $this->resolve_excludes('parents_exclude_sheets');
00701         if ($excludes !== true) {
00702             foreach (array_reverse($this->parent_configs) as $parent_config) { // base first, the immediate parent last
00703                 $parent = $parent_config->name;
00704                 if (empty($parent_config->sheets) || (!empty($excludes[$parent]) and $excludes[$parent] === true)) {
00705                     continue;
00706                 }
00707                 foreach ($parent_config->sheets as $sheet) {
00708                     if (!empty($excludes[$parent]) and is_array($excludes[$parent])
00709                         and in_array($sheet, $excludes[$parent])) {
00710                         continue;
00711                     }
00712                     $sheetfile = "$parent_config->dir/style/$sheet.css";
00713                     if (is_readable($sheetfile)) {
00714                         $cssfiles['parents'][$parent][$sheet] = $sheetfile;
00715                     }
00716                 }
00717             }
00718         }
00719 
00720         // current theme sheets
00721         if (is_array($this->sheets)) {
00722             foreach ($this->sheets as $sheet) {
00723                 $sheetfile = "$this->dir/style/$sheet.css";
00724                 if (is_readable($sheetfile)) {
00725                     $cssfiles['theme'][$sheet] = $sheetfile;
00726                 }
00727             }
00728         }
00729 
00730         return $cssfiles;
00731     }
00732 
00737     public function css_content() {
00738         $files = array_merge($this->css_files(), array('editor'=>$this->editor_css_files()));
00739         $css = $this->css_files_get_contents($files, array());
00740         return $css;
00741     }
00742 
00753     protected function css_files_get_contents($file, array $keys) {
00754         if (is_array($file)) {
00755             foreach ($file as $key=>$f) {
00756                 $file[$key] = $this->css_files_get_contents($f, array_merge($keys, array($key)));
00757             }
00758             return $file;
00759         } else {
00760             $comment = '/** Path: '.implode(' ', $keys).' **/'."\n";
00761             return $comment.$this->post_process(file_get_contents($file));
00762         }
00763     }
00764 
00765 
00771     public function javascript_url($inhead) {
00772         global $CFG;
00773 
00774         $rev = theme_get_revision();
00775         $params = array('theme'=>$this->name,'rev'=>$rev);
00776         $params['type'] = $inhead ? 'head' : 'footer';
00777 
00778         return new moodle_url($CFG->httpswwwroot.'/theme/javascript.php', $params);
00779     }
00780 
00781     public function javascript_files($type) {
00782         if ($type === 'footer') {
00783             $type = 'javascripts_footer';
00784         } else {
00785             $type = 'javascripts';
00786         }
00787 
00788         $js = array();
00789         // find out wanted parent javascripts
00790         $excludes = $this->resolve_excludes('parents_exclude_javascripts');
00791         if ($excludes !== true) {
00792             foreach (array_reverse($this->parent_configs) as $parent_config) { // base first, the immediate parent last
00793                 $parent = $parent_config->name;
00794                 if (empty($parent_config->$type)) {
00795                     continue;
00796                 }
00797                 if (!empty($excludes[$parent]) and $excludes[$parent] === true) {
00798                     continue;
00799                 }
00800                 foreach ($parent_config->$type as $javascript) {
00801                     if (!empty($excludes[$parent]) and is_array($excludes[$parent])
00802                         and in_array($javascript, $excludes[$parent])) {
00803                         continue;
00804                     }
00805                     $javascriptfile = "$parent_config->dir/javascript/$javascript.js";
00806                     if (is_readable($javascriptfile)) {
00807                         $js[] = $javascriptfile;
00808                     }
00809                 }
00810             }
00811         }
00812 
00813         // current theme javascripts
00814         if (is_array($this->$type)) {
00815             foreach ($this->$type as $javascript) {
00816                 $javascriptfile = "$this->dir/javascript/$javascript.js";
00817                 if (is_readable($javascriptfile)) {
00818                     $js[] = $javascriptfile;
00819                 }
00820             }
00821         }
00822 
00823         return $js;
00824     }
00825 
00833     protected function resolve_excludes($variable, $default=null) {
00834         $setting = $default;
00835         if (is_array($this->{$variable}) or $this->{$variable} === true) {
00836             $setting = $this->{$variable};
00837         } else {
00838             foreach ($this->parent_configs as $parent_config) { // the immediate parent first, base last
00839                 if (!isset($parent_config->{$variable})) {
00840                     continue;
00841                 }
00842                 if (is_array($parent_config->{$variable}) or $parent_config->{$variable} === true) {
00843                     $setting = $parent_config->{$variable};
00844                     break;
00845                 }
00846             }
00847         }
00848         return $setting;
00849     }
00850 
00856     public function javascript_content($type) {
00857         $jsfiles = $this->javascript_files($type);
00858         $js = '';
00859         foreach ($jsfiles as $jsfile) {
00860             $js .= file_get_contents($jsfile)."\n";
00861         }
00862         return $js;
00863     }
00864 
00865     public function post_process($css) {
00866         global $CFG;
00867 
00868         // now resolve all image locations
00869         if (preg_match_all('/\[\[pix:([a-z_]+\|)?([^\]]+)\]\]/', $css, $matches, PREG_SET_ORDER)) {
00870             $replaced = array();
00871             foreach ($matches as $match) {
00872                 if (isset($replaced[$match[0]])) {
00873                     continue;
00874                 }
00875                 $replaced[$match[0]] = true;
00876                 $imagename = $match[2];
00877                 $component = rtrim($match[1], '|');
00878                 $imageurl = $this->pix_url($imagename, $component)->out(false);
00879                  // we do not need full url because the image.php is always in the same dir
00880                 $imageurl = str_replace("$CFG->httpswwwroot/theme/", '', $imageurl);
00881                 $css = str_replace($match[0], $imageurl, $css);
00882             }
00883         }
00884 
00885         // now resolve all theme settings or do any other postprocessing
00886         $csspostprocess = $this->csspostprocess;
00887         if (function_exists($csspostprocess)) {
00888             $css = $csspostprocess($css, $this);
00889         }
00890 
00891         return $css;
00892     }
00893 
00901     public function pix_url($imagename, $component) {
00902         global $CFG;
00903 
00904         $params = array('theme'=>$this->name, 'image'=>$imagename);
00905 
00906         $rev = theme_get_revision();
00907         if ($rev != -1) {
00908             $params['rev'] = $rev;
00909         }
00910         if (!empty($component) and $component !== 'moodle'and $component !== 'core') {
00911             $params['component'] = $component;
00912         }
00913 
00914         return new moodle_url("$CFG->httpswwwroot/theme/image.php", $params);
00915     }
00916 
00923     public function resolve_image_location($image, $component) {
00924         global $CFG;
00925 
00926         if ($component === 'moodle' or $component === 'core' or empty($component)) {
00927             if ($imagefile = $this->image_exists("$this->dir/pix_core/$image")) {
00928                 return $imagefile;
00929             }
00930             foreach (array_reverse($this->parent_configs) as $parent_config) { // base first, the immediate parent last
00931                 if ($imagefile = $this->image_exists("$parent_config->dir/pix_core/$image")) {
00932                     return $imagefile;
00933                 }
00934             }
00935             if ($imagefile = $this->image_exists("$CFG->dirroot/pix/$image")) {
00936                 return $imagefile;
00937             }
00938             return null;
00939 
00940         } else if ($component === 'theme') { //exception
00941             if ($image === 'favicon') {
00942                 return "$this->dir/pix/favicon.ico";
00943             }
00944             if ($imagefile = $this->image_exists("$this->dir/pix/$image")) {
00945                 return $imagefile;
00946             }
00947             foreach (array_reverse($this->parent_configs) as $parent_config) { // base first, the immediate parent last
00948                 if ($imagefile = $this->image_exists("$parent_config->dir/pix/$image")) {
00949                     return $imagefile;
00950                 }
00951             }
00952             return null;
00953 
00954         } else {
00955             if (strpos($component, '_') === false) {
00956                 $component = 'mod_'.$component;
00957             }
00958             list($type, $plugin) = explode('_', $component, 2);
00959 
00960             if ($imagefile = $this->image_exists("$this->dir/pix_plugins/$type/$plugin/$image")) {
00961                 return $imagefile;
00962             }
00963             foreach (array_reverse($this->parent_configs) as $parent_config) { // base first, the immediate parent last
00964                 if ($imagefile = $this->image_exists("$parent_config->dir/pix_plugins/$type/$plugin/$image")) {
00965                     return $imagefile;
00966                 }
00967             }
00968             $dir = get_plugin_directory($type, $plugin);
00969             if ($imagefile = $this->image_exists("$dir/pix/$image")) {
00970                 return $imagefile;
00971             }
00972             return null;
00973         }
00974     }
00975 
00981     private static function image_exists($filepath) {
00982         if (file_exists("$filepath.gif")) {
00983             return "$filepath.gif";
00984         } else  if (file_exists("$filepath.png")) {
00985             return "$filepath.png";
00986         } else  if (file_exists("$filepath.jpg")) {
00987             return "$filepath.jpg";
00988         } else  if (file_exists("$filepath.jpeg")) {
00989             return "$filepath.jpeg";
00990         } else {
00991             return false;
00992         }
00993     }
00994 
01001     private static function find_theme_config($themename, $settings) {
01002         // We have to use the variable name $THEME (upper case) because that
01003         // is what is used in theme config.php files.
01004 
01005         if (!$dir = theme_config::find_theme_location($themename)) {
01006             return null;
01007         }
01008 
01009         $THEME = new stdClass();
01010         $THEME->name     = $themename;
01011         $THEME->dir      = $dir;
01012         $THEME->settings = $settings;
01013 
01014         global $CFG; // just in case somebody tries to use $CFG in theme config
01015         include("$THEME->dir/config.php");
01016 
01017         // verify the theme configuration is OK
01018         if (!is_array($THEME->parents)) {
01019             // parents option is mandatory now
01020             return null;
01021         }
01022 
01023         return $THEME;
01024     }
01025 
01032     private static function find_theme_location($themename) {
01033         global $CFG;
01034 
01035         if (file_exists("$CFG->dirroot/theme/$themename/config.php")) {
01036             $dir = "$CFG->dirroot/theme/$themename";
01037 
01038         } else if (!empty($CFG->themedir) and file_exists("$CFG->themedir/$themename/config.php")) {
01039             $dir = "$CFG->themedir/$themename";
01040 
01041         } else {
01042             return null;
01043         }
01044 
01045         if (file_exists("$dir/styles.php")) {
01046             //legacy theme - needs to be upgraded - upgrade info is displayed on the admin settings page
01047             return null;
01048         }
01049 
01050         return $dir;
01051     }
01052 
01061     public function get_renderer(moodle_page $page, $component, $subtype = null, $target = null) {
01062         if (is_null($this->rf)) {
01063             $classname = $this->rendererfactory;
01064             $this->rf = new $classname($this);
01065         }
01066 
01067         return $this->rf->get_renderer($page, $component, $subtype, $target);
01068     }
01069 
01075     protected function layout_info_for_page($pagelayout) {
01076         if (array_key_exists($pagelayout, $this->layouts)) {
01077             return $this->layouts[$pagelayout];
01078         } else {
01079             debugging('Invalid page layout specified: ' . $pagelayout);
01080             return $this->layouts['standard'];
01081         }
01082     }
01083 
01093     public function layout_file($pagelayout) {
01094         global $CFG;
01095 
01096         $layoutinfo = $this->layout_info_for_page($pagelayout);
01097         $layoutfile = $layoutinfo['file'];
01098 
01099         if (array_key_exists('theme', $layoutinfo)) {
01100             $themes = array($layoutinfo['theme']);
01101         } else {
01102             $themes = array_merge(array($this->name),$this->parents);
01103         }
01104 
01105         foreach ($themes as $theme) {
01106             if ($dir = $this->find_theme_location($theme)) {
01107                 $path = "$dir/layout/$layoutfile";
01108 
01109                 // Check the template exists, return general base theme template if not.
01110                 if (is_readable($path)) {
01111                     return $path;
01112                 }
01113             }
01114         }
01115 
01116         debugging('Can not find layout file for: ' . $pagelayout);
01117         // fallback to standard normal layout
01118         return "$CFG->dirroot/theme/base/layout/general.php";
01119     }
01120 
01126     public function pagelayout_options($pagelayout) {
01127         $info = $this->layout_info_for_page($pagelayout);
01128         if (!empty($info['options'])) {
01129             return $info['options'];
01130         }
01131         return array();
01132     }
01133 
01141     public function setup_blocks($pagelayout, $blockmanager) {
01142         $layoutinfo = $this->layout_info_for_page($pagelayout);
01143         if (!empty($layoutinfo['regions'])) {
01144             $blockmanager->add_regions($layoutinfo['regions']);
01145             $blockmanager->set_default_region($layoutinfo['defaultregion']);
01146         }
01147     }
01148 
01149     protected function get_region_name($region, $theme) {
01150         $regionstring = get_string('region-' . $region, 'theme_' . $theme);
01151         // A name exists in this theme, so use it
01152         if (substr($regionstring, 0, 1) != '[') {
01153             return $regionstring;
01154         }
01155 
01156         // Otherwise, try to find one elsewhere
01157         // Check parents, if any
01158         foreach ($this->parents as $parentthemename) {
01159             $regionstring = get_string('region-' . $region, 'theme_' . $parentthemename);
01160             if (substr($regionstring, 0, 1) != '[') {
01161                 return $regionstring;
01162             }
01163         }
01164 
01165         // Last resort, try the base theme for names
01166         return get_string('region-' . $region, 'theme_base');
01167     }
01168 
01173     public function get_all_block_regions() {
01174         $regions = array();
01175         foreach ($this->layouts as $layoutinfo) {
01176             foreach ($layoutinfo['regions'] as $region) {
01177                 $regions[$region] = $this->get_region_name($region, $this->name);
01178             }
01179         }
01180         return $regions;
01181     }
01182 
01188     public function get_theme_name() {
01189         return get_string('pluginname', 'theme_'.$this->name);
01190     }
01191 }
01192 
01193 
01206 class xhtml_container_stack {
01208     protected $opencontainers = array();
01213     protected $log = array();
01219     protected $isdebugging;
01220 
01221     public function __construct() {
01222         $this->isdebugging = debugging('', DEBUG_DEVELOPER);
01223     }
01224 
01232     public function push($type, $closehtml) {
01233         $container = new stdClass;
01234         $container->type = $type;
01235         $container->closehtml = $closehtml;
01236         if ($this->isdebugging) {
01237             $this->log('Open', $type);
01238         }
01239         array_push($this->opencontainers, $container);
01240     }
01241 
01249     public function pop($type) {
01250         if (empty($this->opencontainers)) {
01251             debugging('<p>There are no more open containers. This suggests there is a nesting problem.</p>' .
01252                     $this->output_log(), DEBUG_DEVELOPER);
01253             return;
01254         }
01255 
01256         $container = array_pop($this->opencontainers);
01257         if ($container->type != $type) {
01258             debugging('<p>The type of container to be closed (' . $container->type .
01259                     ') does not match the type of the next open container (' . $type .
01260                     '). This suggests there is a nesting problem.</p>' .
01261                     $this->output_log(), DEBUG_DEVELOPER);
01262         }
01263         if ($this->isdebugging) {
01264             $this->log('Close', $type);
01265         }
01266         return $container->closehtml;
01267     }
01268 
01277     public function pop_all_but_last($shouldbenone = false) {
01278         if ($shouldbenone && count($this->opencontainers) != 1) {
01279             debugging('<p>Some HTML tags were opened in the body of the page but not closed.</p>' .
01280                     $this->output_log(), DEBUG_DEVELOPER);
01281         }
01282         $output = '';
01283         while (count($this->opencontainers) > 1) {
01284             $container = array_pop($this->opencontainers);
01285             $output .= $container->closehtml;
01286         }
01287         return $output;
01288     }
01289 
01297     public function discard() {
01298         $this->opencontainers = null;
01299     }
01300 
01307     protected function log($action, $type) {
01308         $this->log[] = '<li>' . $action . ' ' . $type . ' at:' .
01309                 format_backtrace(debug_backtrace()) . '</li>';
01310     }
01311 
01316     protected function output_log() {
01317         return '<ul>' . implode("\n", $this->log) . '</ul>';
01318     }
01319 }
 All Data Structures Namespaces Files Functions Variables Enumerations