moodle_page: MDL-12212 reimplement user_is_editing, deprecate isediting
[moodle.git] / lib / pagelib.php
CommitLineData
56eeebdf 1<?php // $Id$
2
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///////////////////////////////////////////////////////////////////////////
f032aa7a 25
e0134f84 26/**
de60de04 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.
e0134f84 30 *
e0134f84 31 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
32 * @package pages
33 */
34
c13a5e71 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 */
42class 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 /**#@-*/
50
753debd2 51/// Field declarations =========================================================
52
c13a5e71 53 protected $_state = self::STATE_BEFORE_HEADER;
54
55 protected $_course = null;
56
2afe21ee 57 protected $_context = null;
58
948203a5 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;
69
753debd2 70 protected $_bodyclasses = array();
71
f230ce19 72 protected $_pagetype = null;
73
82611d8d 74 protected $_docspath = null;
75
d529807a 76 protected $_legacyclass = null;
77
75781f87 78 protected $_url = null;
79
ad52c04f 80 protected $_blocks = null;
81
753debd2 82/// Getter methods =============================================================
ad5d5997 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.
753debd2 85
c13a5e71 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 }
95
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 }
103
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 }
117
948203a5 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 }
130
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 }
141
2afe21ee 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 }
151
f230ce19 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() {
d529807a 156 if (is_null($this->_pagetype) || isset($CFG->pagepath)) {
157 $this->initialise_default_pagetype();
f230ce19 158 }
159 return $this->_pagetype;
160 }
161
753debd2 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 }
168
82611d8d 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 }
179
75781f87 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 }
ad52c04f 189 return new moodle_url($this->_url); // Return a clone for safety.
190 }
191
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;
75781f87 200 }
201
89fbdca3 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 }
213
830dd6e9 214/// Other information getting methods ==========================================
215
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 }
225
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 }
232
753debd2 233/// Setter methods =============================================================
234
c13a5e71 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 }
245
753debd2 246 if ($state == self::STATE_PRINTING_HEADER) {
247 if (!$this->_course) {
248 global $SITE;
249 $this->set_course($SITE);
250 }
251
252 $this->initialise_standard_body_classes();
c13a5e71 253 }
254
255 $this->_state = $state;
256 }
257
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 *
2afe21ee 266 * Sets $PAGE->context to the course context, if it is not already set.
267 *
c13a5e71 268 * @param object the course to set as the global course.
269 */
270 public function set_course($course) {
948203a5 271 global $COURSE;
c13a5e71 272
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 }
276
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 }
280
d7ab8879 281 if (!empty($this->_course->id) && $this->_course->id != $course->id) {
282 $this->_categories = null;
283 }
284
c13a5e71 285 $this->_course = clone($course);
286 $COURSE = $this->_course;
287
2afe21ee 288 if (!$this->_context) {
289 $this->set_context(get_context_instance(CONTEXT_COURSE, $this->_course->id));
290 }
291
c13a5e71 292 moodle_setlocale();
293 theme_setup();
294 }
295
2afe21ee 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 }
303
f230ce19 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 }
315
753debd2 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 }
325
c13a5e71 326 /**
89fbdca3 327 * @param array $classes this utility method calls add_body_class for each array element.
c13a5e71 328 */
89fbdca3 329 public function add_body_classes($classes) {
330 foreach ($classes as $class) {
331 $this->add_body_class($class);
c13a5e71 332 }
333 }
f230ce19 334
948203a5 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 }
355
82611d8d 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 }
366
75781f87 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 }
384
753debd2 385/// Initialisation methods =====================================================
386/// These set various things up in a default way.
387
d529807a 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 */
ad52c04f 396 protected function initialise_default_pagetype($script = null) {
d529807a 397 global $CFG, $SCRIPT;
398
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 }
405
ad52c04f 406 if (is_null($script)) {
d529807a 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 }
413
414 $path = str_replace('.php', '', $script);
415 if (substr($path, -1) == '/') {
416 $path .= 'index';
417 }
418
419 if (empty($path) || $path == 'index') {
420 $this->_pagetype = 'site-index';
421 } else {
422 $this->_pagetype = str_replace('/', '-', $path);
423 }
424 }
425
753debd2 426 protected function initialise_standard_body_classes() {
89fbdca3 427 global $CFG;
428
753debd2 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);
438
439 $this->add_body_class('course-' . $this->_course->id);
89fbdca3 440 $this->add_body_classes(get_browser_version_classes());
753debd2 441 $this->add_body_class('dir-' . get_string('thisdirection'));
442 $this->add_body_class('lang-' . current_language());
443
89fbdca3 444 $this->add_body_class($this->url_to_class_name($CFG->wwwroot));
445
d7ab8879 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 }
463
753debd2 464 if (!isloggedin()) {
465 $this->add_body_class('notloggedin');
466 }
467
468 if (!empty($USER->editing)) {
469 $this->add_body_class('editing');
470 }
471
472 if (!empty($CFG->blocksdrag)) {
473 $this->add_body_class('drag');
474 }
475 }
476
948203a5 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 }
490
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 }
504
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 }
518
89fbdca3 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 }
533
753debd2 534/// Deprecated fields and methods for backwards compatibility ==================
d529807a 535
f230ce19 536 /**
31940ba6 537 * @deprecated since Moodle 2.0 - use $PAGE->pagetype instead.
f230ce19 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 }
d529807a 544
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 }
552
31940ba6 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 }
d529807a 561
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)) {
753debd2 568 $this->initialise_standard_body_classes();
d529807a 569 }
570 debugging('Call to deprecated method moodle_page::get_legacyclass.');
571 return $this->_legacyclass;
572 }
4873f5f7 573
574 /**
ad52c04f 575 * @deprecated since Moodle 2.0 - use $PAGE->blocks->get_positions() instead
4873f5f7 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.');
ad52c04f 580 return $this->blocks->get_positions();
4873f5f7 581 }
582
583 /**
ad52c04f 584 * @deprecated since Moodle 2.0 - use $PAGE->blocks->get_default_position() instead
4873f5f7 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.');
ad52c04f 589 return $this->blocks->get_default_position();
4873f5f7 590 }
591
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 }
598
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 }
ad52c04f 605
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 }
614
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 }
623
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 }
632}
633
634/** Stub implementation of the blocks_manager, to stop things from breaking too badly. */
635class blocks_manager {
636 public function get_positions() {
637 return array(BLOCK_POS_LEFT, BLOCK_POS_RIGHT);
638 }
639
640 public function get_default_position() {
641 return BLOCK_POS_RIGHT;
642 }
c13a5e71 643}
644
de60de04 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 */
3f6aba0c 651function page_import_types($path) {
652 global $CFG;
de60de04 653 debugging('Call to deprecated function page_import_types.', DEBUG_DEVELOPER);
3f6aba0c 654}
655
7c6c0513 656/**
d529807a 657 * @deprecated since Moodle 2.0
658 * @param integer $instance legacy page instance id.
659 * @return the global $PAGE object.
7c6c0513 660 */
7c6c0513 661function page_create_instance($instance) {
d529807a 662 return page_create_object($PAGE->pagetype, $instance);
7c6c0513 663}
664
9b128500 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 */
9b128500 669function page_create_object($type, $id = NULL) {
d7ab8879 670 global $CFG, $PAGE, $SITE;
93dcb13e 671
9b128500 672 $data = new stdClass;
673 $data->pagetype = $type;
674 $data->pageid = $id;
675
676 $classname = page_map_class($type);
ad52c04f 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;
682
d7ab8879 683 $course = $PAGE->course;
684 if ($course->id != $SITE->id) {
ad52c04f 685 $legacypage->set_course($course);
d7ab8879 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) {
ad52c04f 694 $legacypage->set_category_by_id($category->id);
d7ab8879 695 } else {
ad52c04f 696 $legacypage->set_course($SITE);
d7ab8879 697 }
698 }
ad52c04f 699 return $legacypage;
9b128500 700}
701
702/**
5809683f 703 * Function page_map_class() is the way for your code to define its own page subclasses and let Moodle recognize them.
9b128500 704 * Use it to associate the textual identifier of your Page with the actual class name that has to be instantiated.
705 */
9b128500 706function page_map_class($type, $classname = NULL) {
93dcb13e 707 global $CFG;
708
0d7e0e8a 709 static $mappings = NULL;
de60de04 710
93dcb13e 711 if ($mappings === NULL) {
0d7e0e8a 712 $mappings = array(
4f0d565d 713 PAGE_COURSE_VIEW => 'page_course'
0d7e0e8a 714 );
0d7e0e8a 715 }
9b128500 716
93dcb13e 717 if (!empty($type) && !empty($classname)) {
9b128500 718 $mappings[$type] = $classname;
719 }
93dcb13e 720
721 if (!isset($mappings[$type])) {
ea82d6b6 722 debugging('Page class mapping requested for unknown type: '.$type);
9b128500 723 }
724
e15e02bf 725 if (empty($classname) && !class_exists($mappings[$type])) {
ea82d6b6 726 debugging('Page class mapping for id "'.$type.'" exists but class "'.$mappings[$type].'" is not defined');
9b128500 727 }
728
729 return $mappings[$type];
730}
731
f032aa7a 732/**
733 * Parent class from which all Moodle page classes derive
734 *
f032aa7a 735 * @author Jon Papaioannou
736 * @package pages
9b128500 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.
f032aa7a 738 */
c13a5e71 739class page_base extends moodle_page {
e0134f84 740 /**
741 * The string identifier for the type of page being described.
742 * @var string $type
743 */
f032aa7a 744 var $type = NULL;
e0134f84 745
746 /**
747 * The numeric identifier of the page being described.
748 * @var int $id
749 */
f032aa7a 750 var $id = NULL;
e0134f84 751
752 /**
753 * Class bool to determine if the instance's full initialization has been completed.
754 * @var boolean $full_init_done
755 */
f032aa7a 756 var $full_init_done = false;
757
e0134f84 758/// Class Functions
759
c8e0b579 760 // HTML OUTPUT SECTION
761
762 // We have absolutely no idea what derived pages are all about
1d6608d8 763 function print_header($title, $morenavlinks=NULL) {
c8e0b579 764 trigger_error('Page class does not implement method <strong>print_header()</strong>', E_USER_WARNING);
765 return;
766 }
767
c8e0b579 768 // SELF-REPORTING SECTION
769
c8e0b579 770
c8e0b579 771 // Simple stuff, do not override this.
f032aa7a 772 function get_id() {
773 return $this->id;
774 }
c8e0b579 775
c8e0b579 776 // Initialize the data members of the parent class
f032aa7a 777 function init_quick($data) {
778 $this->type = $data->pagetype;
779 $this->id = $data->pageid;
780 }
781
c8e0b579 782 function init_full() {
783 $this->full_init_done = true;
784 }
f032aa7a 785}
786
f032aa7a 787/**
788 * Class that models the behavior of a moodle course
789 *
f032aa7a 790 * @author Jon Papaioannou
791 * @package pages
792 */
99110470 793class page_course extends page_base {
f032aa7a 794
f032aa7a 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) {
12177083 798 if(empty($data->pageid) && !defined('ADMIN_STICKYBLOCKS')) {
775f811a 799 print_error('cannotinitpage', 'debug', '', (object)array('name'=>'course', 'id'=>'?'));
f032aa7a 800 }
801 parent::init_quick($data);
802 }
803
e0134f84 804 // Here you should load up all heavy-duty data for your page. Basically everything that
f032aa7a 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() {
4a991ae3 809 global $COURSE, $DB;
810
f032aa7a 811 if($this->full_init_done) {
812 return;
813 }
12177083 814 if (empty($this->id)) {
815 $this->id = 0; // avoid db errors
816 }
e1e1f498 817
818 $this->context = get_context_instance(CONTEXT_COURSE, $this->id);
819
820 // Preload - ensures that the context cache is populated
821 // in one DB query...
822 $this->childcontexts = get_child_contexts($this->context);
823
824 // Mark we're done
f032aa7a 825 $this->full_init_done = true;
826 }
827
c8e0b579 828 // USER-RELATED THINGS
829
d847bb32 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
e6260a45 832 // update_course_icon() has very similar checks - it must use the same capabilities
e1e1f498 833 //
834 // this is a _very_ expensive check - so cache it during execution
835 //
f032aa7a 836 function user_allowed_editing() {
e1e1f498 837 $this->init_full();
838
839 if (isset($this->_user_allowed_editing)) {
840 return $this->_user_allowed_editing;
841 }
842
843 if (has_capability('moodle/site:manageblocks', get_context_instance(CONTEXT_SYSTEM))
d529807a 844 && defined('ADMIN_STICKYBLOCKS')) {
e1e1f498 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;
12177083 850 return true;
851 }
e1e1f498 852
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 }
f032aa7a 866 }
867
c8e0b579 868 // HTML OUTPUT SECTION
869
870 // This function prints out the common part of the page's header.
f032aa7a 871 // You should NEVER print the header "by hand" in other code.
ca29e37d 872 function print_header($title, $morenavlinks=NULL, $meta='', $bodytags='', $extrabuttons='') {
edb42f09 873 global $USER, $CFG;
874
f032aa7a 875 $this->init_full();
876 $replacements = array(
31940ba6 877 '%fullname%' => $this->course->fullname
f032aa7a 878 );
879 foreach($replacements as $search => $replace) {
880 $title = str_replace($search, $replace, $title);
881 }
de60de04 882
3b27b0fe 883 $navlinks = array();
de60de04 884
3b27b0fe 885 if(!empty($morenavlinks)) {
886 $navlinks = array_merge($navlinks, $morenavlinks);
edb42f09 887 }
888
3b27b0fe 889 $navigation = build_navigation($navlinks);
edb42f09 890
0f256abc 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)
31940ba6 893 $buttons = switchroles_form($this->course->id);
e1e1f498 894 if ($this->user_allowed_editing()) {
31940ba6 895 $buttons .= update_course_icon($this->course->id );
e1e1f498 896 }
3b27b0fe 897 $buttons = empty($morenavlinks) ? $buttons : '&nbsp;';
0f256abc 898
ca29e37d 899 // Add any extra buttons requested (by the resource module, for example)
900 if ($extrabuttons != '') {
901 $buttons = ($buttons == '&nbsp;') ? $extrabuttons : $buttons.$extrabuttons;
902 }
903
31940ba6 904 print_header($title, $this->course->fullname, $navigation,
905 '', $meta, true, $buttons, user_login_string($this->course, $USER), false, $bodytags);
f032aa7a 906 }
907
c8e0b579 908 // SELF-REPORTING SECTION
909
f032aa7a 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
ad52c04f 912 // colons (:) to delimit between block positions in the page.
4873f5f7 913 function _legacy_blocks_get_default() {
f032aa7a 914 global $CFG;
de60de04 915
f032aa7a 916 $this->init_full();
917
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 {
2b156ffb 925 $blocknames = 'site_main_menu,admin_tree:course_summary,calendar_month';
f032aa7a 926 }
927 }
928 // It's a normal course, so do it according to the course format
929 else {
31940ba6 930 $pageformat = $this->course->format;
f032aa7a 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 }
de60de04 951
f032aa7a 952 return $blocknames;
953 }
f032aa7a 954}
955
cadc69c6 956/**
b83eee7d 957 * Class that models the common parts of all activity modules
cadc69c6 958 *
959 * @author Jon Papaioannou
960 * @package pages
961 */
b83eee7d 962class page_generic_activity extends page_base {
963 var $activityname = NULL;
b83eee7d 964 var $modulerecord = NULL;
965 var $activityrecord = NULL;
cadc69c6 966
cadc69c6 967 function init_full() {
4a991ae3 968 global $DB;
969
cadc69c6 970 if($this->full_init_done) {
971 return;
972 }
b83eee7d 973 if(empty($this->activityname)) {
2f137aa1 974 print_error('noactivityname', 'debug');
b83eee7d 975 }
0d7c3b78 976 if (!$this->modulerecord = get_coursemodule_from_instance($this->activityname, $this->id)) {
775f811a 977 print_error('cannotinitpager', 'debug', '', (object)array('name'=>$this->activityname, 'id'=>$this->id));
cadc69c6 978 }
4a991ae3 979 $this->activityrecord = $DB->get_record($this->activityname, array('id'=>$this->id));
71beacf6 980 if(empty($this->activityrecord)) {
775f811a 981 print_error('cannotinitpager', 'debug', '', (object)array('name'=>$this->activityname, 'id'=>$this->id));
cadc69c6 982 }
983 $this->full_init_done = true;
984 }
985
cadc69c6 986 function user_allowed_editing() {
246c2064 987 $this->init_full();
5c91baaa 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));
de60de04 990 return has_capability('moodle/course:manageactivities', get_context_instance(CONTEXT_MODULE, $this->modulerecord->id));
cadc69c6 991 }
992
3b27b0fe 993 function print_header($title, $morenavlinks = NULL, $bodytags = '', $meta = '') {
6db8acb6 994 global $USER, $CFG;
de60de04 995
6db8acb6 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 }
de60de04 1003
3b27b0fe 1004 if (empty($morenavlinks) && $this->user_allowed_editing()) {
31940ba6 1005 $buttons = '<table><tr><td>'.update_module_button($this->modulerecord->id, $this->course->id, get_string('modulename', $this->activityname)).'</td>';
6db8acb6 1006 if (!empty($CFG->showblocksonmodpages)) {
263c81b0 1007 $buttons .= '<td><form '.$CFG->frametarget.' method="get" action="view.php"><div>'.
6db8acb6 1008 '<input type="hidden" name="id" value="'.$this->modulerecord->id.'" />'.
1009 '<input type="hidden" name="edit" value="'.($this->user_is_editing()?'off':'on').'" />'.
263c81b0 1010 '<input type="submit" value="'.get_string($this->user_is_editing()?'blockseditoff':'blocksediton').'" /></div></form></td>';
6db8acb6 1011 }
1012 $buttons .= '</tr></table>';
1013 } else {
1014 $buttons = '&nbsp;';
1015 }
de60de04 1016
38e179a4 1017 if (empty($morenavlinks)) {
1018 $morenavlinks = array();
1019 }
1020 $navigation = build_navigation($morenavlinks, $this->modulerecord);
31940ba6 1021 print_header($title, $this->course->fullname, $navigation, '', $meta, true, $buttons, navmenu($this->course, $this->modulerecord), false, $bodytags);
6db8acb6 1022 }
b83eee7d 1023}
1024
93dcb13e 1025?>