[ Index ]

PHP Cross Reference of Moodle 1.9.3 [Build 15-Oct-2008]

title

Body

[close]

/lib/ -> pagelib.php (source)

   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? '?' : '&amp;';
 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 : '&nbsp;';
 434  
 435          // Add any extra buttons requested (by the resource module, for example)
 436          if ($extrabuttons != '') {
 437              $buttons = ($buttons == '&nbsp;') ? $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 = '&nbsp;';
 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  ?>


Generated: Wed Jan 14 11:33:29 2009 Cross-referenced by PHPXref 0.7