Moodle  2.2.1
http://www.collinsharper.com
C:/xampp/htdocs/moodle/lib/outputrenderers.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 
00043 class renderer_base {
00045     protected $opencontainers;
00047     protected $page;
00049     protected $target;
00050 
00056     public function __construct(moodle_page $page, $target) {
00057         $this->opencontainers = $page->opencontainers;
00058         $this->page = $page;
00059         $this->target = $target;
00060     }
00061 
00067     public function render(renderable $widget) {
00068         $rendermethod = 'render_'.get_class($widget);
00069         if (method_exists($this, $rendermethod)) {
00070             return $this->$rendermethod($widget);
00071         }
00072         throw new coding_exception('Can not render widget, renderer method ('.$rendermethod.') not found.');
00073     }
00074 
00081     public function add_action_handler(component_action $action, $id=null) {
00082         if (!$id) {
00083             $id = html_writer::random_id($action->event);
00084         }
00085         $this->page->requires->event_handler("#$id", $action->event, $action->jsfunction, $action->jsfunctionargs);
00086         return $id;
00087     }
00088 
00093     public function has_started() {
00094         return $this->page->state >= moodle_page::STATE_IN_BODY;
00095     }
00096 
00102     public static function prepare_classes($classes) {
00103         if (is_array($classes)) {
00104             return implode(' ', array_unique($classes));
00105         }
00106         return $classes;
00107     }
00108 
00132     public function pix_url($imagename, $component = 'moodle') {
00133         return $this->page->theme->pix_url($imagename, $component);
00134     }
00135 }
00136 
00137 
00145 class plugin_renderer_base extends renderer_base {
00150     protected $output;
00151 
00157     public function __construct(moodle_page $page, $target) {
00158         $this->output = $page->get_renderer('core', null, $target);
00159         parent::__construct($page, $target);
00160     }
00161 
00167     public function render(renderable $widget) {
00168         $rendermethod = 'render_'.get_class($widget);
00169         if (method_exists($this, $rendermethod)) {
00170             return $this->$rendermethod($widget);
00171         }
00172         // pass to core renderer if method not found here
00173         return $this->output->render($widget);
00174     }
00175 
00184     public function __call($method, $arguments) {
00185         if (method_exists('renderer_base', $method)) {
00186             throw new coding_exception('Protected method called against '.__CLASS__.' :: '.$method);
00187         }
00188         if (method_exists($this->output, $method)) {
00189             return call_user_func_array(array($this->output, $method), $arguments);
00190         } else {
00191             throw new coding_exception('Unknown method called against '.__CLASS__.' :: '.$method);
00192         }
00193     }
00194 }
00195 
00196 
00204 class core_renderer extends renderer_base {
00211     const MAIN_CONTENT_TOKEN = '[MAIN CONTENT GOES HERE]';
00213     protected $contenttype;
00215     protected $metarefreshtag = '';
00217     protected $unique_end_html_token;
00219     protected $unique_performance_info_token;
00221     protected $unique_main_content_token;
00222 
00228     public function __construct(moodle_page $page, $target) {
00229         $this->opencontainers = $page->opencontainers;
00230         $this->page = $page;
00231         $this->target = $target;
00232 
00233         $this->unique_end_html_token = '%%ENDHTML-'.sesskey().'%%';
00234         $this->unique_performance_info_token = '%%PERFORMANCEINFO-'.sesskey().'%%';
00235         $this->unique_main_content_token = '[MAIN CONTENT GOES HERE - '.sesskey().']';
00236     }
00237 
00243     public function doctype() {
00244         global $CFG;
00245 
00246         $doctype = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">' . "\n";
00247         $this->contenttype = 'text/html; charset=utf-8';
00248 
00249         if (empty($CFG->xmlstrictheaders)) {
00250             return $doctype;
00251         }
00252 
00253         // We want to serve the page with an XML content type, to force well-formedness errors to be reported.
00254         $prolog = '<?xml version="1.0" encoding="utf-8"?>' . "\n";
00255         if (isset($_SERVER['HTTP_ACCEPT']) && strpos($_SERVER['HTTP_ACCEPT'], 'application/xhtml+xml') !== false) {
00256             // Firefox and other browsers that can cope natively with XHTML.
00257             $this->contenttype = 'application/xhtml+xml; charset=utf-8';
00258 
00259         } else if (preg_match('/MSIE.*Windows NT/', $_SERVER['HTTP_USER_AGENT'])) {
00260             // IE can't cope with application/xhtml+xml, but it will cope if we send application/xml with an XSL stylesheet.
00261             $this->contenttype = 'application/xml; charset=utf-8';
00262             $prolog .= '<?xml-stylesheet type="text/xsl" href="' . $CFG->httpswwwroot . '/lib/xhtml.xsl"?>' . "\n";
00263 
00264         } else {
00265             $prolog = '';
00266         }
00267 
00268         return $prolog . $doctype;
00269     }
00270 
00276     public function htmlattributes() {
00277         return get_html_lang(true) . ' xmlns="http://www.w3.org/1999/xhtml"';
00278     }
00279 
00286     public function standard_head_html() {
00287         global $CFG, $SESSION;
00288         $output = '';
00289         $output .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />' . "\n";
00290         $output .= '<meta name="keywords" content="moodle, ' . $this->page->title . '" />' . "\n";
00291         if (!$this->page->cacheable) {
00292             $output .= '<meta http-equiv="pragma" content="no-cache" />' . "\n";
00293             $output .= '<meta http-equiv="expires" content="0" />' . "\n";
00294         }
00295         // This is only set by the {@link redirect()} method
00296         $output .= $this->metarefreshtag;
00297 
00298         // Check if a periodic refresh delay has been set and make sure we arn't
00299         // already meta refreshing
00300         if ($this->metarefreshtag=='' && $this->page->periodicrefreshdelay!==null) {
00301             $output .= '<meta http-equiv="refresh" content="'.$this->page->periodicrefreshdelay.';url='.$this->page->url->out().'" />';
00302         }
00303 
00304         // flow player embedding support
00305         $this->page->requires->js_function_call('M.util.load_flowplayer');
00306 
00307         $this->page->requires->js_function_call('setTimeout', array('fix_column_widths()', 20));
00308 
00309         $focus = $this->page->focuscontrol;
00310         if (!empty($focus)) {
00311             if (preg_match("#forms\['([a-zA-Z0-9]+)'\].elements\['([a-zA-Z0-9]+)'\]#", $focus, $matches)) {
00312                 // This is a horrifically bad way to handle focus but it is passed in
00313                 // through messy formslib::moodleform
00314                 $this->page->requires->js_function_call('old_onload_focus', array($matches[1], $matches[2]));
00315             } else if (strpos($focus, '.')!==false) {
00316                 // Old style of focus, bad way to do it
00317                 debugging('This code is using the old style focus event, Please update this code to focus on an element id or the moodleform focus method.', DEBUG_DEVELOPER);
00318                 $this->page->requires->js_function_call('old_onload_focus', explode('.', $focus, 2));
00319             } else {
00320                 // Focus element with given id
00321                 $this->page->requires->js_function_call('focuscontrol', array($focus));
00322             }
00323         }
00324 
00325         // Get the theme stylesheet - this has to be always first CSS, this loads also styles.css from all plugins;
00326         // any other custom CSS can not be overridden via themes and is highly discouraged
00327         $urls = $this->page->theme->css_urls($this->page);
00328         foreach ($urls as $url) {
00329             $this->page->requires->css_theme($url);
00330         }
00331 
00332         // Get the theme javascript head and footer
00333         $jsurl = $this->page->theme->javascript_url(true);
00334         $this->page->requires->js($jsurl, true);
00335         $jsurl = $this->page->theme->javascript_url(false);
00336         $this->page->requires->js($jsurl);
00337 
00338         // Get any HTML from the page_requirements_manager.
00339         $output .= $this->page->requires->get_head_code($this->page, $this);
00340 
00341         // List alternate versions.
00342         foreach ($this->page->alternateversions as $type => $alt) {
00343             $output .= html_writer::empty_tag('link', array('rel' => 'alternate',
00344                     'type' => $type, 'title' => $alt->title, 'href' => $alt->url));
00345         }
00346 
00347         if (!empty($CFG->additionalhtmlhead)) {
00348             $output .= "\n".$CFG->additionalhtmlhead;
00349         }
00350 
00351         return $output;
00352     }
00353 
00359     public function standard_top_of_body_html() {
00360         global $CFG;
00361         $output = $this->page->requires->get_top_of_body_code();
00362         if (!empty($CFG->additionalhtmltopofbody)) {
00363             $output .= "\n".$CFG->additionalhtmltopofbody;
00364         }
00365         return $output;
00366     }
00367 
00374     public function standard_footer_html() {
00375         global $CFG, $SCRIPT;
00376 
00377         // This function is normally called from a layout.php file in {@link header()}
00378         // but some of the content won't be known until later, so we return a placeholder
00379         // for now. This will be replaced with the real content in {@link footer()}.
00380         $output = $this->unique_performance_info_token;
00381         if ($this->page->devicetypeinuse == 'legacy') {
00382             // The legacy theme is in use print the notification
00383             $output .= html_writer::tag('div', get_string('legacythemeinuse'), array('class'=>'legacythemeinuse'));
00384         }
00385 
00386         // Get links to switch device types (only shown for users not on a default device)
00387         $output .= $this->theme_switch_links();
00388 
00389         if (!empty($CFG->debugpageinfo)) {
00390             $output .= '<div class="performanceinfo pageinfo">This page is: ' . $this->page->debug_summary() . '</div>';
00391         }
00392         if (debugging(null, DEBUG_DEVELOPER) and has_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM))) {  // Only in developer mode
00393             // Add link to profiling report if necessary
00394             if (function_exists('profiling_is_running') && profiling_is_running()) {
00395                 $txt = get_string('profiledscript', 'admin');
00396                 $title = get_string('profiledscriptview', 'admin');
00397                 $url = $CFG->wwwroot . '/admin/tool/profiling/index.php?script=' . urlencode($SCRIPT);
00398                 $link= '<a title="' . $title . '" href="' . $url . '">' . $txt . '</a>';
00399                 $output .= '<div class="profilingfooter">' . $link . '</div>';
00400             }
00401             $output .= '<div class="purgecaches"><a href="'.$CFG->wwwroot.'/admin/purgecaches.php?confirm=1&amp;sesskey='.sesskey().'">'.get_string('purgecaches', 'admin').'</a></div>';
00402         }
00403         if (!empty($CFG->debugvalidators)) {
00404             $output .= '<div class="validators"><ul>
00405               <li><a href="http://validator.w3.org/check?verbose=1&amp;ss=1&amp;uri=' . urlencode(qualified_me()) . '">Validate HTML</a></li>
00406               <li><a href="http://www.contentquality.com/mynewtester/cynthia.exe?rptmode=-1&amp;url1=' . urlencode(qualified_me()) . '">Section 508 Check</a></li>
00407               <li><a href="http://www.contentquality.com/mynewtester/cynthia.exe?rptmode=0&amp;warnp2n3e=1&amp;url1=' . urlencode(qualified_me()) . '">WCAG 1 (2,3) Check</a></li>
00408             </ul></div>';
00409         }
00410         if (!empty($CFG->additionalhtmlfooter)) {
00411             $output .= "\n".$CFG->additionalhtmlfooter;
00412         }
00413         return $output;
00414     }
00415 
00421     public function main_content() {
00422         return $this->unique_main_content_token;
00423     }
00424 
00430     public function standard_end_of_body_html() {
00431         // This function is normally called from a layout.php file in {@link header()}
00432         // but some of the content won't be known until later, so we return a placeholder
00433         // for now. This will be replaced with the real content in {@link footer()}.
00434         return $this->unique_end_html_token;
00435     }
00436 
00442     public function login_info() {
00443         global $USER, $CFG, $DB, $SESSION;
00444 
00445         if (during_initial_install()) {
00446             return '';
00447         }
00448 
00449         $loginapge = ((string)$this->page->url === get_login_url());
00450         $course = $this->page->course;
00451 
00452         if (session_is_loggedinas()) {
00453             $realuser = session_get_realuser();
00454             $fullname = fullname($realuser, true);
00455             $realuserinfo = " [<a href=\"$CFG->wwwroot/course/loginas.php?id=$course->id&amp;sesskey=".sesskey()."\">$fullname</a>] ";
00456         } else {
00457             $realuserinfo = '';
00458         }
00459 
00460         $loginurl = get_login_url();
00461 
00462         if (empty($course->id)) {
00463             // $course->id is not defined during installation
00464             return '';
00465         } else if (isloggedin()) {
00466             $context = get_context_instance(CONTEXT_COURSE, $course->id);
00467 
00468             $fullname = fullname($USER, true);
00469             // Since Moodle 2.0 this link always goes to the public profile page (not the course profile page)
00470             $username = "<a href=\"$CFG->wwwroot/user/profile.php?id=$USER->id\">$fullname</a>";
00471             if (is_mnet_remote_user($USER) and $idprovider = $DB->get_record('mnet_host', array('id'=>$USER->mnethostid))) {
00472                 $username .= " from <a href=\"{$idprovider->wwwroot}\">{$idprovider->name}</a>";
00473             }
00474             if (isguestuser()) {
00475                 $loggedinas = $realuserinfo.get_string('loggedinasguest');
00476                 if (!$loginapge) {
00477                     $loggedinas .= " (<a href=\"$loginurl\">".get_string('login').'</a>)';
00478                 }
00479             } else if (is_role_switched($course->id)) { // Has switched roles
00480                 $rolename = '';
00481                 if ($role = $DB->get_record('role', array('id'=>$USER->access['rsw'][$context->path]))) {
00482                     $rolename = ': '.format_string($role->name);
00483                 }
00484                 $loggedinas = get_string('loggedinas', 'moodle', $username).$rolename.
00485                           " (<a href=\"$CFG->wwwroot/course/view.php?id=$course->id&amp;switchrole=0&amp;sesskey=".sesskey()."\">".get_string('switchrolereturn').'</a>)';
00486             } else {
00487                 $loggedinas = $realuserinfo.get_string('loggedinas', 'moodle', $username).' '.
00488                           " (<a href=\"$CFG->wwwroot/login/logout.php?sesskey=".sesskey()."\">".get_string('logout').'</a>)';
00489             }
00490         } else {
00491             $loggedinas = get_string('loggedinnot', 'moodle');
00492             if (!$loginapge) {
00493                 $loggedinas .= " (<a href=\"$loginurl\">".get_string('login').'</a>)';
00494             }
00495         }
00496 
00497         $loggedinas = '<div class="logininfo">'.$loggedinas.'</div>';
00498 
00499         if (isset($SESSION->justloggedin)) {
00500             unset($SESSION->justloggedin);
00501             if (!empty($CFG->displayloginfailures)) {
00502                 if (!isguestuser()) {
00503                     if ($count = count_login_failures($CFG->displayloginfailures, $USER->username, $USER->lastlogin)) {
00504                         $loggedinas .= '&nbsp;<div class="loginfailures">';
00505                         if (empty($count->accounts)) {
00506                             $loggedinas .= get_string('failedloginattempts', '', $count);
00507                         } else {
00508                             $loggedinas .= get_string('failedloginattemptsall', '', $count);
00509                         }
00510                         if (file_exists("$CFG->dirroot/report/log/index.php") and has_capability('report/log:view', get_context_instance(CONTEXT_SYSTEM))) {
00511                             $loggedinas .= ' (<a href="'.$CFG->wwwroot.'/report/log/index.php'.
00512                                                  '?chooselog=1&amp;id=1&amp;modid=site_errors">'.get_string('logs').'</a>)';
00513                         }
00514                         $loggedinas .= '</div>';
00515                     }
00516                 }
00517             }
00518         }
00519 
00520         return $loggedinas;
00521     }
00522 
00527     public function home_link() {
00528         global $CFG, $SITE;
00529 
00530         if ($this->page->pagetype == 'site-index') {
00531             // Special case for site home page - please do not remove
00532             return '<div class="sitelink">' .
00533                    '<a title="Moodle" href="http://moodle.org/">' .
00534                    '<img style="width:100px;height:30px" src="' . $this->pix_url('moodlelogo') . '" alt="moodlelogo" /></a></div>';
00535 
00536         } else if (!empty($CFG->target_release) && $CFG->target_release != $CFG->release) {
00537             // Special case for during install/upgrade.
00538             return '<div class="sitelink">'.
00539                    '<a title="Moodle" href="http://docs.moodle.org/en/Administrator_documentation" onclick="this.target=\'_blank\'">' .
00540                    '<img style="width:100px;height:30px" src="' . $this->pix_url('moodlelogo') . '" alt="moodlelogo" /></a></div>';
00541 
00542         } else if ($this->page->course->id == $SITE->id || strpos($this->page->pagetype, 'course-view') === 0) {
00543             return '<div class="homelink"><a href="' . $CFG->wwwroot . '/">' .
00544                     get_string('home') . '</a></div>';
00545 
00546         } else {
00547             return '<div class="homelink"><a href="' . $CFG->wwwroot . '/course/view.php?id=' . $this->page->course->id . '">' .
00548                     format_string($this->page->course->shortname, true, array('context' => $this->page->context)) . '</a></div>';
00549         }
00550     }
00551 
00571     public function redirect_message($encodedurl, $message, $delay, $debugdisableredirect) {
00572         global $CFG;
00573         $url = str_replace('&amp;', '&', $encodedurl);
00574 
00575         switch ($this->page->state) {
00576             case moodle_page::STATE_BEFORE_HEADER :
00577                 // No output yet it is safe to delivery the full arsenal of redirect methods
00578                 if (!$debugdisableredirect) {
00579                     // Don't use exactly the same time here, it can cause problems when both redirects fire at the same time.
00580                     $this->metarefreshtag = '<meta http-equiv="refresh" content="'. $delay .'; url='. $encodedurl .'" />'."\n";
00581                     $this->page->requires->js_function_call('document.location.replace', array($url), false, ($delay + 3));
00582                 }
00583                 $output = $this->header();
00584                 break;
00585             case moodle_page::STATE_PRINTING_HEADER :
00586                 // We should hopefully never get here
00587                 throw new coding_exception('You cannot redirect while printing the page header');
00588                 break;
00589             case moodle_page::STATE_IN_BODY :
00590                 // We really shouldn't be here but we can deal with this
00591                 debugging("You should really redirect before you start page output");
00592                 if (!$debugdisableredirect) {
00593                     $this->page->requires->js_function_call('document.location.replace', array($url), false, $delay);
00594                 }
00595                 $output = $this->opencontainers->pop_all_but_last();
00596                 break;
00597             case moodle_page::STATE_DONE :
00598                 // Too late to be calling redirect now
00599                 throw new coding_exception('You cannot redirect after the entire page has been generated');
00600                 break;
00601         }
00602         $output .= $this->notification($message, 'redirectmessage');
00603         $output .= '<div class="continuebutton">(<a href="'. $encodedurl .'">'. get_string('continue') .'</a>)</div>';
00604         if ($debugdisableredirect) {
00605             $output .= '<p><strong>Error output, so disabling automatic redirect.</strong></p>';
00606         }
00607         $output .= $this->footer();
00608         return $output;
00609     }
00610 
00625     public function header() {
00626         global $USER, $CFG;
00627 
00628         if (session_is_loggedinas()) {
00629             $this->page->add_body_class('userloggedinas');
00630         }
00631 
00632         $this->page->set_state(moodle_page::STATE_PRINTING_HEADER);
00633 
00634         // Find the appropriate page layout file, based on $this->page->pagelayout.
00635         $layoutfile = $this->page->theme->layout_file($this->page->pagelayout);
00636         // Render the layout using the layout file.
00637         $rendered = $this->render_page_layout($layoutfile);
00638 
00639         // Slice the rendered output into header and footer.
00640         $cutpos = strpos($rendered, $this->unique_main_content_token);
00641         if ($cutpos === false) {
00642             $cutpos = strpos($rendered, self::MAIN_CONTENT_TOKEN);
00643             $token = self::MAIN_CONTENT_TOKEN;
00644         } else {
00645             $token = $this->unique_main_content_token;
00646         }
00647 
00648         if ($cutpos === false) {
00649             throw new coding_exception('page layout file ' . $layoutfile . ' does not contain the main content placeholder, please include "<?php echo $OUTPUT->main_content() ?>" in theme layout file.');
00650         }
00651         $header = substr($rendered, 0, $cutpos);
00652         $footer = substr($rendered, $cutpos + strlen($token));
00653 
00654         if (empty($this->contenttype)) {
00655             debugging('The page layout file did not call $OUTPUT->doctype()');
00656             $header = $this->doctype() . $header;
00657         }
00658 
00659         send_headers($this->contenttype, $this->page->cacheable);
00660 
00661         $this->opencontainers->push('header/footer', $footer);
00662         $this->page->set_state(moodle_page::STATE_IN_BODY);
00663 
00664         return $header . $this->skip_link_target('maincontent');
00665     }
00666 
00672     protected function render_page_layout($layoutfile) {
00673         global $CFG, $SITE, $USER;
00674         // The next lines are a bit tricky. The point is, here we are in a method
00675         // of a renderer class, and this object may, or may not, be the same as
00676         // the global $OUTPUT object. When rendering the page layout file, we want to use
00677         // this object. However, people writing Moodle code expect the current
00678         // renderer to be called $OUTPUT, not $this, so define a variable called
00679         // $OUTPUT pointing at $this. The same comment applies to $PAGE and $COURSE.
00680         $OUTPUT = $this;
00681         $PAGE = $this->page;
00682         $COURSE = $this->page->course;
00683 
00684         ob_start();
00685         include($layoutfile);
00686         $rendered = ob_get_contents();
00687         ob_end_clean();
00688         return $rendered;
00689     }
00690 
00695     public function footer() {
00696         global $CFG, $DB;
00697 
00698         $output = $this->container_end_all(true);
00699 
00700         $footer = $this->opencontainers->pop('header/footer');
00701 
00702         if (debugging() and $DB and $DB->is_transaction_started()) {
00703             // TODO: MDL-20625 print warning - transaction will be rolled back
00704         }
00705 
00706         // Provide some performance info if required
00707         $performanceinfo = '';
00708         if (defined('MDL_PERF') || (!empty($CFG->perfdebug) and $CFG->perfdebug > 7)) {
00709             $perf = get_performance_info();
00710             if (defined('MDL_PERFTOLOG') && !function_exists('register_shutdown_function')) {
00711                 error_log("PERF: " . $perf['txt']);
00712             }
00713             if (defined('MDL_PERFTOFOOT') || debugging() || $CFG->perfdebug > 7) {
00714                 $performanceinfo = $perf['html'];
00715             }
00716         }
00717         $footer = str_replace($this->unique_performance_info_token, $performanceinfo, $footer);
00718 
00719         $footer = str_replace($this->unique_end_html_token, $this->page->requires->get_end_code(), $footer);
00720 
00721         $this->page->set_state(moodle_page::STATE_DONE);
00722 
00723         return $output . $footer;
00724     }
00725 
00734     public function container_end_all($shouldbenone = false) {
00735         return $this->opencontainers->pop_all_but_last($shouldbenone);
00736     }
00737 
00742     public function lang_menu() {
00743         global $CFG;
00744 
00745         if (empty($CFG->langmenu)) {
00746             return '';
00747         }
00748 
00749         if ($this->page->course != SITEID and !empty($this->page->course->lang)) {
00750             // do not show lang menu if language forced
00751             return '';
00752         }
00753 
00754         $currlang = current_language();
00755         $langs = get_string_manager()->get_list_of_translations();
00756 
00757         if (count($langs) < 2) {
00758             return '';
00759         }
00760 
00761         $s = new single_select($this->page->url, 'lang', $langs, $currlang, null);
00762         $s->label = get_accesshide(get_string('language'));
00763         $s->class = 'langmenu';
00764         return $this->render($s);
00765     }
00766 
00772     public function block_controls($controls) {
00773         if (empty($controls)) {
00774             return '';
00775         }
00776         $controlshtml = array();
00777         foreach ($controls as $control) {
00778             $controlshtml[] = html_writer::tag('a',
00779                     html_writer::empty_tag('img',  array('src' => $this->pix_url($control['icon'])->out(false), 'alt' => $control['caption'])),
00780                     array('class' => 'icon','title' => $control['caption'], 'href' => $control['url']));
00781         }
00782         return html_writer::tag('div', implode('', $controlshtml), array('class' => 'commands'));
00783     }
00784 
00806     function block(block_contents $bc, $region) {
00807         $bc = clone($bc); // Avoid messing up the object passed in.
00808         if (empty($bc->blockinstanceid) || !strip_tags($bc->title)) {
00809             $bc->collapsible = block_contents::NOT_HIDEABLE;
00810         }
00811         if ($bc->collapsible == block_contents::HIDDEN) {
00812             $bc->add_class('hidden');
00813         }
00814         if (!empty($bc->controls)) {
00815             $bc->add_class('block_with_controls');
00816         }
00817 
00818         $skiptitle = strip_tags($bc->title);
00819         if (empty($skiptitle)) {
00820             $output = '';
00821             $skipdest = '';
00822         } else {
00823             $output = html_writer::tag('a', get_string('skipa', 'access', $skiptitle), array('href' => '#sb-' . $bc->skipid, 'class' => 'skip-block'));
00824             $skipdest = html_writer::tag('span', '', array('id' => 'sb-' . $bc->skipid, 'class' => 'skip-block-to'));
00825         }
00826 
00827         $output .= html_writer::start_tag('div', $bc->attributes);
00828 
00829         $output .= $this->block_header($bc);
00830         $output .= $this->block_content($bc);
00831 
00832         $output .= html_writer::end_tag('div');
00833 
00834         $output .= $this->block_annotation($bc);
00835 
00836         $output .= $skipdest;
00837 
00838         $this->init_block_hider_js($bc);
00839         return $output;
00840     }
00841 
00848     protected function block_header(block_contents $bc) {
00849 
00850         $title = '';
00851         if ($bc->title) {
00852             $title = html_writer::tag('h2', $bc->title, null);
00853         }
00854 
00855         $controlshtml = $this->block_controls($bc->controls);
00856 
00857         $output = '';
00858         if ($title || $controlshtml) {
00859             $output .= html_writer::tag('div', html_writer::tag('div', html_writer::tag('div', '', array('class'=>'block_action')). $title . $controlshtml, array('class' => 'title')), array('class' => 'header'));
00860         }
00861         return $output;
00862     }
00863 
00870     protected function block_content(block_contents $bc) {
00871         $output = html_writer::start_tag('div', array('class' => 'content'));
00872         if (!$bc->title && !$this->block_controls($bc->controls)) {
00873             $output .= html_writer::tag('div', '', array('class'=>'block_action notitle'));
00874         }
00875         $output .= $bc->content;
00876         $output .= $this->block_footer($bc);
00877         $output .= html_writer::end_tag('div');
00878 
00879         return $output;
00880     }
00881 
00888     protected function block_footer(block_contents $bc) {
00889         $output = '';
00890         if ($bc->footer) {
00891             $output .= html_writer::tag('div', $bc->footer, array('class' => 'footer'));
00892         }
00893         return $output;
00894     }
00895 
00902     protected function block_annotation(block_contents $bc) {
00903         $output = '';
00904         if ($bc->annotation) {
00905             $output .= html_writer::tag('div', $bc->annotation, array('class' => 'blockannotation'));
00906         }
00907         return $output;
00908     }
00909 
00915     protected function init_block_hider_js(block_contents $bc) {
00916         if (!empty($bc->attributes['id']) and $bc->collapsible != block_contents::NOT_HIDEABLE) {
00917             $config = new stdClass;
00918             $config->id = $bc->attributes['id'];
00919             $config->title = strip_tags($bc->title);
00920             $config->preference = 'block' . $bc->blockinstanceid . 'hidden';
00921             $config->tooltipVisible = get_string('hideblocka', 'access', $config->title);
00922             $config->tooltipHidden = get_string('showblocka', 'access', $config->title);
00923 
00924             $this->page->requires->js_init_call('M.util.init_block_hider', array($config));
00925             user_preference_allow_ajax_update($config->preference, PARAM_BOOL);
00926         }
00927     }
00928 
00935     public function list_block_contents($icons, $items) {
00936         $row = 0;
00937         $lis = array();
00938         foreach ($items as $key => $string) {
00939             $item = html_writer::start_tag('li', array('class' => 'r' . $row));
00940             if (!empty($icons[$key])) { //test if the content has an assigned icon
00941                 $item .= html_writer::tag('div', $icons[$key], array('class' => 'icon column c0'));
00942             }
00943             $item .= html_writer::tag('div', $string, array('class' => 'column c1'));
00944             $item .= html_writer::end_tag('li');
00945             $lis[] = $item;
00946             $row = 1 - $row; // Flip even/odd.
00947         }
00948         return html_writer::tag('ul', implode("\n", $lis), array('class' => 'unlist'));
00949     }
00950 
00956     public function blocks_for_region($region) {
00957         $blockcontents = $this->page->blocks->get_content_for_region($region, $this);
00958 
00959         $output = '';
00960         foreach ($blockcontents as $bc) {
00961             if ($bc instanceof block_contents) {
00962                 $output .= $this->block($bc, $region);
00963             } else if ($bc instanceof block_move_target) {
00964                 $output .= $this->block_move_target($bc);
00965             } else {
00966                 throw new coding_exception('Unexpected type of thing (' . get_class($bc) . ') found in list of block contents.');
00967             }
00968         }
00969         return $output;
00970     }
00971 
00977     public function block_move_target($target) {
00978         return html_writer::tag('a', html_writer::tag('span', $target->text, array('class' => 'accesshide')), array('href' => $target->url, 'class' => 'blockmovetarget'));
00979     }
00980 
00990     public function action_link($url, $text, component_action $action = null, array $attributes=null) {
00991         if (!($url instanceof moodle_url)) {
00992             $url = new moodle_url($url);
00993         }
00994         $link = new action_link($url, $text, $action, $attributes);
00995 
00996         return $this->render($link);
00997     }
00998 
01004     protected function render_action_link(action_link $link) {
01005         global $CFG;
01006 
01007         if ($link->text instanceof renderable) {
01008             $text = $this->render($link->text);
01009         } else {
01010             $text = $link->text;
01011         }
01012 
01013         // A disabled link is rendered as formatted text
01014         if (!empty($link->attributes['disabled'])) {
01015             // do not use div here due to nesting restriction in xhtml strict
01016             return html_writer::tag('span', $text, array('class'=>'currentlink'));
01017         }
01018 
01019         $attributes = $link->attributes;
01020         unset($link->attributes['disabled']);
01021         $attributes['href'] = $link->url;
01022 
01023         if ($link->actions) {
01024             if (empty($attributes['id'])) {
01025                 $id = html_writer::random_id('action_link');
01026                 $attributes['id'] = $id;
01027             } else {
01028                 $id = $attributes['id'];
01029             }
01030             foreach ($link->actions as $action) {
01031                 $this->add_action_handler($action, $id);
01032             }
01033         }
01034 
01035         return html_writer::tag('a', $text, $attributes);
01036     }
01037 
01038 
01049     public function action_icon($url, pix_icon $pixicon, component_action $action = null, array $attributes = null, $linktext=false) {
01050         if (!($url instanceof moodle_url)) {
01051             $url = new moodle_url($url);
01052         }
01053         $attributes = (array)$attributes;
01054 
01055         if (empty($attributes['class'])) {
01056             // let ppl override the class via $options
01057             $attributes['class'] = 'action-icon';
01058         }
01059 
01060         $icon = $this->render($pixicon);
01061 
01062         if ($linktext) {
01063             $text = $pixicon->attributes['alt'];
01064         } else {
01065             $text = '';
01066         }
01067 
01068         return $this->action_link($url, $text.$icon, $action, $attributes);
01069     }
01070 
01081     public function confirm($message, $continue, $cancel) {
01082         if ($continue instanceof single_button) {
01083             // ok
01084         } else if (is_string($continue)) {
01085             $continue = new single_button(new moodle_url($continue), get_string('continue'), 'post');
01086         } else if ($continue instanceof moodle_url) {
01087             $continue = new single_button($continue, get_string('continue'), 'post');
01088         } else {
01089             throw new coding_exception('The continue param to $OUTPUT->confirm() must be either a URL (string/moodle_url) or a single_button instance.');
01090         }
01091 
01092         if ($cancel instanceof single_button) {
01093             // ok
01094         } else if (is_string($cancel)) {
01095             $cancel = new single_button(new moodle_url($cancel), get_string('cancel'), 'get');
01096         } else if ($cancel instanceof moodle_url) {
01097             $cancel = new single_button($cancel, get_string('cancel'), 'get');
01098         } else {
01099             throw new coding_exception('The cancel param to $OUTPUT->confirm() must be either a URL (string/moodle_url) or a single_button instance.');
01100         }
01101 
01102         $output = $this->box_start('generalbox', 'notice');
01103         $output .= html_writer::tag('p', $message);
01104         $output .= html_writer::tag('div', $this->render($continue) . $this->render($cancel), array('class' => 'buttons'));
01105         $output .= $this->box_end();
01106         return $output;
01107     }
01108 
01118     public function single_button($url, $label, $method='post', array $options=null) {
01119         if (!($url instanceof moodle_url)) {
01120             $url = new moodle_url($url);
01121         }
01122         $button = new single_button($url, $label, $method);
01123 
01124         foreach ((array)$options as $key=>$value) {
01125             if (array_key_exists($key, $button)) {
01126                 $button->$key = $value;
01127             }
01128         }
01129 
01130         return $this->render($button);
01131     }
01132 
01138     protected function render_single_button(single_button $button) {
01139         $attributes = array('type'     => 'submit',
01140                             'value'    => $button->label,
01141                             'disabled' => $button->disabled ? 'disabled' : null,
01142                             'title'    => $button->tooltip);
01143 
01144         if ($button->actions) {
01145             $id = html_writer::random_id('single_button');
01146             $attributes['id'] = $id;
01147             foreach ($button->actions as $action) {
01148                 $this->add_action_handler($action, $id);
01149             }
01150         }
01151 
01152         // first the input element
01153         $output = html_writer::empty_tag('input', $attributes);
01154 
01155         // then hidden fields
01156         $params = $button->url->params();
01157         if ($button->method === 'post') {
01158             $params['sesskey'] = sesskey();
01159         }
01160         foreach ($params as $var => $val) {
01161             $output .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => $var, 'value' => $val));
01162         }
01163 
01164         // then div wrapper for xhtml strictness
01165         $output = html_writer::tag('div', $output);
01166 
01167         // now the form itself around it
01168         if ($button->method === 'get') {
01169             $url = $button->url->out_omit_querystring(true); // url without params, the anchor part allowed
01170         } else {
01171             $url = $button->url->out_omit_querystring();     // url without params, the anchor part not allowed
01172         }
01173         if ($url === '') {
01174             $url = '#'; // there has to be always some action
01175         }
01176         $attributes = array('method' => $button->method,
01177                             'action' => $url,
01178                             'id'     => $button->formid);
01179         $output = html_writer::tag('form', $output, $attributes);
01180 
01181         // and finally one more wrapper with class
01182         return html_writer::tag('div', $output, array('class' => $button->class));
01183     }
01184 
01195     public function single_select($url, $name, array $options, $selected='', $nothing=array(''=>'choosedots'), $formid=null) {
01196         if (!($url instanceof moodle_url)) {
01197             $url = new moodle_url($url);
01198         }
01199         $select = new single_select($url, $name, $options, $selected, $nothing, $formid);
01200 
01201         return $this->render($select);
01202     }
01203 
01209     protected function render_single_select(single_select $select) {
01210         $select = clone($select);
01211         if (empty($select->formid)) {
01212             $select->formid = html_writer::random_id('single_select_f');
01213         }
01214 
01215         $output = '';
01216         $params = $select->url->params();
01217         if ($select->method === 'post') {
01218             $params['sesskey'] = sesskey();
01219         }
01220         foreach ($params as $name=>$value) {
01221             $output .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>$name, 'value'=>$value));
01222         }
01223 
01224         if (empty($select->attributes['id'])) {
01225             $select->attributes['id'] = html_writer::random_id('single_select');
01226         }
01227 
01228         if ($select->disabled) {
01229             $select->attributes['disabled'] = 'disabled';
01230         }
01231 
01232         if ($select->tooltip) {
01233             $select->attributes['title'] = $select->tooltip;
01234         }
01235 
01236         if ($select->label) {
01237             $output .= html_writer::label($select->label, $select->attributes['id']);
01238         }
01239 
01240         if ($select->helpicon instanceof help_icon) {
01241             $output .= $this->render($select->helpicon);
01242         } else if ($select->helpicon instanceof old_help_icon) {
01243             $output .= $this->render($select->helpicon);
01244         }
01245 
01246         $output .= html_writer::select($select->options, $select->name, $select->selected, $select->nothing, $select->attributes);
01247 
01248         $go = html_writer::empty_tag('input', array('type'=>'submit', 'value'=>get_string('go')));
01249         $output .= html_writer::tag('noscript', html_writer::tag('div', $go), array('style'=>'inline'));
01250 
01251         $nothing = empty($select->nothing) ? false : key($select->nothing);
01252         $this->page->requires->js_init_call('M.util.init_select_autosubmit', array($select->formid, $select->attributes['id'], $nothing));
01253 
01254         // then div wrapper for xhtml strictness
01255         $output = html_writer::tag('div', $output);
01256 
01257         // now the form itself around it
01258         if ($select->method === 'get') {
01259             $url = $select->url->out_omit_querystring(true); // url without params, the anchor part allowed
01260         } else {
01261             $url = $select->url->out_omit_querystring();     // url without params, the anchor part not allowed
01262         }
01263         $formattributes = array('method' => $select->method,
01264                                 'action' => $url,
01265                                 'id'     => $select->formid);
01266         $output = html_writer::tag('form', $output, $formattributes);
01267 
01268         // and finally one more wrapper with class
01269         return html_writer::tag('div', $output, array('class' => $select->class));
01270     }
01271 
01280     public function url_select(array $urls, $selected, $nothing=array(''=>'choosedots'), $formid=null) {
01281         $select = new url_select($urls, $selected, $nothing, $formid);
01282         return $this->render($select);
01283     }
01284 
01290     protected function render_url_select(url_select $select) {
01291         global $CFG;
01292 
01293         $select = clone($select);
01294         if (empty($select->formid)) {
01295             $select->formid = html_writer::random_id('url_select_f');
01296         }
01297 
01298         if (empty($select->attributes['id'])) {
01299             $select->attributes['id'] = html_writer::random_id('url_select');
01300         }
01301 
01302         if ($select->disabled) {
01303             $select->attributes['disabled'] = 'disabled';
01304         }
01305 
01306         if ($select->tooltip) {
01307             $select->attributes['title'] = $select->tooltip;
01308         }
01309 
01310         $output = '';
01311 
01312         if ($select->label) {
01313             $output .= html_writer::label($select->label, $select->attributes['id']);
01314         }
01315 
01316         if ($select->helpicon instanceof help_icon) {
01317             $output .= $this->render($select->helpicon);
01318         } else if ($select->helpicon instanceof old_help_icon) {
01319             $output .= $this->render($select->helpicon);
01320         }
01321 
01322         // For security reasons, the script course/jumpto.php requires URL starting with '/'. To keep
01323         // backward compatibility, we are removing heading $CFG->wwwroot from URLs here.
01324         $urls = array();
01325         foreach ($select->urls as $k=>$v) {
01326             if (is_array($v)) {
01327                 // optgroup structure
01328                 foreach ($v as $optgrouptitle => $optgroupoptions) {
01329                     foreach ($optgroupoptions as $optionurl => $optiontitle) {
01330                         if (empty($optionurl)) {
01331                             $safeoptionurl = '';
01332                         } else if (strpos($optionurl, $CFG->wwwroot.'/') === 0) {
01333                             // debugging('URLs passed to url_select should be in local relative form - please fix the code.', DEBUG_DEVELOPER);
01334                             $safeoptionurl = str_replace($CFG->wwwroot, '', $optionurl);
01335                         } else if (strpos($optionurl, '/') !== 0) {
01336                             debugging("Invalid url_select urls parameter inside optgroup: url '$optionurl' is not local relative url!");
01337                             continue;
01338                         } else {
01339                             $safeoptionurl = $optionurl;
01340                         }
01341                         $urls[$k][$optgrouptitle][$safeoptionurl] = $optiontitle;
01342                     }
01343                 }
01344             } else {
01345                 // plain list structure
01346                 if (empty($k)) {
01347                     // nothing selected option
01348                 } else if (strpos($k, $CFG->wwwroot.'/') === 0) {
01349                     $k = str_replace($CFG->wwwroot, '', $k);
01350                 } else if (strpos($k, '/') !== 0) {
01351                     debugging("Invalid url_select urls parameter: url '$k' is not local relative url!");
01352                     continue;
01353                 }
01354                 $urls[$k] = $v;
01355             }
01356         }
01357         $selected = $select->selected;
01358         if (!empty($selected)) {
01359             if (strpos($select->selected, $CFG->wwwroot.'/') === 0) {
01360                 $selected = str_replace($CFG->wwwroot, '', $selected);
01361             } else if (strpos($selected, '/') !== 0) {
01362                 debugging("Invalid value of parameter 'selected': url '$selected' is not local relative url!");
01363             }
01364         }
01365 
01366         $output .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'sesskey', 'value'=>sesskey()));
01367         $output .= html_writer::select($urls, 'jump', $selected, $select->nothing, $select->attributes);
01368 
01369         if (!$select->showbutton) {
01370             $go = html_writer::empty_tag('input', array('type'=>'submit', 'value'=>get_string('go')));
01371             $output .= html_writer::tag('noscript', html_writer::tag('div', $go), array('style'=>'inline'));
01372             $nothing = empty($select->nothing) ? false : key($select->nothing);
01373             $output .= $this->page->requires->js_init_call('M.util.init_url_select', array($select->formid, $select->attributes['id'], $nothing));
01374         } else {
01375             $output .= html_writer::empty_tag('input', array('type'=>'submit', 'value'=>$select->showbutton));
01376         }
01377 
01378         // then div wrapper for xhtml strictness
01379         $output = html_writer::tag('div', $output);
01380 
01381         // now the form itself around it
01382         $formattributes = array('method' => 'post',
01383                                 'action' => new moodle_url('/course/jumpto.php'),
01384                                 'id'     => $select->formid);
01385         $output = html_writer::tag('form', $output, $formattributes);
01386 
01387         // and finally one more wrapper with class
01388         return html_writer::tag('div', $output, array('class' => $select->class));
01389     }
01390 
01398     public function doc_link($path, $text = '') {
01399         global $CFG;
01400 
01401         $icon = $this->pix_icon('docs', $text, 'moodle', array('class'=>'iconhelp'));
01402 
01403         $url = new moodle_url(get_docs_url($path));
01404 
01405         $attributes = array('href'=>$url);
01406         if (!empty($CFG->doctonewwindow)) {
01407             $attributes['id'] = $this->add_action_handler(new popup_action('click', $url));
01408         }
01409 
01410         return html_writer::tag('a', $icon.$text, $attributes);
01411     }
01412 
01421     public function pix_icon($pix, $alt, $component='moodle', array $attributes = null) {
01422         $icon = new pix_icon($pix, $alt, $component, $attributes);
01423         return $this->render($icon);
01424     }
01425 
01431     protected function render_pix_icon(pix_icon $icon) {
01432         $attributes = $icon->attributes;
01433         $attributes['src'] = $this->pix_url($icon->pix, $icon->component);
01434         return html_writer::empty_tag('img', $attributes);
01435     }
01436 
01442     protected function render_pix_emoticon(pix_emoticon $emoticon) {
01443         $attributes = $emoticon->attributes;
01444         $attributes['src'] = $this->pix_url($emoticon->pix, $emoticon->component);
01445         return html_writer::empty_tag('img', $attributes);
01446     }
01447 
01452     function render_rating(rating $rating) {
01453         global $CFG, $USER;
01454 
01455         if ($rating->settings->aggregationmethod == RATING_AGGREGATE_NONE) {
01456             return null;//ratings are turned off
01457         }
01458 
01459         $ratingmanager = new rating_manager();
01460         // Initialise the JavaScript so ratings can be done by AJAX.
01461         $ratingmanager->initialise_rating_javascript($this->page);
01462 
01463         $strrate = get_string("rate", "rating");
01464         $ratinghtml = ''; //the string we'll return
01465 
01466         // permissions check - can they view the aggregate?
01467         if ($rating->user_can_view_aggregate()) {
01468 
01469             $aggregatelabel = $ratingmanager->get_aggregate_label($rating->settings->aggregationmethod);
01470             $aggregatestr   = $rating->get_aggregate_string();
01471 
01472             $aggregatehtml  = html_writer::tag('span', $aggregatestr, array('id' => 'ratingaggregate'.$rating->itemid, 'class' => 'ratingaggregate')).' ';
01473             if ($rating->count > 0) {
01474                 $countstr = "({$rating->count})";
01475             } else {
01476                 $countstr = '-';
01477             }
01478             $aggregatehtml .= html_writer::tag('span', $countstr, array('id'=>"ratingcount{$rating->itemid}", 'class' => 'ratingcount')).' ';
01479 
01480             $ratinghtml .= html_writer::tag('span', $aggregatelabel, array('class'=>'rating-aggregate-label'));
01481             if ($rating->settings->permissions->viewall && $rating->settings->pluginpermissions->viewall) {
01482 
01483                 $nonpopuplink = $rating->get_view_ratings_url();
01484                 $popuplink = $rating->get_view_ratings_url(true);
01485 
01486                 $action = new popup_action('click', $popuplink, 'ratings', array('height' => 400, 'width' => 600));
01487                 $ratinghtml .= $this->action_link($nonpopuplink, $aggregatehtml, $action);
01488             } else {
01489                 $ratinghtml .= $aggregatehtml;
01490             }
01491         }
01492 
01493         $formstart = null;
01494         // if the item doesn't belong to the current user, the user has permission to rate
01495         // and we're within the assessable period
01496         if ($rating->user_can_rate()) {
01497 
01498             $rateurl = $rating->get_rate_url();
01499             $inputs = $rateurl->params();
01500 
01501             //start the rating form
01502             $formattrs = array(
01503                 'id'     => "postrating{$rating->itemid}",
01504                 'class'  => 'postratingform',
01505                 'method' => 'post',
01506                 'action' => $rateurl->out_omit_querystring()
01507             );
01508             $formstart  = html_writer::start_tag('form', $formattrs);
01509             $formstart .= html_writer::start_tag('div', array('class' => 'ratingform'));
01510 
01511             // add the hidden inputs
01512             foreach ($inputs as $name => $value) {
01513                 $attributes = array('type' => 'hidden', 'class' => 'ratinginput', 'name' => $name, 'value' => $value);
01514                 $formstart .= html_writer::empty_tag('input', $attributes);
01515             }
01516 
01517             if (empty($ratinghtml)) {
01518                 $ratinghtml .= $strrate.': ';
01519             }
01520             $ratinghtml = $formstart.$ratinghtml;
01521 
01522             $scalearray = array(RATING_UNSET_RATING => $strrate.'...') + $rating->settings->scale->scaleitems;
01523             $scaleattrs = array('class'=>'postratingmenu ratinginput','id'=>'menurating'.$rating->itemid);
01524             $ratinghtml .= html_writer::select($scalearray, 'rating', $rating->rating, false, $scaleattrs);
01525 
01526             //output submit button
01527             $ratinghtml .= html_writer::start_tag('span', array('class'=>"ratingsubmit"));
01528 
01529             $attributes = array('type' => 'submit', 'class' => 'postratingmenusubmit', 'id' => 'postratingsubmit'.$rating->itemid, 'value' => s(get_string('rate', 'rating')));
01530             $ratinghtml .= html_writer::empty_tag('input', $attributes);
01531 
01532             if (!$rating->settings->scale->isnumeric) {
01533                 $ratinghtml .= $this->help_icon_scale($rating->settings->scale->courseid, $rating->settings->scale);
01534             }
01535             $ratinghtml .= html_writer::end_tag('span');
01536             $ratinghtml .= html_writer::end_tag('div');
01537             $ratinghtml .= html_writer::end_tag('form');
01538         }
01539 
01540         return $ratinghtml;
01541     }
01542 
01543     /*
01544      * Centered heading with attached help button (same title text)
01545      * and optional icon attached
01546      * @param string $text A heading text
01547      * @param string $helpidentifier The keyword that defines a help page
01548      * @param string $component component name
01549      * @param string|moodle_url $icon
01550      * @param string $iconalt icon alt text
01551      * @return string HTML fragment
01552      */
01553     public function heading_with_help($text, $helpidentifier, $component='moodle', $icon='', $iconalt='') {
01554         $image = '';
01555         if ($icon) {
01556             $image = $this->pix_icon($icon, $iconalt, $component, array('class'=>'icon'));
01557         }
01558 
01559         $help = '';
01560         if ($helpidentifier) {
01561             $help = $this->help_icon($helpidentifier, $component);
01562         }
01563 
01564         return $this->heading($image.$text.$help, 2, 'main help');
01565     }
01566 
01577     public function old_help_icon($helpidentifier, $title, $component = 'moodle', $linktext = '') {
01578         debugging('The method old_help_icon() is deprecated, please fix the code and use help_icon() method instead', DEBUG_DEVELOPER);
01579         $icon = new old_help_icon($helpidentifier, $title, $component);
01580         if ($linktext === true) {
01581             $icon->linktext = $title;
01582         } else if (!empty($linktext)) {
01583             $icon->linktext = $linktext;
01584         }
01585         return $this->render($icon);
01586     }
01587 
01593     protected function render_old_help_icon(old_help_icon $helpicon) {
01594         global $CFG;
01595 
01596         // first get the help image icon
01597         $src = $this->pix_url('help');
01598 
01599         if (empty($helpicon->linktext)) {
01600             $alt = $helpicon->title;
01601         } else {
01602             $alt = get_string('helpwiththis');
01603         }
01604 
01605         $attributes = array('src'=>$src, 'alt'=>$alt, 'class'=>'iconhelp');
01606         $output = html_writer::empty_tag('img', $attributes);
01607 
01608         // add the link text if given
01609         if (!empty($helpicon->linktext)) {
01610             // the spacing has to be done through CSS
01611             $output .= $helpicon->linktext;
01612         }
01613 
01614         // now create the link around it - we need https on loginhttps pages
01615         $url = new moodle_url($CFG->httpswwwroot.'/help.php', array('component' => $helpicon->component, 'identifier' => $helpicon->helpidentifier, 'lang'=>current_language()));
01616 
01617         // note: this title is displayed only if JS is disabled, otherwise the link will have the new ajax tooltip
01618         $title = get_string('helpprefix2', '', trim($helpicon->title, ". \t"));
01619 
01620         $attributes = array('href'=>$url, 'title'=>$title);
01621         $id = html_writer::random_id('helpicon');
01622         $attributes['id'] = $id;
01623         $output = html_writer::tag('a', $output, $attributes);
01624 
01625         $this->page->requires->js_init_call('M.util.help_icon.add', array(array('id'=>$id, 'url'=>$url->out(false))));
01626 
01627         // and finally span
01628         return html_writer::tag('span', $output, array('class' => 'helplink'));
01629     }
01630 
01639     public function help_icon($identifier, $component = 'moodle', $linktext = '') {
01640         $icon = new help_icon($identifier, $component);
01641         $icon->diag_strings();
01642         if ($linktext === true) {
01643             $icon->linktext = get_string($icon->identifier, $icon->component);
01644         } else if (!empty($linktext)) {
01645             $icon->linktext = $linktext;
01646         }
01647         return $this->render($icon);
01648     }
01649 
01655     protected function render_help_icon(help_icon $helpicon) {
01656         global $CFG;
01657 
01658         // first get the help image icon
01659         $src = $this->pix_url('help');
01660 
01661         $title = get_string($helpicon->identifier, $helpicon->component);
01662 
01663         if (empty($helpicon->linktext)) {
01664             $alt = get_string('helpprefix2', '', trim($title, ". \t"));
01665         } else {
01666             $alt = get_string('helpwiththis');
01667         }
01668 
01669         $attributes = array('src'=>$src, 'alt'=>$alt, 'class'=>'iconhelp');
01670         $output = html_writer::empty_tag('img', $attributes);
01671 
01672         // add the link text if given
01673         if (!empty($helpicon->linktext)) {
01674             // the spacing has to be done through CSS
01675             $output .= $helpicon->linktext;
01676         }
01677 
01678         // now create the link around it - we need https on loginhttps pages
01679         $url = new moodle_url($CFG->httpswwwroot.'/help.php', array('component' => $helpicon->component, 'identifier' => $helpicon->identifier, 'lang'=>current_language()));
01680 
01681         // note: this title is displayed only if JS is disabled, otherwise the link will have the new ajax tooltip
01682         $title = get_string('helpprefix2', '', trim($title, ". \t"));
01683 
01684         $attributes = array('href'=>$url, 'title'=>$title);
01685         $id = html_writer::random_id('helpicon');
01686         $attributes['id'] = $id;
01687         $output = html_writer::tag('a', $output, $attributes);
01688 
01689         $this->page->requires->js_init_call('M.util.help_icon.add', array(array('id'=>$id, 'url'=>$url->out(false))));
01690 
01691         // and finally span
01692         return html_writer::tag('span', $output, array('class' => 'helplink'));
01693     }
01694 
01702     public function help_icon_scale($courseid, stdClass $scale) {
01703         global $CFG;
01704 
01705         $title = get_string('helpprefix2', '', $scale->name) .' ('.get_string('newwindow').')';
01706 
01707         $icon = $this->pix_icon('help', get_string('scales'), 'moodle', array('class'=>'iconhelp'));
01708 
01709         $scaleid = abs($scale->id);
01710 
01711         $link = new moodle_url('/course/scales.php', array('id' => $courseid, 'list' => true, 'scaleid' => $scaleid));
01712         $action = new popup_action('click', $link, 'ratingscale');
01713 
01714         return html_writer::tag('span', $this->action_link($link, $icon, $action), array('class' => 'helplink'));
01715     }
01716 
01724     public function spacer(array $attributes = null, $br = false) {
01725         $attributes = (array)$attributes;
01726         if (empty($attributes['width'])) {
01727             $attributes['width'] = 1;
01728         }
01729         if (empty($attributes['height'])) {
01730             $attributes['height'] = 1;
01731         }
01732         $attributes['class'] = 'spacer';
01733 
01734         $output = $this->pix_icon('spacer', '', 'moodle', $attributes);
01735 
01736         if (!empty($br)) {
01737             $output .= '<br />';
01738         }
01739 
01740         return $output;
01741     }
01742 
01772     public function user_picture(stdClass $user, array $options = null) {
01773         $userpicture = new user_picture($user);
01774         foreach ((array)$options as $key=>$value) {
01775             if (array_key_exists($key, $userpicture)) {
01776                 $userpicture->$key = $value;
01777             }
01778         }
01779         return $this->render($userpicture);
01780     }
01781 
01787     protected function render_user_picture(user_picture $userpicture) {
01788         global $CFG, $DB;
01789 
01790         $user = $userpicture->user;
01791 
01792         if ($userpicture->alttext) {
01793             if (!empty($user->imagealt)) {
01794                 $alt = $user->imagealt;
01795             } else {
01796                 $alt = get_string('pictureof', '', fullname($user));
01797             }
01798         } else {
01799             $alt = '';
01800         }
01801 
01802         if (empty($userpicture->size)) {
01803             $size = 35;
01804         } else if ($userpicture->size === true or $userpicture->size == 1) {
01805             $size = 100;
01806         } else {
01807             $size = $userpicture->size;
01808         }
01809 
01810         $class = $userpicture->class;
01811 
01812         if ($user->picture != 1 && $user->picture != 2) {
01813             $class .= ' defaultuserpic';
01814         }
01815 
01816         $src = $userpicture->get_url($this->page, $this);
01817 
01818         $attributes = array('src'=>$src, 'alt'=>$alt, 'title'=>$alt, 'class'=>$class, 'width'=>$size, 'height'=>$size);
01819 
01820         // get the image html output fisrt
01821         $output = html_writer::empty_tag('img', $attributes);;
01822 
01823         // then wrap it in link if needed
01824         if (!$userpicture->link) {
01825             return $output;
01826         }
01827 
01828         if (empty($userpicture->courseid)) {
01829             $courseid = $this->page->course->id;
01830         } else {
01831             $courseid = $userpicture->courseid;
01832         }
01833 
01834         if ($courseid == SITEID) {
01835             $url = new moodle_url('/user/profile.php', array('id' => $user->id));
01836         } else {
01837             $url = new moodle_url('/user/view.php', array('id' => $user->id, 'course' => $courseid));
01838         }
01839 
01840         $attributes = array('href'=>$url);
01841 
01842         if ($userpicture->popup) {
01843             $id = html_writer::random_id('userpicture');
01844             $attributes['id'] = $id;
01845             $this->add_action_handler(new popup_action('click', $url), $id);
01846         }
01847 
01848         return html_writer::tag('a', $output, $attributes);
01849     }
01850 
01856     public function htmllize_file_tree($dir) {
01857         if (empty($dir['subdirs']) and empty($dir['files'])) {
01858             return '';
01859         }
01860         $result = '<ul>';
01861         foreach ($dir['subdirs'] as $subdir) {
01862             $result .= '<li>'.s($subdir['dirname']).' '.$this->htmllize_file_tree($subdir).'</li>';
01863         }
01864         foreach ($dir['files'] as $file) {
01865             $filename = $file->get_filename();
01866             $result .= '<li><span>'.html_writer::link($file->fileurl, $filename).'</span></li>';
01867         }
01868         $result .= '</ul>';
01869 
01870         return $result;
01871     }
01889     public function file_picker($options) {
01890         $fp = new file_picker($options);
01891         return $this->render($fp);
01892     }
01898     public function render_file_picker(file_picker $fp) {
01899         global $CFG, $OUTPUT, $USER;
01900         $options = $fp->options;
01901         $client_id = $options->client_id;
01902         $strsaved = get_string('filesaved', 'repository');
01903         $straddfile = get_string('openpicker', 'repository');
01904         $strloading  = get_string('loading', 'repository');
01905         $icon_progress = $OUTPUT->pix_icon('i/loading_small', $strloading).'';
01906 
01907         $currentfile = $options->currentfile;
01908         if (empty($currentfile)) {
01909             $currentfile = get_string('nofilesattached', 'repository');
01910         }
01911         if ($options->maxbytes) {
01912             $size = $options->maxbytes;
01913         } else {
01914             $size = get_max_upload_file_size();
01915         }
01916         if ($size == -1) {
01917             $maxsize = '';
01918         } else {
01919             $maxsize = get_string('maxfilesize', 'moodle', display_size($size));
01920         }
01921         if ($options->buttonname) {
01922             $buttonname = ' name="' . $options->buttonname . '"';
01923         } else {
01924             $buttonname = '';
01925         }
01926         $html = <<<EOD
01927 <div class="filemanager-loading mdl-align" id='filepicker-loading-{$client_id}'>
01928 $icon_progress
01929 </div>
01930 <div id="filepicker-wrapper-{$client_id}" class="mdl-left" style="display:none">
01931     <div>
01932         <input type="button" id="filepicker-button-{$client_id}" value="{$straddfile}"{$buttonname}/>
01933         <span> $maxsize </span>
01934     </div>
01935 EOD;
01936         if ($options->env != 'url') {
01937             $html .= <<<EOD
01938     <div id="file_info_{$client_id}" class="mdl-left filepicker-filelist">$currentfile</div>
01939 EOD;
01940         }
01941         $html .= '</div>';
01942         return $html;
01943     }
01944 
01952     public function update_module_button($cmid, $modulename) {
01953         global $CFG;
01954         if (has_capability('moodle/course:manageactivities', get_context_instance(CONTEXT_MODULE, $cmid))) {
01955             $modulename = get_string('modulename', $modulename);
01956             $string = get_string('updatethis', '', $modulename);
01957             $url = new moodle_url("$CFG->wwwroot/course/mod.php", array('update' => $cmid, 'return' => true, 'sesskey' => sesskey()));
01958             return $this->single_button($url, $string);
01959         } else {
01960             return '';
01961         }
01962     }
01963 
01969     public function edit_button(moodle_url $url) {
01970 
01971         $url->param('sesskey', sesskey());
01972         if ($this->page->user_is_editing()) {
01973             $url->param('edit', 'off');
01974             $editstring = get_string('turneditingoff');
01975         } else {
01976             $url->param('edit', 'on');
01977             $editstring = get_string('turneditingon');
01978         }
01979 
01980         return $this->single_button($url, $editstring);
01981     }
01982 
01989     public function close_window_button($text='') {
01990         if (empty($text)) {
01991             $text = get_string('closewindow');
01992         }
01993         $button = new single_button(new moodle_url('#'), $text, 'get');
01994         $button->add_action(new component_action('click', 'close_window'));
01995 
01996         return $this->container($this->render($button), 'closewindow');
01997     }
01998 
02005     public function error_text($message) {
02006         if (empty($message)) {
02007             return '';
02008         }
02009         return html_writer::tag('span', $message, array('class' => 'error'));
02010     }
02011 
02026     public function fatal_error($message, $moreinfourl, $link, $backtrace, $debuginfo = null) {
02027         global $CFG;
02028 
02029         $output = '';
02030         $obbuffer = '';
02031 
02032         if ($this->has_started()) {
02033             // we can not always recover properly here, we have problems with output buffering,
02034             // html tables, etc.
02035             $output .= $this->opencontainers->pop_all_but_last();
02036 
02037         } else {
02038             // It is really bad if library code throws exception when output buffering is on,
02039             // because the buffered text would be printed before our start of page.
02040             // NOTE: this hack might be behave unexpectedly in case output buffering is enabled in PHP.ini
02041             error_reporting(0); // disable notices from gzip compression, etc.
02042             while (ob_get_level() > 0) {
02043                 $buff = ob_get_clean();
02044                 if ($buff === false) {
02045                     break;
02046                 }
02047                 $obbuffer .= $buff;
02048             }
02049             error_reporting($CFG->debug);
02050 
02051             // Header not yet printed
02052             if (isset($_SERVER['SERVER_PROTOCOL'])) {
02053                 // server protocol should be always present, because this render
02054                 // can not be used from command line or when outputting custom XML
02055                 @header($_SERVER['SERVER_PROTOCOL'] . ' 404 Not Found');
02056             }
02057             $this->page->set_context(null); // ugly hack - make sure page context is set to something, we do not want bogus warnings here
02058             $this->page->set_url('/'); // no url
02059             //$this->page->set_pagelayout('base'); //TODO: MDL-20676 blocks on error pages are weird, unfortunately it somehow detect the pagelayout from URL :-(
02060             $this->page->set_title(get_string('error'));
02061             $this->page->set_heading($this->page->course->fullname);
02062             $output .= $this->header();
02063         }
02064 
02065         $message = '<p class="errormessage">' . $message . '</p>'.
02066                 '<p class="errorcode"><a href="' . $moreinfourl . '">' .
02067                 get_string('moreinformation') . '</a></p>';
02068         if (empty($CFG->rolesactive)) {
02069             $message .= '<p class="errormessage">' . get_string('installproblem', 'error') . '</p>';
02070             //It is usually not possible to recover from errors triggered during installation, you may need to create a new database or use a different database prefix for new installation.
02071         }
02072         $output .= $this->box($message, 'errorbox');
02073 
02074         if (debugging('', DEBUG_DEVELOPER)) {
02075             if (!empty($debuginfo)) {
02076                 $debuginfo = s($debuginfo); // removes all nasty JS
02077                 $debuginfo = str_replace("\n", '<br />', $debuginfo); // keep newlines
02078                 $output .= $this->notification('<strong>Debug info:</strong> '.$debuginfo, 'notifytiny');
02079             }
02080             if (!empty($backtrace)) {
02081                 $output .= $this->notification('<strong>Stack trace:</strong> '.format_backtrace($backtrace), 'notifytiny');
02082             }
02083             if ($obbuffer !== '' ) {
02084                 $output .= $this->notification('<strong>Output buffer:</strong> '.s($obbuffer), 'notifytiny');
02085             }
02086         }
02087 
02088         if (empty($CFG->rolesactive)) {
02089             // continue does not make much sense if moodle is not installed yet because error is most probably not recoverable
02090         } else if (!empty($link)) {
02091             $output .= $this->continue_button($link);
02092         }
02093 
02094         $output .= $this->footer();
02095 
02096         // Padding to encourage IE to display our error page, rather than its own.
02097         $output .= str_repeat(' ', 512);
02098 
02099         return $output;
02100     }
02101 
02110     public function notification($message, $classes = 'notifyproblem') {
02111         return html_writer::tag('div', clean_text($message), array('class' => renderer_base::prepare_classes($classes)));
02112     }
02113 
02120     public function continue_button($url) {
02121         if (!($url instanceof moodle_url)) {
02122             $url = new moodle_url($url);
02123         }
02124         $button = new single_button($url, get_string('continue'), 'get');
02125         $button->class = 'continuebutton';
02126 
02127         return $this->render($button);
02128     }
02129 
02140     public function paging_bar($totalcount, $page, $perpage, $baseurl, $pagevar = 'page') {
02141         $pb = new paging_bar($totalcount, $page, $perpage, $baseurl, $pagevar);
02142         return $this->render($pb);
02143     }
02144 
02150     protected function render_paging_bar(paging_bar $pagingbar) {
02151         $output = '';
02152         $pagingbar = clone($pagingbar);
02153         $pagingbar->prepare($this, $this->page, $this->target);
02154 
02155         if ($pagingbar->totalcount > $pagingbar->perpage) {
02156             $output .= get_string('page') . ':';
02157 
02158             if (!empty($pagingbar->previouslink)) {
02159                 $output .= '&#160;(' . $pagingbar->previouslink . ')&#160;';
02160             }
02161 
02162             if (!empty($pagingbar->firstlink)) {
02163                 $output .= '&#160;' . $pagingbar->firstlink . '&#160;...';
02164             }
02165 
02166             foreach ($pagingbar->pagelinks as $link) {
02167                 $output .= "&#160;&#160;$link";
02168             }
02169 
02170             if (!empty($pagingbar->lastlink)) {
02171                 $output .= '&#160;...' . $pagingbar->lastlink . '&#160;';
02172             }
02173 
02174             if (!empty($pagingbar->nextlink)) {
02175                 $output .= '&#160;&#160;(' . $pagingbar->nextlink . ')';
02176             }
02177         }
02178 
02179         return html_writer::tag('div', $output, array('class' => 'paging'));
02180     }
02181 
02187     public function skip_link_target($id = null) {
02188         return html_writer::tag('span', '', array('id' => $id));
02189     }
02190 
02199     public function heading($text, $level = 2, $classes = 'main', $id = null) {
02200         $level = (integer) $level;
02201         if ($level < 1 or $level > 6) {
02202             throw new coding_exception('Heading level must be an integer between 1 and 6.');
02203         }
02204         return html_writer::tag('h' . $level, $text, array('id' => $id, 'class' => renderer_base::prepare_classes($classes)));
02205     }
02206 
02214     public function box($contents, $classes = 'generalbox', $id = null) {
02215         return $this->box_start($classes, $id) . $contents . $this->box_end();
02216     }
02217 
02224     public function box_start($classes = 'generalbox', $id = null) {
02225         $this->opencontainers->push('box', html_writer::end_tag('div'));
02226         return html_writer::start_tag('div', array('id' => $id,
02227                 'class' => 'box ' . renderer_base::prepare_classes($classes)));
02228     }
02229 
02234     public function box_end() {
02235         return $this->opencontainers->pop('box');
02236     }
02237 
02245     public function container($contents, $classes = null, $id = null) {
02246         return $this->container_start($classes, $id) . $contents . $this->container_end();
02247     }
02248 
02255     public function container_start($classes = null, $id = null) {
02256         $this->opencontainers->push('container', html_writer::end_tag('div'));
02257         return html_writer::start_tag('div', array('id' => $id,
02258                 'class' => renderer_base::prepare_classes($classes)));
02259     }
02260 
02265     public function container_end() {
02266         return $this->opencontainers->pop('container');
02267     }
02268 
02289     function tree_block_contents($items, $attrs=array()) {
02290         // exit if empty, we don't want an empty ul element
02291         if (empty($items)) {
02292             return '';
02293         }
02294         // array of nested li elements
02295         $lis = array();
02296         foreach ($items as $item) {
02297             // this applies to the li item which contains all child lists too
02298             $content = $item->content($this);
02299             $liclasses = array($item->get_css_type());
02300             if (!$item->forceopen || (!$item->forceopen && $item->collapse) || ($item->children->count()==0  && $item->nodetype==navigation_node::NODETYPE_BRANCH)) {
02301                 $liclasses[] = 'collapsed';
02302             }
02303             if ($item->isactive === true) {
02304                 $liclasses[] = 'current_branch';
02305             }
02306             $liattr = array('class'=>join(' ',$liclasses));
02307             // class attribute on the div item which only contains the item content
02308             $divclasses = array('tree_item');
02309             if ($item->children->count()>0  || $item->nodetype==navigation_node::NODETYPE_BRANCH) {
02310                 $divclasses[] = 'branch';
02311             } else {
02312                 $divclasses[] = 'leaf';
02313             }
02314             if (!empty($item->classes) && count($item->classes)>0) {
02315                 $divclasses[] = join(' ', $item->classes);
02316             }
02317             $divattr = array('class'=>join(' ', $divclasses));
02318             if (!empty($item->id)) {
02319                 $divattr['id'] = $item->id;
02320             }
02321             $content = html_writer::tag('p', $content, $divattr) . $this->tree_block_contents($item->children);
02322             if (!empty($item->preceedwithhr) && $item->preceedwithhr===true) {
02323                 $content = html_writer::empty_tag('hr') . $content;
02324             }
02325             $content = html_writer::tag('li', $content, $liattr);
02326             $lis[] = $content;
02327         }
02328         return html_writer::tag('ul', implode("\n", $lis), $attrs);
02329     }
02330 
02335     public function navbar() {
02336         $items = $this->page->navbar->get_items();
02337 
02338         $htmlblocks = array();
02339         // Iterate the navarray and display each node
02340         $itemcount = count($items);
02341         $separator = get_separator();
02342         for ($i=0;$i < $itemcount;$i++) {
02343             $item = $items[$i];
02344             $item->hideicon = true;
02345             if ($i===0) {
02346                 $content = html_writer::tag('li', $this->render($item));
02347             } else {
02348                 $content = html_writer::tag('li', $separator.$this->render($item));
02349             }
02350             $htmlblocks[] = $content;
02351         }
02352 
02353         //accessibility: heading for navbar list  (MDL-20446)
02354         $navbarcontent = html_writer::tag('span', get_string('pagepath'), array('class'=>'accesshide'));
02355         $navbarcontent .= html_writer::tag('ul', join('', $htmlblocks));
02356         // XHTML
02357         return $navbarcontent;
02358     }
02359 
02360     protected function render_navigation_node(navigation_node $item) {
02361         $content = $item->get_content();
02362         $title = $item->get_title();
02363         if ($item->icon instanceof renderable && !$item->hideicon) {
02364             $icon = $this->render($item->icon);
02365             $content = $icon.$content; // use CSS for spacing of icons
02366         }
02367         if ($item->helpbutton !== null) {
02368             $content = trim($item->helpbutton).html_writer::tag('span', $content, array('class'=>'clearhelpbutton', 'tabindex'=>'0'));
02369         }
02370         if ($content === '') {
02371             return '';
02372         }
02373         if ($item->action instanceof action_link) {
02374             $link = $item->action;
02375             if ($item->hidden) {
02376                 $link->add_class('dimmed');
02377             }
02378             $link->text = $content.$link->text; // add help icon
02379             $content = $this->render($link);
02380         } else if ($item->action instanceof moodle_url) {
02381             $attributes = array();
02382             if ($title !== '') {
02383                 $attributes['title'] = $title;
02384             }
02385             if ($item->hidden) {
02386                 $attributes['class'] = 'dimmed_text';
02387             }
02388             $content = html_writer::link($item->action, $content, $attributes);
02389 
02390         } else if (is_string($item->action) || empty($item->action)) {
02391             $attributes = array('tabindex'=>'0'); //add tab support to span but still maintain character stream sequence.
02392             if ($title !== '') {
02393                 $attributes['title'] = $title;
02394             }
02395             if ($item->hidden) {
02396                 $attributes['class'] = 'dimmed_text';
02397             }
02398             $content = html_writer::tag('span', $content, $attributes);
02399         }
02400         return $content;
02401     }
02402 
02412     public function rarrow() {
02413         return $this->page->theme->rarrow;
02414     }
02415 
02425     public function larrow() {
02426         return $this->page->theme->larrow;
02427     }
02428 
02438     public function custom_menu() {
02439         global $CFG;
02440         if (empty($CFG->custommenuitems)) {
02441             return '';
02442         }
02443         $custommenu = new custom_menu($CFG->custommenuitems, current_language());
02444         return $this->render_custom_menu($custommenu);
02445     }
02446 
02457     protected function render_custom_menu(custom_menu $menu) {
02458         static $menucount = 0;
02459         // If the menu has no children return an empty string
02460         if (!$menu->has_children()) {
02461             return '';
02462         }
02463         // Increment the menu count. This is used for ID's that get worked with
02464         // in JavaScript as is essential
02465         $menucount++;
02466         // Initialise this custom menu (the custom menu object is contained in javascript-static
02467         $jscode = js_writer::function_call_with_Y('M.core_custom_menu.init', array('custom_menu_'.$menucount));
02468         $jscode = "(function(){{$jscode}})";
02469         $this->page->requires->yui_module('node-menunav', $jscode);
02470         // Build the root nodes as required by YUI
02471         $content = html_writer::start_tag('div', array('id'=>'custom_menu_'.$menucount, 'class'=>'yui3-menu yui3-menu-horizontal javascript-disabled'));
02472         $content .= html_writer::start_tag('div', array('class'=>'yui3-menu-content'));
02473         $content .= html_writer::start_tag('ul');
02474         // Render each child
02475         foreach ($menu->get_children() as $item) {
02476             $content .= $this->render_custom_menu_item($item);
02477         }
02478         // Close the open tags
02479         $content .= html_writer::end_tag('ul');
02480         $content .= html_writer::end_tag('div');
02481         $content .= html_writer::end_tag('div');
02482         // Return the custom menu
02483         return $content;
02484     }
02485 
02498     protected function render_custom_menu_item(custom_menu_item $menunode) {
02499         // Required to ensure we get unique trackable id's
02500         static $submenucount = 0;
02501         if ($menunode->has_children()) {
02502             // If the child has menus render it as a sub menu
02503             $submenucount++;
02504             $content = html_writer::start_tag('li');
02505             if ($menunode->get_url() !== null) {
02506                 $url = $menunode->get_url();
02507             } else {
02508                 $url = '#cm_submenu_'.$submenucount;
02509             }
02510             $content .= html_writer::link($url, $menunode->get_text(), array('class'=>'yui3-menu-label', 'title'=>$menunode->get_title()));
02511             $content .= html_writer::start_tag('div', array('id'=>'cm_submenu_'.$submenucount, 'class'=>'yui3-menu custom_menu_submenu'));
02512             $content .= html_writer::start_tag('div', array('class'=>'yui3-menu-content'));
02513             $content .= html_writer::start_tag('ul');
02514             foreach ($menunode->get_children() as $menunode) {
02515                 $content .= $this->render_custom_menu_item($menunode);
02516             }
02517             $content .= html_writer::end_tag('ul');
02518             $content .= html_writer::end_tag('div');
02519             $content .= html_writer::end_tag('div');
02520             $content .= html_writer::end_tag('li');
02521         } else {
02522             // The node doesn't have children so produce a final menuitem
02523             $content = html_writer::start_tag('li', array('class'=>'yui3-menuitem'));
02524             if ($menunode->get_url() !== null) {
02525                 $url = $menunode->get_url();
02526             } else {
02527                 $url = '#';
02528             }
02529             $content .= html_writer::link($url, $menunode->get_text(), array('class'=>'yui3-menuitem-content', 'title'=>$menunode->get_title()));
02530             $content .= html_writer::end_tag('li');
02531         }
02532         // Return the sub menu
02533         return $content;
02534     }
02535 
02541     protected function theme_switch_links() {
02542 
02543         $actualdevice = get_device_type();
02544         $currentdevice = $this->page->devicetypeinuse;
02545         $switched = ($actualdevice != $currentdevice);
02546 
02547         if (!$switched && $currentdevice == 'default' && $actualdevice == 'default') {
02548             // The user is using the a default device and hasn't switched so don't shown the switch
02549             // device links.
02550             return '';
02551         }
02552 
02553         if ($switched) {
02554             $linktext = get_string('switchdevicerecommended');
02555             $devicetype = $actualdevice;
02556         } else {
02557             $linktext = get_string('switchdevicedefault');
02558             $devicetype = 'default';
02559         }
02560         $linkurl = new moodle_url('/theme/switchdevice.php', array('url' => $this->page->url, 'device' => $devicetype, 'sesskey' => sesskey()));
02561 
02562         $content  = html_writer::start_tag('div', array('id' => 'theme_switch_link'));
02563         $content .= html_writer::link($linkurl, $linktext);
02564         $content .= html_writer::end_tag('div');
02565 
02566         return $content;
02567     }
02568 }
02569 
02571 
02581 class core_renderer_cli extends core_renderer {
02586     public function header() {
02587         return $this->page->heading . "\n";
02588     }
02589 
02598     public function heading($text, $level = 2, $classes = 'main', $id = null) {
02599         $text .= "\n";
02600         switch ($level) {
02601             case 1:
02602                 return '=>' . $text;
02603             case 2:
02604                 return '-->' . $text;
02605             default:
02606                 return $text;
02607         }
02608     }
02609 
02619     public function fatal_error($message, $moreinfourl, $link, $backtrace, $debuginfo = null) {
02620         $output = "!!! $message !!!\n";
02621 
02622         if (debugging('', DEBUG_DEVELOPER)) {
02623             if (!empty($debuginfo)) {
02624                 $output .= $this->notification($debuginfo, 'notifytiny');
02625             }
02626             if (!empty($backtrace)) {
02627                 $output .= $this->notification('Stack trace: ' . format_backtrace($backtrace, true), 'notifytiny');
02628             }
02629         }
02630 
02631         return $output;
02632     }
02633 
02640     public function notification($message, $classes = 'notifyproblem') {
02641         $message = clean_text($message);
02642         if ($classes === 'notifysuccess') {
02643             return "++ $message ++\n";
02644         }
02645         return "!! $message !!\n";
02646     }
02647 }
02648 
02649 
02660 class core_renderer_ajax extends core_renderer {
02670     public function fatal_error($message, $moreinfourl, $link, $backtrace, $debuginfo = null) {
02671         global $CFG;
02672 
02673         $this->page->set_context(null); // ugly hack - make sure page context is set to something, we do not want bogus warnings here
02674 
02675         $e = new stdClass();
02676         $e->error      = $message;
02677         $e->stacktrace = NULL;
02678         $e->debuginfo  = NULL;
02679         $e->reproductionlink = NULL;
02680         if (!empty($CFG->debug) and $CFG->debug >= DEBUG_DEVELOPER) {
02681             $e->reproductionlink = $link;
02682             if (!empty($debuginfo)) {
02683                 $e->debuginfo = $debuginfo;
02684             }
02685             if (!empty($backtrace)) {
02686                 $e->stacktrace = format_backtrace($backtrace, true);
02687             }
02688         }
02689         $this->header();
02690         return json_encode($e);
02691     }
02692 
02693     public function notification($message, $classes = 'notifyproblem') {
02694     }
02695 
02696     public function redirect_message($encodedurl, $message, $delay, $debugdisableredirect) {
02697     }
02698 
02699     public function header() {
02700         // unfortunately YUI iframe upload does not support application/json
02701         if (!empty($_FILES)) {
02702             @header('Content-type: text/plain; charset=utf-8');
02703         } else {
02704             @header('Content-type: application/json; charset=utf-8');
02705         }
02706 
02708         @header('Cache-Control: no-store, no-cache, must-revalidate');
02709         @header('Cache-Control: post-check=0, pre-check=0', false);
02710         @header('Pragma: no-cache');
02711         @header('Expires: Mon, 20 Aug 1969 09:23:00 GMT');
02712         @header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
02713         @header('Accept-Ranges: none');
02714     }
02715 
02716     public function footer() {
02717     }
02718 
02719     public function heading($text, $level = 2, $classes = 'main', $id = null) {
02720     }
02721 }
02722 
 All Data Structures Namespaces Files Functions Variables Enumerations