| [ Index ] |
PHP Cross Reference of Moodle 1.9.3 [Build 15-Oct-2008] |
[Summary view] [Print] [Text view]
1 <?php //$Id: pagelib.php,v 1.62.2.8 2008/01/25 08:34:37 scyrma Exp $ 2 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). 8 * 9 * @author Jon Papaioannou 10 * @version $Id: pagelib.php,v 1.62.2.8 2008/01/25 08:34:37 scyrma Exp $ 11 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License 12 * @package pages 13 */ 14 15 function page_import_types($path) { 16 global $CFG; 17 18 static $types = array(); 19 20 if(substr($path, -1) != '/') { 21 $path .= '/'; 22 } 23 24 $path = clean_param($path, PARAM_PATH); 25 26 if(isset($types[$path])) { 27 return $types[$path]; 28 } 29 30 $file = $CFG->dirroot.'/'.$path.'pagelib.php'; 31 32 if(is_file($file)) { 33 require($file); 34 if(!isset($DEFINEDPAGES)) { 35 error('Imported '.$file.' but found no page classes'); 36 } 37 return $types[$path] = $DEFINEDPAGES; 38 } 39 40 return false; 41 } 42 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. 46 */ 47 48 function page_create_instance($instance) { 49 page_id_and_class($id, $class); 50 return page_create_object($id, $instance); 51 } 52 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. 56 */ 57 58 function page_create_object($type, $id = NULL) { 59 global $CFG; 60 61 $data = new stdClass; 62 $data->pagetype = $type; 63 $data->pageid = $id; 64 65 $classname = page_map_class($type); 66 67 $object = new $classname; 68 // TODO: subclassing check here 69 70 if ($object->get_type() !== $type) { 71 // Somehow somewhere someone made a mistake 72 debugging('Page object\'s type ('. $object->get_type() .') does not match requested type ('. $type .')'); 73 } 74 75 $object->init_quick($data); 76 return $object; 77 } 78 79 /** 80 * Function page_map_class() is the way for your code to define its own page subclasses and let Moodle recognize them. 81 * Use it to associate the textual identifier of your Page with the actual class name that has to be instantiated. 82 */ 83 84 function page_map_class($type, $classname = NULL) { 85 global $CFG; 86 87 static $mappings = NULL; 88 89 if ($mappings === NULL) { 90 $mappings = array( 91 PAGE_COURSE_VIEW => 'page_course' 92 ); 93 } 94 95 if (!empty($type) && !empty($classname)) { 96 $mappings[$type] = $classname; 97 } 98 99 if (!isset($mappings[$type])) { 100 debugging('Page class mapping requested for unknown type: '.$type); 101 } 102 103 if (empty($classname) && !class_exists($mappings[$type])) { 104 debugging('Page class mapping for id "'.$type.'" exists but class "'.$mappings[$type].'" is not defined'); 105 } 106 107 return $mappings[$type]; 108 } 109 110 /** 111 * Parent class from which all Moodle page classes derive 112 * 113 * @author Jon Papaioannou 114 * @package pages 115 * @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. 116 */ 117 118 class page_base { 119 /** 120 * The string identifier for the type of page being described. 121 * @var string $type 122 */ 123 var $type = NULL; 124 125 /** 126 * The numeric identifier of the page being described. 127 * @var int $id 128 */ 129 var $id = NULL; 130 131 /** 132 * Class bool to determine if the instance's full initialization has been completed. 133 * @var boolean $full_init_done 134 */ 135 var $full_init_done = false; 136 137 /** 138 * The class attribute that Moodle has to assign to the BODY tag for this page. 139 * @var string $body_class 140 */ 141 var $body_class = NULL; 142 143 /** 144 * The id attribute that Moodle has to assign to the BODY tag for this page. 145 * @var string $body_id 146 */ 147 var $body_id = NULL; 148 149 /// Class Functions 150 151 // CONSTRUCTION 152 153 // A whole battery of functions to allow standardized-name constructors in all versions of PHP. 154 // The constructor is actually called construct() 155 function page_base() { 156 $this->construct(); 157 } 158 159 function construct() { 160 page_id_and_class($this->body_id, $this->body_class); 161 } 162 163 // USER-RELATED THINGS 164 165 // By default, no user is editing anything and none CAN edit anything. Developers 166 // will have to override these settings to let Moodle know when it should grant 167 // editing rights to the user viewing the page. 168 function user_allowed_editing() { 169 trigger_error('Page class does not implement method <strong>user_allowed_editing()</strong>', E_USER_WARNING); 170 return false; 171 } 172 function user_is_editing() { 173 trigger_error('Page class does not implement method <strong>user_is_editing()</strong>', E_USER_WARNING); 174 return false; 175 } 176 177 // HTML OUTPUT SECTION 178 179 // We have absolutely no idea what derived pages are all about 180 function print_header($title, $morenavlinks=NULL) { 181 trigger_error('Page class does not implement method <strong>print_header()</strong>', E_USER_WARNING); 182 return; 183 } 184 185 // BLOCKS RELATED SECTION 186 187 // By default, pages don't have any blocks. Override this in your derived class if you need blocks. 188 function blocks_get_positions() { 189 return array(); 190 } 191 192 // Thus there is no default block position. If you override the above you should override this one too. 193 // Because this makes sense only if blocks_get_positions() is overridden and because these two should 194 // be overridden as a group or not at all, this one issues a warning. The sneaky part is that this warning 195 // will only be seen if you override blocks_get_positions() but NOT blocks_default_position(). 196 function blocks_default_position() { 197 trigger_error('Page class does not implement method <strong>blocks_default_position()</strong>', E_USER_WARNING); 198 return NULL; 199 } 200 201 // If you don't override this, newly constructed pages of this kind won't have any blocks. 202 function blocks_get_default() { 203 return ''; 204 } 205 206 // If you don't override this, your blocks will not be able to change positions 207 function blocks_move_position(&$instance, $move) { 208 return $instance->position; 209 } 210 211 // SELF-REPORTING SECTION 212 213 // Derived classes HAVE to define their "home url" 214 function url_get_path() { 215 trigger_error('Page class does not implement method <strong>url_get_path()</strong>', E_USER_WARNING); 216 return NULL; 217 } 218 219 // It's not always required to pass any arguments to the home url, so this doesn't trigger any errors (sensible default) 220 function url_get_parameters() { 221 return array(); 222 } 223 224 // This should actually NEVER be overridden unless you have GOOD reason. Works fine as it is. 225 function url_get_full($extraparams = array()) { 226 $path = $this->url_get_path(); 227 if(empty($path)) { 228 return NULL; 229 } 230 231 $params = $this->url_get_parameters(); 232 if (!empty($params)) { 233 $params = array_merge($params, $extraparams); 234 } else { 235 $params = $extraparams; 236 } 237 238 if(empty($params)) { 239 return $path; 240 } 241 242 $first = true; 243 244 foreach($params as $var => $value) { 245 $path .= $first? '?' : '&'; 246 $path .= $var .'='. urlencode($value); 247 $first = false; 248 } 249 250 return $path; 251 } 252 253 // This forces implementers to actually hardwire their page identification constant in the class. 254 // Good thing, if you ask me. That way we can later auto-detect "installed" page types by querying 255 // the classes themselves in the future. 256 function get_type() { 257 trigger_error('Page class does not implement method <strong>get_type()</strong>', E_USER_ERROR); 258 return NULL; 259 } 260 261 // Simple stuff, do not override this. 262 function get_id() { 263 return $this->id; 264 } 265 266 // "Sensible default" case here. Take it from the body id. 267 function get_format_name() { 268 return $this->body_id; 269 } 270 271 // Returns $this->body_class 272 function get_body_class() { 273 return $this->body_class; 274 } 275 276 // Returns $this->body_id 277 function get_body_id() { 278 return $this->body_id; 279 } 280 281 // Initialize the data members of the parent class 282 function init_quick($data) { 283 $this->type = $data->pagetype; 284 $this->id = $data->pageid; 285 } 286 287 function init_full() { 288 $this->full_init_done = true; 289 } 290 291 292 // is this page always editable, regardless of anything else? 293 function edit_always() { 294 return (has_capability('moodle/site:manageblocks', get_context_instance(CONTEXT_SYSTEM)) && defined('ADMIN_STICKYBLOCKS')); 295 } 296 } 297 298 299 /** 300 * Class that models the behavior of a moodle course 301 * 302 * @author Jon Papaioannou 303 * @package pages 304 */ 305 306 class page_course extends page_base { 307 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; 312 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) && !defined('ADMIN_STICKYBLOCKS')) { 317 error('Cannot quickly initialize page: empty course id'); 318 } 319 parent::init_quick($data); 320 } 321 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 global $COURSE; 328 if($this->full_init_done) { 329 return; 330 } 331 if (empty($this->id)) { 332 $this->id = 0; // avoid db errors 333 } 334 if ($this->id == $COURSE->id) { 335 $this->courserecord = $COURSE; 336 } else { 337 $this->courserecord = get_record('course', 'id', $this->id); 338 } 339 340 if(empty($this->courserecord) && !defined('ADMIN_STICKYBLOCKS')) { 341 error('Cannot fully initialize page: invalid course id '. $this->id); 342 } 343 344 $this->context = get_context_instance(CONTEXT_COURSE, $this->id); 345 346 // Preload - ensures that the context cache is populated 347 // in one DB query... 348 $this->childcontexts = get_child_contexts($this->context); 349 350 // Mark we're done 351 $this->full_init_done = true; 352 } 353 354 // USER-RELATED THINGS 355 356 // Can user edit the course page or "sticky page"? 357 // This is also about editting of blocks BUT mainly activities in course page layout, see 358 // update_course_icon() has very similar checks - it must use the same capabilities 359 // 360 // this is a _very_ expensive check - so cache it during execution 361 // 362 function user_allowed_editing() { 363 364 $this->init_full(); 365 366 if (isset($this->_user_allowed_editing)) { 367 return $this->_user_allowed_editing; 368 } 369 370 if (has_capability('moodle/site:manageblocks', get_context_instance(CONTEXT_SYSTEM)) 371 && defined('ADMIN_STICKYBLOCKS')) { 372 $this->_user_allowed_editing = true; 373 return true; 374 } 375 if (has_capability('moodle/course:manageactivities', $this->context)) { 376 $this->_user_allowed_editing = true; 377 return true; 378 } 379 380 // Exhaustive (and expensive!) checks to see if the user 381 // has editing abilities to a specific module/block/group... 382 // This code would benefit from the ability to check specifically 383 // for overrides. 384 foreach ($this->childcontexts as $cc) { 385 if (($cc->contextlevel == CONTEXT_MODULE && 386 has_capability('moodle/course:manageactivities', $cc)) || 387 ($cc->contextlevel == CONTEXT_BLOCK && 388 has_capability('moodle/site:manageblocks', $cc))) { 389 $this->_user_allowed_editing = true; 390 return true; 391 } 392 } 393 } 394 395 // Is the user actually editing this course page or "sticky page" right now? 396 function user_is_editing() { 397 if (has_capability('moodle/site:manageblocks', get_context_instance(CONTEXT_SYSTEM)) && defined('ADMIN_STICKYBLOCKS')) { 398 //always in edit mode on sticky page 399 return true; 400 } 401 return isediting($this->id); 402 } 403 404 // HTML OUTPUT SECTION 405 406 // This function prints out the common part of the page's header. 407 // You should NEVER print the header "by hand" in other code. 408 function print_header($title, $morenavlinks=NULL, $meta='', $bodytags='', $extrabuttons='') { 409 global $USER, $CFG; 410 411 $this->init_full(); 412 $replacements = array( 413 '%fullname%' => $this->courserecord->fullname 414 ); 415 foreach($replacements as $search => $replace) { 416 $title = str_replace($search, $replace, $title); 417 } 418 419 $navlinks = array(); 420 421 if(!empty($morenavlinks)) { 422 $navlinks = array_merge($navlinks, $morenavlinks); 423 } 424 425 $navigation = build_navigation($navlinks); 426 427 // The "Editing On" button will be appearing only in the "main" course screen 428 // (i.e., no breadcrumbs other than the default one added inside this function) 429 $buttons = switchroles_form($this->courserecord->id); 430 if ($this->user_allowed_editing()) { 431 $buttons .= update_course_icon($this->courserecord->id ); 432 } 433 $buttons = empty($morenavlinks) ? $buttons : ' '; 434 435 // Add any extra buttons requested (by the resource module, for example) 436 if ($extrabuttons != '') { 437 $buttons = ($buttons == ' ') ? $extrabuttons : $buttons.$extrabuttons; 438 } 439 440 print_header($title, $this->courserecord->fullname, $navigation, 441 '', $meta, true, $buttons, user_login_string($this->courserecord, $USER), false, $bodytags); 442 } 443 444 // SELF-REPORTING SECTION 445 446 // This is hardwired here so the factory function page_create_object() can be sure there was no mistake. 447 // Also, it doubles as a way to let others inquire about our type. 448 function get_type() { 449 return PAGE_COURSE_VIEW; 450 } 451 452 // This is like the "category" of a page of this "type". For example, if the type is PAGE_COURSE_VIEW 453 // the format_name is the actual name of the course format. If the type were PAGE_ACTIVITY_VIEW, then 454 // the format_name might be that activity's name etc. 455 function get_format_name() { 456 $this->init_full(); 457 if (defined('ADMIN_STICKYBLOCKS')) { 458 return PAGE_COURSE_VIEW; 459 } 460 if($this->id == SITEID) { 461 return parent::get_format_name(); 462 } 463 // This needs to reflect the path hierarchy under Moodle root. 464 return 'course-view-'.$this->courserecord->format; 465 } 466 467 // This should return a fully qualified path to the URL which is responsible for displaying us. 468 function url_get_path() { 469 global $CFG; 470 if (defined('ADMIN_STICKYBLOCKS')) { 471 return $CFG->wwwroot.'/'.$CFG->admin.'/stickyblocks.php'; 472 } 473 if($this->id == SITEID) { 474 return $CFG->wwwroot .'/index.php'; 475 } 476 else { 477 return $CFG->wwwroot .'/course/view.php'; 478 } 479 } 480 481 // This should return an associative array of any GET/POST parameters that are needed by the URL 482 // which displays us to make it work. If none are needed, return an empty array. 483 function url_get_parameters() { 484 if (defined('ADMIN_STICKYBLOCKS')) { 485 return array('pt' => ADMIN_STICKYBLOCKS); 486 } 487 if($this->id == SITEID) { 488 return array(); 489 } 490 else { 491 return array('id' => $this->id); 492 } 493 } 494 495 // BLOCKS RELATED SECTION 496 497 // Which are the positions in this page which support blocks? Return an array containing their identifiers. 498 // BE CAREFUL, ORDER DOES MATTER! In textual representations, lists of blocks in a page use the ':' character 499 // to delimit different positions in the page. The part before the first ':' in such a representation will map 500 // directly to the first item of the array you return here, the second to the next one and so on. This way, 501 // you can add more positions in the future without interfering with legacy textual representations. 502 function blocks_get_positions() { 503 return array(BLOCK_POS_LEFT, BLOCK_POS_RIGHT); 504 } 505 506 // When a new block is created in this page, which position should it go to? 507 function blocks_default_position() { 508 return BLOCK_POS_RIGHT; 509 } 510 511 // When we are creating a new page, use the data at your disposal to provide a textual representation of the 512 // blocks that are going to get added to this new page. Delimit block names with commas (,) and use double 513 // colons (:) to delimit between block positions in the page. See blocks_get_positions() for additional info. 514 function blocks_get_default() { 515 global $CFG; 516 517 $this->init_full(); 518 519 if($this->id == SITEID) { 520 // Is it the site? 521 if (!empty($CFG->defaultblocks_site)) { 522 $blocknames = $CFG->defaultblocks_site; 523 } 524 /// Failsafe - in case nothing was defined. 525 else { 526 $blocknames = 'site_main_menu,admin_tree:course_summary,calendar_month'; 527 } 528 } 529 // It's a normal course, so do it according to the course format 530 else { 531 $pageformat = $this->courserecord->format; 532 if (!empty($CFG->{'defaultblocks_'. $pageformat})) { 533 $blocknames = $CFG->{'defaultblocks_'. $pageformat}; 534 } 535 else { 536 $format_config = $CFG->dirroot.'/course/format/'.$pageformat.'/config.php'; 537 if (@is_file($format_config) && is_readable($format_config)) { 538 require($format_config); 539 } 540 if (!empty($format['defaultblocks'])) { 541 $blocknames = $format['defaultblocks']; 542 } 543 else if (!empty($CFG->defaultblocks)){ 544 $blocknames = $CFG->defaultblocks; 545 } 546 /// Failsafe - in case nothing was defined. 547 else { 548 $blocknames = 'participants,activity_modules,search_forums,admin,course_list:news_items,calendar_upcoming,recent_activity'; 549 } 550 } 551 } 552 553 return $blocknames; 554 } 555 556 // Given an instance of a block in this page and the direction in which we want to move it, where is 557 // it going to go? Return the identifier of the instance's new position. This allows us to tell blocklib 558 // how we want the blocks to move around in this page in an arbitrarily complex way. If the move as given 559 // does not make sense, make sure to return the instance's original position. 560 // 561 // Since this is going to get called a LOT, pass the instance by reference purely for speed. Do **NOT** 562 // modify its data in any way, this will actually confuse blocklib!!! 563 function blocks_move_position(&$instance, $move) { 564 if($instance->position == BLOCK_POS_LEFT && $move == BLOCK_MOVE_RIGHT) { 565 return BLOCK_POS_RIGHT; 566 } else if ($instance->position == BLOCK_POS_RIGHT && $move == BLOCK_MOVE_LEFT) { 567 return BLOCK_POS_LEFT; 568 } 569 return $instance->position; 570 } 571 } 572 573 /** 574 * Class that models the common parts of all activity modules 575 * 576 * @author Jon Papaioannou 577 * @package pages 578 */ 579 580 class page_generic_activity extends page_base { 581 var $activityname = NULL; 582 var $courserecord = NULL; 583 var $modulerecord = NULL; 584 var $activityrecord = NULL; 585 586 function init_full() { 587 if($this->full_init_done) { 588 return; 589 } 590 if(empty($this->activityname)) { 591 error('Page object derived from page_generic_activity but did not define $this->activityname'); 592 } 593 if (!$this->modulerecord = get_coursemodule_from_instance($this->activityname, $this->id)) { 594 error('Cannot fully initialize page: invalid '.$this->activityname.' instance id '. $this->id); 595 } 596 $this->courserecord = get_record('course', 'id', $this->modulerecord->course); 597 if(empty($this->courserecord)) { 598 error('Cannot fully initialize page: invalid course id '. $this->modulerecord->course); 599 } 600 $this->activityrecord = get_record($this->activityname, 'id', $this->id); 601 if(empty($this->activityrecord)) { 602 error('Cannot fully initialize page: invalid '.$this->activityname.' id '. $this->id); 603 } 604 $this->full_init_done = true; 605 } 606 607 function user_allowed_editing() { 608 $this->init_full(); 609 // Yu: I think this is wrong, should be checking manageactivities instead 610 //return has_capability('moodle/site:manageblocks', get_context_instance(CONTEXT_COURSE, $this->modulerecord->course)); 611 return has_capability('moodle/course:manageactivities', get_context_instance(CONTEXT_MODULE, $this->modulerecord->id)); 612 } 613 614 function user_is_editing() { 615 $this->init_full(); 616 return isediting($this->modulerecord->course); 617 } 618 619 function url_get_path() { 620 global $CFG; 621 return $CFG->wwwroot .'/mod/'.$this->activityname.'/view.php'; 622 } 623 624 function url_get_parameters() { 625 $this->init_full(); 626 return array('id' => $this->modulerecord->id); 627 } 628 629 function blocks_get_positions() { 630 return array(BLOCK_POS_LEFT); 631 } 632 633 function blocks_default_position() { 634 return BLOCK_POS_LEFT; 635 } 636 637 function print_header($title, $morenavlinks = NULL, $bodytags = '', $meta = '') { 638 global $USER, $CFG; 639 640 $this->init_full(); 641 $replacements = array( 642 '%fullname%' => format_string($this->activityrecord->name) 643 ); 644 foreach ($replacements as $search => $replace) { 645 $title = str_replace($search, $replace, $title); 646 } 647 648 if (empty($morenavlinks) && $this->user_allowed_editing()) { 649 $buttons = '<table><tr><td>'.update_module_button($this->modulerecord->id, $this->courserecord->id, get_string('modulename', $this->activityname)).'</td>'; 650 if (!empty($CFG->showblocksonmodpages)) { 651 $buttons .= '<td><form '.$CFG->frametarget.' method="get" action="view.php"><div>'. 652 '<input type="hidden" name="id" value="'.$this->modulerecord->id.'" />'. 653 '<input type="hidden" name="edit" value="'.($this->user_is_editing()?'off':'on').'" />'. 654 '<input type="submit" value="'.get_string($this->user_is_editing()?'blockseditoff':'blocksediton').'" /></div></form></td>'; 655 } 656 $buttons .= '</tr></table>'; 657 } else { 658 $buttons = ' '; 659 } 660 661 if (empty($morenavlinks)) { 662 $morenavlinks = array(); 663 } 664 $navigation = build_navigation($morenavlinks, $this->modulerecord); 665 print_header($title, $this->courserecord->fullname, $navigation, '', $meta, true, $buttons, navmenu($this->courserecord, $this->modulerecord), false, $bodytags); 666 } 667 668 } 669 670 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Wed Jan 14 11:33:29 2009 | Cross-referenced by PHPXref 0.7 |