moodle_page: MDL-12212 reimplement user_is_editing, deprecate isediting
[moodle.git] / lib / pagelib.php
1 <?php // $Id$
3 ///////////////////////////////////////////////////////////////////////////
4 //                                                                       //
5 // NOTICE OF COPYRIGHT                                                   //
6 //                                                                       //
7 // Moodle - Modular Object-Oriented Dynamic Learning Environment         //
8 //          http://moodle.org                                            //
9 //                                                                       //
10 // Copyright (C) 1999 onwards Martin Dougiamas  http://dougiamas.com     //
11 //                                                                       //
12 // This program is free software; you can redistribute it and/or modify  //
13 // it under the terms of the GNU General Public License as published by  //
14 // the Free Software Foundation; either version 2 of the License, or     //
15 // (at your option) any later version.                                   //
16 //                                                                       //
17 // This program is distributed in the hope that it will be useful,       //
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of        //
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         //
20 // GNU General Public License for more details:                          //
21 //                                                                       //
22 //          http://www.gnu.org/copyleft/gpl.html                         //
23 //                                                                       //
24 ///////////////////////////////////////////////////////////////////////////
26 /**
27  * This file contains the moodle_page class. There is normally a single instance
28  * of this class in the $PAGE global variable. This class is a central reporitory
29  * of information about the page we are building up to send back to the user.
30  *
31  * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
32  * @package pages
33  */
35 /**
36  * $PAGE is a central store of information about the current page we are
37  * generating in response to the user's request. It does not do very much itself
38  * except keep track of information, however, it serves as the access point to
39  * some more significant components like $PAGE->theme, $PAGE->requires,
40  * $PAGE->blocks, etc.
41  */
42 class moodle_page {
43     /**#@+ Tracks the where we are in the generation of the page. */
44     const STATE_BEFORE_HEADER = 0;
45     const STATE_PRINTING_HEADER = 1;
46     const STATE_IN_BODY = 2;
47     const STATE_PRINTING_FOOTER = 3;
48     const STATE_DONE = 4;
49     /**#@-*/
51 /// Field declarations =========================================================
53     protected $_state = self::STATE_BEFORE_HEADER;
55     protected $_course = null;
57     protected $_context = null;
59     /**
60      * This holds any categories that $_course belongs to, starting with the
61      * particular category it belongs to, and working out through any parent
62      * categories to the top level. These are loaded progressively, if neaded.
63      * There are three states. $_categories = null initially when nothing is
64      * loaded; $_categories = array($id => $cat, $parentid => null) when we have
65      * loaded $_course->category, but not any parents; and a complete array once
66      * everything is loaded.
67      */
68     protected $_categories = null;
70     protected $_bodyclasses = array();
72     protected $_pagetype = null;
74     protected $_docspath = null;
76     protected $_legacyclass = null;
78     protected $_url = null;
80     protected $_blocks = null;
82 /// Getter methods =============================================================
83 /// Due to the __get magic below, you normally do not call these as $PAGE->get_x
84 /// methods, but instead use the $PAGE->x syntax.
86     /**
87      * @return integer one of the STATE_... constants. You should not normally need
88      * to use this in your code. It is indended for internal use by this class
89      * and its friends like print_header, to check that everything is working as
90      * expected. Also accessible as $PAGE->state.
91      */
92     public function get_state() {
93         return $this->_state;
94     }
96     /**
97      * @return boolean has the header already been printed? Also accessible as
98      * $PAGE->headerprinted.
99      */
100     public function get_headerprinted() {
101         return $this->_state >= self::STATE_IN_BODY;
102     }
104     /**
105      * @return object the current course that we are inside - a row from the
106      * course table. (Also available as $COURSE global.) If we are not inside
107      * an actual course, this will be the site course. You can also access this
108      * as $PAGE->course.
109      */
110     public function get_course() {
111         global $SITE;
112         if (is_null($this->_course)) {
113             return $SITE;
114         }
115         return $this->_course;
116     }
118     /**
119      * @return mixed the category that the page course belongs to. If there isn't one
120      * (that is, if this is the front page course) returns null.
121      */
122     public function get_category() {
123         $this->ensure_category_loaded();
124         if (!empty($this->_categories)) {
125             return reset($this->_categories);
126         } else {
127             return null;
128         }
129     }
131     /**
132      * @return array an array of all the categories the page course belongs to,
133      * starting with the immediately containing category, and working out to
134      * the top-level category. This may be the empty array if we are in the
135      * front page course.
136      */
137     public function get_categories() {
138         $this->ensure_categories_loaded();
139         return $this->_categories;
140     }
142     /**
143      * @return object the main context to which this page belongs.
144      */
145     public function get_context() {
146         if (is_null($this->_context)) {
147             throw new coding_exception('$PAGE->context accessed before it was known.');
148         }
149         return $this->_context;
150     }
152     /**
153      * @return string e.g. 'my-index' or 'mod-quiz-attempt'. Same as the id attribute on <body>.
154      */
155     public function get_pagetype() {
156         if (is_null($this->_pagetype) || isset($CFG->pagepath)) {
157             $this->initialise_default_pagetype();
158         }
159         return $this->_pagetype;
160     }
162     /**
163      * @return string the class names to put on the body element in the HTML.
164      */
165     public function get_bodyclasses() {
166         return implode(' ', array_keys($this->_bodyclasses));
167     }
169     /**
170      * @return string the class names to put on the body element in the HTML.
171      */
172     public function get_docspath() {
173         if (is_string($this->_docspath)) {
174             return $this->_docspath;
175         } else {
176             return str_replace('-', '/', $this->pagetype);
177         }
178     }
180     /**
181      * @return moodle_url the clean URL required to load the current page. (You
182      * should normally use this in preference to $ME or $FULLME.)
183      */
184     public function get_url() {
185         if (is_null($this->_url)) {
186             debugging('This page did no call $PAGE->set_url(...). Realying on a guess.', DEBUG_DEVELOPER);
187             return new moodle_url($ME);
188         }
189         return new moodle_url($this->_url); // Return a clone for safety.
190     }
192     /**
193      * @return blocks_manager the blocks manager object for this page.
194      */
195     public function get_blocks() {
196         if (is_null($this->_blocks)) {
197             $this->_blocks = new blocks_manager();
198         }
199         return $this->_blocks;
200     }
202     /**
203      * PHP overloading magic to make the $PAGE->course syntax work.
204      */
205     public function __get($field) {
206         $getmethod = 'get_' . $field;
207         if (method_exists($this, $getmethod)) {
208             return $this->$getmethod();
209         } else {
210             throw new coding_exception('Unknown field ' . $field . ' of $PAGE.');
211         }
212     }
214 /// Other information getting methods ==========================================
216     /**
217      * @return boolean should the current user see this page in editing mode.
218      * That is, are they allowed to edit this page, and are they currently in
219      * editing mode.
220      */
221     public function user_is_editing() {
222         global $USER;
223         return !empty($USER->editing) && $this->user_allowed_editing();
224     }
226     /**
227      * @return boolean does the user have permission to see this page in editing mode.
228      */
229     public function user_allowed_editing() {
230         return true; // TODO
231     }
233 /// Setter methods =============================================================
235     /**
236      * Set the state. The state must be one of that STATE_... constants, and
237      * the state is only allowed to advance one step at a time.
238      * @param integer $state the new state.
239      */
240     public function set_state($state) {
241         if ($state != $this->_state + 1 || $state > self::STATE_DONE) {
242             throw new coding_exception('Invalid state passed to moodle_page::set_state. We are in state ' .
243                     $this->_state . ' and state ' . $state . ' was requestsed.');
244         }
246         if ($state == self::STATE_PRINTING_HEADER) {
247             if (!$this->_course) {
248                 global $SITE;
249                 $this->set_course($SITE);
250             }
252             $this->initialise_standard_body_classes();
253         }
255         $this->_state = $state;
256     }
258     /**
259      * Set the current course. This sets both $PAGE->course and $COURSE. It also
260      * sets the right theme and locale.
261      *
262      * Normally you don't need to call this function yourself, require_login will
263      * call it for you if you pass a $course to it. You can use this function
264      * on pages that do need to call require_login().
265      *
266      * Sets $PAGE->context to the course context, if it is not already set.
267      *
268      * @param object the course to set as the global course.
269      */
270     public function set_course($course) {
271         global $COURSE;
273         if (empty($course->id)) {
274             throw new coding_exception('$course passed to moodle_page::set_course does not look like a proper course object.');
275         }
277         if ($this->_state > self::STATE_BEFORE_HEADER) {
278             throw new coding_exception('Cannot call moodle_page::set_course after output has been started.');
279         }
281         if (!empty($this->_course->id) && $this->_course->id != $course->id) {
282             $this->_categories = null;
283         }
285         $this->_course = clone($course);
286         $COURSE = $this->_course;
288         if (!$this->_context) {
289             $this->set_context(get_context_instance(CONTEXT_COURSE, $this->_course->id));
290         }
292         moodle_setlocale();
293         theme_setup();
294     }
296     /**
297      * Set the main context to which this page belongs.
298      * @param object $context a context object, normally obtained with get_context_instance.
299      */
300     public function set_context($context) {
301         $this->_context = $context;
302     }
304     /**
305      * @param string $pagetype e.g. 'my-index' or 'mod-quiz-attempt'. Normally
306      * you do not need to set this manually, it is automatically created from the
307      * script name. However, on some pages this is overridden. For example, the
308      * page type for coures/view.php includes the course format, for example
309      * 'coures-view-weeks'. This gets used as the id attribute on <body> and
310      * also for determining which blocks are displayed.
311      */
312     public function set_pagetype($pagetype) {
313         $this->_pagetype = $pagetype;
314     }
316     /**
317      * @param string $class add this class name ot the class attribute on the body tag.
318      */
319     public function add_body_class($class) {
320         if ($this->_state > self::STATE_BEFORE_HEADER) {
321             throw new coding_exception('Cannot call moodle_page::add_body_class after output has been started.');
322         }
323         $this->_bodyclasses[$class] = 1;
324     }
326     /**
327      * @param array $classes this utility method calls add_body_class for each array element.
328      */
329     public function add_body_classes($classes) {
330         foreach ($classes as $class) {
331             $this->add_body_class($class);
332         }
333     }
335     /**
336      * Set the course category this page belongs to manually. This automatically
337      * sets $PAGE->course to be the site coures. You cannot use this method if
338      * you have already set $PAGE->course - in that case, the category must be
339      * the one that the course belongs to. This also automatically sets the
340      * page context to the category context.
341      * @param integer $categoryid The id of the category to set.
342      */
343     public function set_category_by_id($categoryid) {
344         global $SITE, $DB;
345         if (!is_null($this->_course)) {
346             throw new coding_exception('Attempt to manually set the course category when the course has been set. This is not allowed.');
347         }
348         if (is_array($this->_categories)) {
349             throw new coding_exception('Course category has already been set. You are not allowed to change it.');
350         }
351         $this->set_course($SITE);
352         $this->load_category($categoryid);
353         $this->set_context(get_context_instance(CONTEXT_COURSECAT, $categoryid));
354     }
356     /**
357      * Set a different path to use for the 'Moodle docs for this page' link.
358      * By default, it uses the pagetype, which is normally the same as the
359      * script name. So, for example, for mod/quiz/attempt.php, pagetype is
360      * mod-quiz-attempt, and so docspath is mod/quiz/attempt.
361      * @param string $path the path to use at the end of the moodle docs URL.
362      */
363     public function set_docs_path($path) {
364         $this->_docspath = $path;
365     }
367     /**
368      * You should call this method from every page to set the cleaned-up URL
369      * that should be used to return to this page. Used, for example, by the
370      * blocks editing UI to know where to return the user after an action.
371      * For example, course/view.php does:
372      *      $id = optional_param('id', 0, PARAM_INT);
373      *      $PAGE->set_url('course/view.php', array('id' => $id));
374      * @param string $url a URL, relative to $CFG->wwwroot.
375      * @param array $params paramters to add ot the URL.
376      */
377     public function set_url($url, $params = array()) {
378         global $CFG;
379         $this->_url = new moodle_url($CFG->wwwroot . '/' . $url, $params);
380         if (is_null($this->_pagetype)) {
381             $this->initialise_default_pagetype($url);
382         }
383     }
385 /// Initialisation methods =====================================================
386 /// These set various things up in a default way.
388     /**
389      * Sets ->pagetype from the script name. For example, if the script that was
390      * run is mod/quiz/view.php, ->pagetype will be set to 'mod-quiz-view'.
391      * @param string $script the path to the script that should be used to
392      * initialise ->pagetype. If not passed the $SCRIPT global will be used.
393      * If legacy code has set $CFG->pagepath that will be used instead, and a
394      * developer warning issued.
395      */
396     protected function initialise_default_pagetype($script = null) {
397         global $CFG, $SCRIPT;
399         if (isset($CFG->pagepath)) {
400             debugging('Some code appears to have set $CFG->pagepath. That was a horrible deprecated thing. ' .
401                     'Don\'t do it! Try calling $PAGE->set_pagetype() instead.');
402             $script = $CFG->pagepath;
403             unset($CFG->pagepath);
404         }
406         if (is_null($script)) {
407             $script = ltrim($SCRIPT, '/');
408             $len = strlen($CFG->admin);
409             if (substr($script, 0, $len) == $CFG->admin) {
410                 $script = 'admin' . substr($script, $len);
411             }
412         }
414         $path = str_replace('.php', '', $script);
415         if (substr($path, -1) == '/') {
416             $path .= 'index';
417         }
419         if (empty($path) || $path == 'index') {
420             $this->_pagetype = 'site-index';
421         } else {
422             $this->_pagetype = str_replace('/', '-', $path);
423         }
424     }
426     protected function initialise_standard_body_classes() {
427         global $CFG;
429         $pagetype = $this->pagetype;
430         if ($pagetype == 'site-index') {
431             $this->_legacyclass = 'course';
432         } else if (substr($pagetype, 0, 6) == 'admin-') {
433             $this->_legacyclass = 'admin';
434         } else {
435             $this->_legacyclass = substr($pagetype, 0, strrpos($pagetype, '-'));
436         }
437         $this->add_body_class($this->_legacyclass);
439         $this->add_body_class('course-' . $this->_course->id);
440         $this->add_body_classes(get_browser_version_classes());
441         $this->add_body_class('dir-' . get_string('thisdirection'));
442         $this->add_body_class('lang-' . current_language());
444         $this->add_body_class($this->url_to_class_name($CFG->wwwroot));
446         if ($CFG->allowcategorythemes) {
447             $this->ensure_category_loaded();
448             foreach ($this->_categories as $catid => $notused) {
449                 $this->add_body_class('category-' . $catid);
450             }
451         } else {
452             $catid = 0;
453             if (is_array($this->_categories)) {
454                 $catids = array_keys($this->_categories);
455                 $catid = reset($catids);
456             } else if (!empty($this->_course->category)) {
457                 $catid = $this->_course->category;
458             }
459             if ($catid) {
460                 $this->add_body_class('category-' . $catid);
461             }
462         }
464         if (!isloggedin()) {
465             $this->add_body_class('notloggedin');
466         }
468         if (!empty($USER->editing)) {
469             $this->add_body_class('editing');
470         }
472         if (!empty($CFG->blocksdrag)) {
473             $this->add_body_class('drag');
474         }
475     }
477     protected function ensure_category_loaded() {
478         if (is_array($this->_categories)) {
479             return; // Already done.
480         }
481         if (is_null($this->_course)) {
482             throw new coding_exception('Attempt to get the course category for this page before the course was set.');
483         }
484         if ($this->_course->category == 0) {
485             $this->_categories = array();
486         } else {
487             $this->load_category($this->_course->category);
488         }
489     }
491     protected function load_category($categoryid) {
492         global $DB;
493         $category = $DB->get_record('course_categories', array('id' => $categoryid));
494         if (!$category) {
495             throw new moodle_exception('unknowncategory');
496         }
497         $this->_categories[$category->id] = $category;
498         $parentcategoryids = explode('/', trim($category->path, '/'));
499         array_pop($parentcategoryids);
500         foreach (array_reverse($parentcategoryids) as $catid) {
501             $this->_categories[$catid] = null;
502         }
503     }
505     protected function ensure_categories_loaded() {
506         global $DB;
507         $this->ensure_category_loaded();
508         if (!is_null(end($this->_categories))) {
509             return; // Already done.
510         }
511         $idstoload = array_keys($this->_categories);
512         array_shift($idstoload);
513         $categories = $DB->get_records_list('course_categories', 'id', $idstoload);
514         foreach ($idstoload as $catid) {
515             $this->_categories[$catid] = $categories[$catid];
516         }
517     }
519     protected function url_to_class_name($url) {
520         $bits = parse_url($url);
521         $class = str_replace('.', '-', $bits['host']);
522         if (!empty($bits['port'])) {
523             $class .= '--' . $bits['port'];
524         }
525         if (!empty($bits['path'])) {
526             $path = trim($bits['path'], '/');
527             if ($path) {
528                 $class .= '--' . str_replace('/', '-', $path);
529             }
530         }
531         return $class;
532     }
534 /// Deprecated fields and methods for backwards compatibility ==================
536     /**
537      * @deprecated since Moodle 2.0 - use $PAGE->pagetype instead.
538      * @return string page type.
539      */
540     public function get_type() {
541         debugging('Call to deprecated method moodle_page::get_type. Please use $PAGE->pagetype instead.');
542         return $this->get_pagetype();
543     }
545     /**
546      * @deprecated since Moodle 2.0 - use $PAGE->pagetype instead.
547      * @return string this is what page_id_and_class used to return via the $getclass parameter.
548      */
549     function get_format_name() {
550         return $this->get_pagetype();
551     }
553     /**
554      * @deprecated since Moodle 2.0 - use $PAGE->course instead.
555      * @return object course.
556      */
557     public function get_courserecord() {
558         debugging('Call to deprecated method moodle_page::get_courserecord. Please use $PAGE->course instead.');
559         return $this->get_course();
560     }
562     /**
563      * @deprecated since Moodle 2.0
564      * @return string this is what page_id_and_class used to return via the $getclass parameter.
565      */
566     public function get_legacyclass() {
567         if (is_null($this->_legacyclass)) {
568             $this->initialise_standard_body_classes();
569         }
570         debugging('Call to deprecated method moodle_page::get_legacyclass.');
571         return $this->_legacyclass;
572     }
574     /**
575      * @deprecated since Moodle 2.0 - use $PAGE->blocks->get_positions() instead
576      * @return string the places on this page where blocks can go.
577      */
578     function blocks_get_positions() {
579         debugging('Call to deprecated method moodle_page::blocks_get_positions. Use $PAGE->blocks->get_positions() instead.');
580         return $this->blocks->get_positions();
581     }
583     /**
584      * @deprecated since Moodle 2.0 - use $PAGE->blocks->get_default_position() instead
585      * @return string the default place for blocks on this page.
586      */
587     function blocks_default_position() {
588         debugging('Call to deprecated method moodle_page::blocks_default_position. Use $PAGE->blocks->get_default_position() instead.');
589         return $this->blocks->get_default_position();
590     }
592     /**
593      * @deprecated since Moodle 2.0 - no longer used.
594      */
595     function blocks_get_default() {
596         debugging('Call to deprecated method moodle_page::blocks_get_default. This method has no function any more.');
597     }
599     /**
600      * @deprecated since Moodle 2.0 - no longer used.
601      */
602     function blocks_move_position(&$instance, $move) {
603         debugging('Call to deprecated method moodle_page::blocks_move_position. This method has no function any more.');
604     }
606     /**
607      * @deprecated since Moodle 2.0 - use $this->url->params() instead.
608      * @return array URL parameters for this page.
609      */
610     function url_get_parameters() {
611         debugging('Call to deprecated method moodle_page::url_get_parameters. Use $this->url->params() instead.');
612         return $this->url->params();
613     }
615     /**
616      * @deprecated since Moodle 2.0 - use $this->url->params() instead.
617      * @return string URL for this page without parameters.
618      */
619     function url_get_path() {
620         debugging('Call to deprecated method moodle_page::url_get_path. Use $this->url->out(false) instead.');
621         return $this->url->out(false);
622     }
624     /**
625      * @deprecated since Moodle 2.0 - use $this->url->out() instead.
626      * @return string full URL for this page.
627      */
628     function url_get_full($extraparams = array()) {
629         debugging('Call to deprecated method moodle_page::url_get_full. Use $this->url->out() instead.');
630         return $this->url->out($extraparams);
631     }
634 /** Stub implementation of the blocks_manager, to stop things from breaking too badly. */
635 class blocks_manager {
636     public function get_positions() {
637         return array(BLOCK_POS_LEFT, BLOCK_POS_RIGHT);
638     }
640     public function get_default_position() {
641         return BLOCK_POS_RIGHT;
642     }
645 /**
646  * @deprecated since Moodle 2.0
647  * Load any page_base subclasses from the pagelib.php library in a particular folder.
648  * @param $path the folder path
649  * @return array an array of page types.
650  */
651 function page_import_types($path) {
652     global $CFG;
653     debugging('Call to deprecated function page_import_types.', DEBUG_DEVELOPER);
656 /**
657  * @deprecated since Moodle 2.0
658  * @param integer $instance legacy page instance id.
659  * @return the global $PAGE object.
660  */
661 function page_create_instance($instance) {
662     return page_create_object($PAGE->pagetype, $instance);
665 /**
666  * Factory function page_create_object(). Called with a pagetype identifier and possibly with
667  * its numeric ID. Returns a fully constructed page_base subclass you can work with.
668  */
669 function page_create_object($type, $id = NULL) {
670     global $CFG, $PAGE, $SITE;
672     $data = new stdClass;
673     $data->pagetype = $type;
674     $data->pageid   = $id;
676     $classname = page_map_class($type);
677     $legacypage = new $classname;
678     $legacypage->init_quick($data);
679     // $PAGE->set_pagetype($type);
680     // $PAGE->set_url(str_replace($CFG->wwwroot . '/', '', $legacypage->url_get_full_()));
681     // return $PAGE;
683     $course = $PAGE->course;
684     if ($course->id != $SITE->id) {
685         $legacypage->set_course($course);
686     } else {
687         try {
688             $category = $PAGE->category;
689         } catch (coding_exception $e) {
690             // Was not set before, so no need to try to set it again.
691             $category = false;
692         }
693         if ($category) {
694             $legacypage->set_category_by_id($category->id);
695         } else {
696             $legacypage->set_course($SITE);
697         }
698     }
699     return $legacypage;
702 /**
703  * Function page_map_class() is the way for your code to define its own page subclasses and let Moodle recognize them.
704  * Use it to associate the textual identifier of your Page with the actual class name that has to be instantiated.
705  */
706 function page_map_class($type, $classname = NULL) {
707     global $CFG;
709     static $mappings = NULL;
711     if ($mappings === NULL) {
712         $mappings = array(
713             PAGE_COURSE_VIEW => 'page_course'
714         );
715     }
717     if (!empty($type) && !empty($classname)) {
718         $mappings[$type] = $classname;
719     }
721     if (!isset($mappings[$type])) {
722         debugging('Page class mapping requested for unknown type: '.$type);
723     }
725     if (empty($classname) && !class_exists($mappings[$type])) {
726         debugging('Page class mapping for id "'.$type.'" exists but class "'.$mappings[$type].'" is not defined');
727     }
729     return $mappings[$type];
732 /**
733  * Parent class from which all Moodle page classes derive
734  *
735  * @author Jon Papaioannou
736  * @package pages
737  * @todo This parent class is very messy still. Please for the moment ignore it and move on to the derived class page_course to see the comments there.
738  */
739 class page_base extends moodle_page {
740     /**
741      * The string identifier for the type of page being described.
742      * @var string $type
743      */
744     var $type           = NULL;
746     /**
747      * The numeric identifier of the page being described.
748      * @var int $id
749      */
750     var $id             = NULL;
752     /**
753      * Class bool to determine if the instance's full initialization has been completed.
754      * @var boolean $full_init_done
755      */
756     var $full_init_done = false;
758 /// Class Functions
760     // HTML OUTPUT SECTION
762     // We have absolutely no idea what derived pages are all about
763     function print_header($title, $morenavlinks=NULL) {
764         trigger_error('Page class does not implement method <strong>print_header()</strong>', E_USER_WARNING);
765         return;
766     }
768     // SELF-REPORTING SECTION
771     // Simple stuff, do not override this.
772     function get_id() {
773         return $this->id;
774     }
776     // Initialize the data members of the parent class
777     function init_quick($data) {
778         $this->type = $data->pagetype;
779         $this->id   = $data->pageid;
780     }
782     function init_full() {
783         $this->full_init_done = true;
784     }
787 /**
788  * Class that models the behavior of a moodle course
789  *
790  * @author Jon Papaioannou
791  * @package pages
792  */
793 class page_course extends page_base {
795     // Do any validation of the officially recognized bits of the data and forward to parent.
796     // Do NOT load up "expensive" resouces (e.g. SQL data) here!
797     function init_quick($data) {
798         if(empty($data->pageid) && !defined('ADMIN_STICKYBLOCKS')) {
799             print_error('cannotinitpage', 'debug', '', (object)array('name'=>'course', 'id'=>'?'));
800         }
801         parent::init_quick($data);
802     }
804     // Here you should load up all heavy-duty data for your page. Basically everything that
805     // does not NEED to be loaded for the class to make basic decisions should NOT be loaded
806     // in init_quick() and instead deferred here. Of course this function had better recognize
807     // $this->full_init_done to prevent wasteful multiple-time data retrieval.
808     function init_full() {
809         global $COURSE, $DB;
811         if($this->full_init_done) {
812             return;
813         }
814         if (empty($this->id)) {
815             $this->id = 0; // avoid db errors
816         }
818         $this->context = get_context_instance(CONTEXT_COURSE, $this->id);
820         // Preload - ensures that the context cache is populated
821         // in one DB query...
822         $this->childcontexts = get_child_contexts($this->context);
824         // Mark we're done
825         $this->full_init_done = true;
826     }
828     // USER-RELATED THINGS
830     // Can user edit the course page or "sticky page"?
831     // This is also about editting of blocks BUT mainly activities in course page layout, see
832     // update_course_icon() has very similar checks - it must use the same capabilities
833     //
834     // this is a _very_ expensive check - so cache it during execution
835     //
836     function user_allowed_editing() {
837         $this->init_full();
839         if (isset($this->_user_allowed_editing)) {
840             return $this->_user_allowed_editing;
841         }
843         if (has_capability('moodle/site:manageblocks', get_context_instance(CONTEXT_SYSTEM))
844                 && defined('ADMIN_STICKYBLOCKS')) {
845             $this->_user_allowed_editing = true;
846             return true;
847         }
848         if (has_capability('moodle/course:manageactivities', $this->context)) {
849             $this->_user_allowed_editing = true;
850             return true;
851         }
853         // Exhaustive (and expensive!) checks to see if the user
854         // has editing abilities to a specific module/block/group...
855         // This code would benefit from the ability to check specifically
856         // for overrides.
857         foreach ($this->childcontexts as $cc) {
858             if (($cc->contextlevel == CONTEXT_MODULE &&
859                  has_capability('moodle/course:manageactivities', $cc)) ||
860                 ($cc->contextlevel == CONTEXT_BLOCK &&
861                  has_capability('moodle/site:manageblocks', $cc))) {
862                 $this->_user_allowed_editing = true;
863                 return true;
864             }
865         }
866     }
868     // HTML OUTPUT SECTION
870     // This function prints out the common part of the page's header.
871     // You should NEVER print the header "by hand" in other code.
872     function print_header($title, $morenavlinks=NULL, $meta='', $bodytags='', $extrabuttons='') {
873         global $USER, $CFG;
875         $this->init_full();
876         $replacements = array(
877             '%fullname%' => $this->course->fullname
878         );
879         foreach($replacements as $search => $replace) {
880             $title = str_replace($search, $replace, $title);
881         }
883         $navlinks = array();
885         if(!empty($morenavlinks)) {
886             $navlinks = array_merge($navlinks, $morenavlinks);
887         }
889         $navigation = build_navigation($navlinks);
891         // The "Editing On" button will be appearing only in the "main" course screen
892         // (i.e., no breadcrumbs other than the default one added inside this function)
893         $buttons = switchroles_form($this->course->id);
894         if ($this->user_allowed_editing()) {
895             $buttons .= update_course_icon($this->course->id );
896         }
897         $buttons = empty($morenavlinks) ? $buttons : '&nbsp;';
899         // Add any extra buttons requested (by the resource module, for example)
900         if ($extrabuttons != '') {
901             $buttons = ($buttons == '&nbsp;') ? $extrabuttons : $buttons.$extrabuttons;
902         }
904         print_header($title, $this->course->fullname, $navigation,
905                      '', $meta, true, $buttons, user_login_string($this->course, $USER), false, $bodytags);
906     }
908     // SELF-REPORTING SECTION
910     // When we are creating a new page, use the data at your disposal to provide a textual representation of the
911     // blocks that are going to get added to this new page. Delimit block names with commas (,) and use double
912     // colons (:) to delimit between block positions in the page.
913     function _legacy_blocks_get_default() {
914         global $CFG;
916         $this->init_full();
918         if($this->id == SITEID) {
919         // Is it the site?
920             if (!empty($CFG->defaultblocks_site)) {
921                 $blocknames = $CFG->defaultblocks_site;
922             }
923             /// Failsafe - in case nothing was defined.
924             else {
925                 $blocknames = 'site_main_menu,admin_tree:course_summary,calendar_month';
926             }
927         }
928         // It's a normal course, so do it according to the course format
929         else {
930             $pageformat = $this->course->format;
931             if (!empty($CFG->{'defaultblocks_'. $pageformat})) {
932                 $blocknames = $CFG->{'defaultblocks_'. $pageformat};
933             }
934             else {
935                 $format_config = $CFG->dirroot.'/course/format/'.$pageformat.'/config.php';
936                 if (@is_file($format_config) && is_readable($format_config)) {
937                     require($format_config);
938                 }
939                 if (!empty($format['defaultblocks'])) {
940                     $blocknames = $format['defaultblocks'];
941                 }
942                 else if (!empty($CFG->defaultblocks)){
943                     $blocknames = $CFG->defaultblocks;
944                 }
945                 /// Failsafe - in case nothing was defined.
946                 else {
947                     $blocknames = 'participants,activity_modules,search_forums,admin,course_list:news_items,calendar_upcoming,recent_activity';
948                 }
949             }
950         }
952         return $blocknames;
953     }
956 /**
957  * Class that models the common parts of all activity modules
958  *
959  * @author Jon Papaioannou
960  * @package pages
961  */
962 class page_generic_activity extends page_base {
963     var $activityname   = NULL;
964     var $modulerecord   = NULL;
965     var $activityrecord = NULL;
967     function init_full() {
968         global $DB;
970         if($this->full_init_done) {
971             return;
972         }
973         if(empty($this->activityname)) {
974             print_error('noactivityname', 'debug');
975         }
976         if (!$this->modulerecord = get_coursemodule_from_instance($this->activityname, $this->id)) {
977             print_error('cannotinitpager', 'debug', '', (object)array('name'=>$this->activityname, 'id'=>$this->id));
978         }
979         $this->activityrecord = $DB->get_record($this->activityname, array('id'=>$this->id));
980         if(empty($this->activityrecord)) {
981             print_error('cannotinitpager', 'debug', '', (object)array('name'=>$this->activityname, 'id'=>$this->id));
982         }
983         $this->full_init_done = true;
984     }
986     function user_allowed_editing() {
987         $this->init_full();
988         // Yu: I think this is wrong, should be checking manageactivities instead
989         //return has_capability('moodle/site:manageblocks', get_context_instance(CONTEXT_COURSE, $this->modulerecord->course));
990         return has_capability('moodle/course:manageactivities', get_context_instance(CONTEXT_MODULE, $this->modulerecord->id));
991     }
993     function print_header($title, $morenavlinks = NULL, $bodytags = '', $meta = '') {
994         global $USER, $CFG;
996         $this->init_full();
997         $replacements = array(
998             '%fullname%' => format_string($this->activityrecord->name)
999         );
1000         foreach ($replacements as $search => $replace) {
1001             $title = str_replace($search, $replace, $title);
1002         }
1004         if (empty($morenavlinks) && $this->user_allowed_editing()) {
1005             $buttons = '<table><tr><td>'.update_module_button($this->modulerecord->id, $this->course->id, get_string('modulename', $this->activityname)).'</td>';
1006             if (!empty($CFG->showblocksonmodpages)) {
1007                 $buttons .= '<td><form '.$CFG->frametarget.' method="get" action="view.php"><div>'.
1008                     '<input type="hidden" name="id" value="'.$this->modulerecord->id.'" />'.
1009                     '<input type="hidden" name="edit" value="'.($this->user_is_editing()?'off':'on').'" />'.
1010                     '<input type="submit" value="'.get_string($this->user_is_editing()?'blockseditoff':'blocksediton').'" /></div></form></td>';
1011             }
1012             $buttons .= '</tr></table>';
1013         } else {
1014             $buttons = '&nbsp;';
1015         }
1017         if (empty($morenavlinks)) {
1018             $morenavlinks = array();
1019         }
1020         $navigation = build_navigation($morenavlinks, $this->modulerecord);
1021         print_header($title, $this->course->fullname, $navigation, '', $meta, true, $buttons, navmenu($this->course, $this->modulerecord), false, $bodytags);
1022     }
1025 ?>