file popup.html was added on branch MOODLE_15_STABLE on 2005-07-07 16:14:38 +0000
[moodle.git] / lib / pagelib.php
bloba5467d5297e2beeccfc01c667bc36a1653a87a15
1 <?php //$Id$
3 /**
4 * This file contains the parent class for moodle pages, page_base,
5 * as well as the page_course subclass.
6 * A page is defined by its page type (ie. course, blog, activity) and its page id
7 * (courseid, blogid, activity id, etc).
9 * @author Jon Papaioannou
10 * @version $Id$
11 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
12 * @package pages
15 function page_import_types($path) {
16 global $CFG;
18 static $types = array();
20 if(substr($path, -1) != '/') {
21 $path .= '/';
24 $path = clean_param($path, PARAM_PATH);
26 if(isset($types[$path])) {
27 return $types[$path];
30 $file = $CFG->dirroot.'/'.$path.'pagelib.php';
32 if(is_file($file)) {
33 require($file);
34 if(!isset($DEFINEDPAGES)) {
35 error('Imported '.$file.' but found no page classes');
37 return $types[$path] = $DEFINEDPAGES;
40 return false;
43 /**
44 * Factory function page_create_object(). Called with a numeric ID for a page, it autodetects
45 * the page type, constructs the correct object and returns it.
48 function page_create_instance($instance) {
49 page_id_and_class($id, $class);
50 return page_create_object($id, $instance);
53 /**
54 * Factory function page_create_object(). Called with a pagetype identifier and possibly with
55 * its numeric ID. Returns a fully constructed page_base subclass you can work with.
58 function page_create_object($type, $id = NULL) {
59 global $CFG;
61 $data = new stdClass;
62 $data->pagetype = $type;
63 $data->pageid = $id;
65 $classname = page_map_class($type);
67 $object = &new $classname;
68 // TODO: subclassing check here
70 if ($object->get_type() !== $type) {
71 // Somehow somewhere someone made a mistake
72 if ($CFG->debug > 7) {
73 error('Page object\'s type ('. $object->get_type() .') does not match requested type ('. $type .')');
77 $object->init_quick($data);
78 return $object;
81 /**
82 * Function page_map_class() is the way for your code to define its own page subclasses and let Moodle recognize them.
83 * Use it to associate the textual identifier of your Page with the actual class name that has to be instantiated.
86 function page_map_class($type, $classname = NULL) {
87 global $CFG;
89 static $mappings = NULL;
91 if ($mappings === NULL) {
92 $mappings = array(
93 PAGE_COURSE_VIEW => 'page_course'
97 if (!empty($type) && !empty($classname)) {
98 $mappings[$type] = $classname;
101 if (!isset($mappings[$type])) {
102 if ($CFG->debug > 7) {
103 error('Page class mapping requested for unknown type: '.$type);
107 if (!class_exists($mappings[$type])) {
108 if ($CFG->debug > 7) {
109 error('Page class mapping for id "'.$type.'" exists but class "'.$mappings[$type].'" is not defined');
113 return $mappings[$type];
117 * Parent class from which all Moodle page classes derive
119 * @author Jon Papaioannou
120 * @package pages
121 * @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.
124 class page_base {
126 * The string identifier for the type of page being described.
127 * @var string $type
129 var $type = NULL;
132 * The numeric identifier of the page being described.
133 * @var int $id
135 var $id = NULL;
138 * Class bool to determine if the instance's full initialization has been completed.
139 * @var boolean $full_init_done
141 var $full_init_done = false;
144 * The class attribute that Moodle has to assign to the BODY tag for this page.
145 * @var string $body_class
147 var $body_class = NULL;
150 * The id attribute that Moodle has to assign to the BODY tag for this page.
151 * @var string $body_id
153 var $body_id = NULL;
155 /// Class Functions
157 // CONSTRUCTION
159 // A whole battery of functions to allow standardized-name constructors in all versions of PHP.
160 // The constructor is actually called construct()
161 function page_base() {
162 $this->construct();
165 function __construct() {
166 $this->construct();
169 function construct() {
170 page_id_and_class($this->body_id, $this->body_class);
173 // USER-RELATED THINGS
175 // By default, no user is editing anything and none CAN edit anything. Developers
176 // will have to override these settings to let Moodle know when it should grant
177 // editing rights to the user viewing the page.
178 function user_allowed_editing() {
179 trigger_error('Page class does not implement method <strong>user_allowed_editing()</strong>', E_USER_WARNING);
180 return false;
182 function user_is_editing() {
183 trigger_error('Page class does not implement method <strong>user_is_editing()</strong>', E_USER_WARNING);
184 return false;
187 // HTML OUTPUT SECTION
189 // We have absolutely no idea what derived pages are all about
190 function print_header($title, $morebreadcrumbs) {
191 trigger_error('Page class does not implement method <strong>print_header()</strong>', E_USER_WARNING);
192 return;
195 // BLOCKS RELATED SECTION
197 // By default, pages don't have any blocks. Override this in your derived class if you need blocks.
198 function blocks_get_positions() {
199 return array();
202 // Thus there is no default block position. If you override the above you should override this one too.
203 // Because this makes sense only if blocks_get_positions() is overridden and because these two should
204 // be overridden as a group or not at all, this one issues a warning. The sneaky part is that this warning
205 // will only be seen if you override blocks_get_positions() but NOT blocks_default_position().
206 function blocks_default_position() {
207 trigger_error('Page class does not implement method <strong>blocks_default_position()</strong>', E_USER_WARNING);
208 return NULL;
211 // If you don't override this, newly constructed pages of this kind won't have any blocks.
212 function blocks_get_default() {
213 return '';
216 // If you don't override this, your blocks will not be able to change positions
217 function blocks_move_position(&$instance, $move) {
218 return $instance->position;
221 // SELF-REPORTING SECTION
223 // Derived classes HAVE to define their "home url"
224 function url_get_path() {
225 trigger_error('Page class does not implement method <strong>url_get_path()</strong>', E_USER_WARNING);
226 return NULL;
229 // It's not always required to pass any arguments to the home url, so this doesn't trigger any errors (sensible default)
230 function url_get_parameters() {
231 return array();
234 // This should actually NEVER be overridden unless you have GOOD reason. Works fine as it is.
235 function url_get_full($extraparams = array()) {
236 $path = $this->url_get_path();
237 if(empty($path)) {
238 return NULL;
241 $params = $this->url_get_parameters();
242 $params = array_merge($params, $extraparams);
244 if(empty($params)) {
245 return $path;
248 $first = true;
250 foreach($params as $var => $value) {
251 $path .= $first? '?' : '&amp;';
252 $path .= $var .'='. urlencode($value);
253 $first = false;
256 return $path;
259 // This forces implementers to actually hardwire their page identification constant in the class.
260 // Good thing, if you ask me. That way we can later auto-detect "installed" page types by querying
261 // the classes themselves in the future.
262 function get_type() {
263 trigger_error('Page class does not implement method <strong>get_type()</strong>', E_USER_ERROR);
264 return NULL;
267 // Simple stuff, do not override this.
268 function get_id() {
269 return $this->id;
272 // "Sensible default" case here. Take it from the body id.
273 function get_format_name() {
274 return $this->body_id;
277 // Returns $this->body_class
278 function get_body_class() {
279 return $this->body_class;
282 // Returns $this->body_id
283 function get_body_id() {
284 return $this->body_id;
287 // Initialize the data members of the parent class
288 function init_quick($data) {
289 $this->type = $data->pagetype;
290 $this->id = $data->pageid;
293 function init_full() {
294 $this->full_init_done = true;
300 * Class that models the behavior of a moodle course
302 * @author Jon Papaioannou
303 * @package pages
306 class page_course extends page_base {
308 // Any data we might need to store specifically about ourself should be declared here.
309 // After init_full() is called for the first time, ALL of these variables should be
310 // initialized correctly and ready for use.
311 var $courserecord = NULL;
313 // Do any validation of the officially recognized bits of the data and forward to parent.
314 // Do NOT load up "expensive" resouces (e.g. SQL data) here!
315 function init_quick($data) {
316 if(empty($data->pageid)) {
317 error('Cannot quickly initialize page: empty course id');
319 parent::init_quick($data);
322 // Here you should load up all heavy-duty data for your page. Basically everything that
323 // does not NEED to be loaded for the class to make basic decisions should NOT be loaded
324 // in init_quick() and instead deferred here. Of course this function had better recognize
325 // $this->full_init_done to prevent wasteful multiple-time data retrieval.
326 function init_full() {
327 if($this->full_init_done) {
328 return;
330 $this->courserecord = get_record('course', 'id', $this->id);
331 if(empty($this->courserecord)) {
332 error('Cannot fully initialize page: invalid course id '. $this->id);
334 $this->full_init_done = true;
337 // USER-RELATED THINGS
339 // When is a user said to have "editing rights" in this page? This would have something
340 // to do with roles, in the future.
341 function user_allowed_editing() {
342 return isteacheredit($this->id);
345 // Is the user actually editing this page right now? This would have something
346 // to do with roles, in the future.
347 function user_is_editing() {
348 return isediting($this->id);
351 // HTML OUTPUT SECTION
353 // This function prints out the common part of the page's header.
354 // You should NEVER print the header "by hand" in other code.
355 function print_header($title, $morebreadcrumbs = NULL) {
356 global $USER, $CFG;
358 $this->init_full();
359 $replacements = array(
360 '%fullname%' => $this->courserecord->fullname
362 foreach($replacements as $search => $replace) {
363 $title = str_replace($search, $replace, $title);
366 if($this->courserecord->id == SITEID) {
367 $breadcrumbs = array();
369 else {
370 $breadcrumbs = array($this->courserecord->shortname => $CFG->wwwroot.'/course/view.php?id='.$this->courserecord->id);
373 if(!empty($morebreadcrumbs)) {
374 $breadcrumbs = array_merge($breadcrumbs, $morebreadcrumbs);
377 $total = count($breadcrumbs);
378 $current = 1;
379 $crumbtext = '';
380 foreach($breadcrumbs as $text => $href) {
381 if($current++ == $total) {
382 $crumbtext .= ' '.$text;
384 else {
385 $crumbtext .= ' <a href="'.$href.'">'.$text.'</a> ->';
389 // The "Editing On" button will be appearing only in the "main" course screen
390 // (i.e., no breadcrumbs other than the default one added inside this function)
391 $button = empty($morebreadcrumbs) ? update_course_icon($this->courserecord->id) : '&nbsp;';
393 print_header($title, $this->courserecord->fullname, $crumbtext,
394 '', '', true, $button, user_login_string($this->courserecord, $USER));
397 // SELF-REPORTING SECTION
399 // This is hardwired here so the factory function page_create_object() can be sure there was no mistake.
400 // Also, it doubles as a way to let others inquire about our type.
401 function get_type() {
402 return PAGE_COURSE_VIEW;
405 // This is like the "category" of a page of this "type". For example, if the type is PAGE_COURSE_VIEW
406 // the format_name is the actual name of the course format. If the type were PAGE_ACTIVITY_VIEW, then
407 // the format_name might be that activity's name etc.
408 function get_format_name() {
409 $this->init_full();
410 if($this->id == SITEID) {
411 return parent::get_format_name();
413 return $this->body_id.'-'.$this->courserecord->format;
416 // This should return a fully qualified path to the URL which is responsible for displaying us.
417 function url_get_path() {
418 global $CFG;
419 if($this->id == SITEID) {
420 return $CFG->wwwroot .'/index.php';
422 else {
423 return $CFG->wwwroot .'/course/view.php';
427 // This should return an associative array of any GET/POST parameters that are needed by the URL
428 // which displays us to make it work. If none are needed, return an empty array.
429 function url_get_parameters() {
430 if($this->id == SITEID) {
431 return array();
433 else {
434 return array('id' => $this->id);
438 // BLOCKS RELATED SECTION
440 // Which are the positions in this page which support blocks? Return an array containing their identifiers.
441 // BE CAREFUL, ORDER DOES MATTER! In textual representations, lists of blocks in a page use the ':' character
442 // to delimit different positions in the page. The part before the first ':' in such a representation will map
443 // directly to the first item of the array you return here, the second to the next one and so on. This way,
444 // you can add more positions in the future without interfering with legacy textual representations.
445 function blocks_get_positions() {
446 return array(BLOCK_POS_LEFT, BLOCK_POS_RIGHT);
449 // When a new block is created in this page, which position should it go to?
450 function blocks_default_position() {
451 return BLOCK_POS_RIGHT;
454 // When we are creating a new page, use the data at your disposal to provide a textual representation of the
455 // blocks that are going to get added to this new page. Delimit block names with commas (,) and use double
456 // colons (:) to delimit between block positions in the page. See blocks_get_positions() for additional info.
457 function blocks_get_default() {
458 global $CFG;
460 $this->init_full();
462 if($this->id == SITEID) {
463 // Is it the site?
464 if (!empty($CFG->defaultblocks_site)) {
465 $blocknames = $CFG->defaultblocks_site;
467 /// Failsafe - in case nothing was defined.
468 else {
469 $blocknames = 'site_main_menu,admin,course_list:course_summary,calendar_month';
472 // It's a normal course, so do it according to the course format
473 else {
474 $pageformat = $this->courserecord->format;
475 if (!empty($CFG->{'defaultblocks_'. $pageformat})) {
476 $blocknames = $CFG->{'defaultblocks_'. $pageformat};
478 else {
479 $format_config = $CFG->dirroot.'/course/format/'.$pageformat.'/config.php';
480 if (@is_file($format_config) && is_readable($format_config)) {
481 require($format_config);
483 if (!empty($format['defaultblocks'])) {
484 $blocknames = $format['defaultblocks'];
486 else if (!empty($CFG->defaultblocks)){
487 $blocknames = $CFG->defaultblocks;
489 /// Failsafe - in case nothing was defined.
490 else {
491 $blocknames = 'participants,activity_modules,search_forums,admin,course_list:news_items,calendar_upcoming,recent_activity';
496 return $blocknames;
499 // Given an instance of a block in this page and the direction in which we want to move it, where is
500 // it going to go? Return the identifier of the instance's new position. This allows us to tell blocklib
501 // how we want the blocks to move around in this page in an arbitrarily complex way. If the move as given
502 // does not make sense, make sure to return the instance's original position.
504 // Since this is going to get called a LOT, pass the instance by reference purely for speed. Do **NOT**
505 // modify its data in any way, this will actually confuse blocklib!!!
506 function blocks_move_position(&$instance, $move) {
507 if($instance->position == BLOCK_POS_LEFT && $move == BLOCK_MOVE_RIGHT) {
508 return BLOCK_POS_RIGHT;
509 } else if ($instance->position == BLOCK_POS_RIGHT && $move == BLOCK_MOVE_LEFT) {
510 return BLOCK_POS_LEFT;
512 return $instance->position;
517 * Class that models the common parts of all activity modules
519 * @author Jon Papaioannou
520 * @package pages
523 class page_generic_activity extends page_base {
524 var $activityname = NULL;
525 var $courserecord = NULL;
526 var $modulerecord = NULL;
527 var $activityrecord = NULL;
529 function init_full() {
530 if($this->full_init_done) {
531 return;
533 if(empty($this->activityname)) {
534 error('Page object derived from page_generic_activity but did not define $this->activityname');
536 $module = get_record('modules', 'name', $this->activityname);
537 $this->modulerecord = get_record('course_modules', 'module', $module->id, 'instance', $this->id);
538 if(empty($this->modulerecord)) {
539 error('Cannot fully initialize page: invalid '.$this->activityname.' instance id '. $this->id);
541 $this->courserecord = get_record('course', 'id', $this->modulerecord->course);
542 if(empty($this->courserecord)) {
543 error('Cannot fully initialize page: invalid course id '. $this->modulerecord->course);
545 $this->activityrecord = get_record($this->activityname, 'id', $this->id);
546 if(empty($this->courserecord)) {
547 error('Cannot fully initialize page: invalid '.$this->activityname.' id '. $this->id);
549 $this->full_init_done = true;
552 function user_allowed_editing() {
553 $this->init_full();
554 return isteacheredit($this->modulerecord->course);
557 function user_is_editing() {
558 $this->init_full();
559 return isediting($this->modulerecord->course);
562 function url_get_path() {
563 global $CFG;
564 return $CFG->wwwroot .'/mod/'.$this->activityname.'/view.php';
567 function url_get_parameters() {
568 $this->init_full();
569 return array('id' => $this->modulerecord->id);
572 function blocks_get_positions() {
573 return array(BLOCK_POS_LEFT);
576 function blocks_default_position() {
577 return BLOCK_POS_LEFT;