MDL-19791 Fixed call to add_action
[moodle.git] / lib / blocklib.php
CommitLineData
d4accfc0 1<?php
2
a19f419d 3// This file is part of Moodle - http://moodle.org/
4//
d4accfc0 5// Moodle is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// Moodle is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
a19f419d 14//
d4accfc0 15// You should have received a copy of the GNU General Public License
16// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
e8c8cee9 17
18/**
d4accfc0 19 * Block Class and Functions
e8c8cee9 20 *
a19f419d 21 * This file defines the {@link block_manager} class,
d4a03c00 22 *
d4accfc0 23 * @package moodlecore
24 * @copyright 1999 onwards Martin Dougiamas http://dougiamas.com
25 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
e8c8cee9 26 */
0f3fe4b6 27
13a0d3d3 28/**#@+
29 * @deprecated since Moodle 2.0. No longer used.
d4a03c00 30 */
0f3fe4b6 31define('BLOCK_MOVE_LEFT', 0x01);
32define('BLOCK_MOVE_RIGHT', 0x02);
33define('BLOCK_MOVE_UP', 0x04);
34define('BLOCK_MOVE_DOWN', 0x08);
9b4b78fd 35define('BLOCK_CONFIGURE', 0x10);
13a0d3d3 36/**#@-*/
0f3fe4b6 37
13a0d3d3 38/**#@+
39 * Default names for the block regions in the standard theme.
40 */
bb46a4fa 41define('BLOCK_POS_LEFT', 'side-pre');
42define('BLOCK_POS_RIGHT', 'side-post');
13a0d3d3 43/**#@-*/
0e9af917 44
13a0d3d3 45/**#@+
46 * @deprecated since Moodle 2.0. No longer used.
47 */
ee6055eb 48define('BLOCKS_PINNED_TRUE',0);
49define('BLOCKS_PINNED_FALSE',1);
50define('BLOCKS_PINNED_BOTH',2);
13a0d3d3 51/**#@-*/
ee6055eb 52
d4accfc0 53/**
d4a03c00 54 * Exception thrown when someone tried to do something with a block that does
55 * not exist on a page.
d4accfc0 56 *
d4a03c00 57 * @copyright 2009 Tim Hunt
58 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
59 * @since Moodle 2.0
d4accfc0 60 */
f4d38d20 61class block_not_on_page_exception extends moodle_exception {
d4accfc0 62 /**
63 * Contructor
d4a03c00 64 * @param int $instanceid the block instance id of the block that was looked for.
65 * @param object $page the current page.
d4accfc0 66 */
f4d38d20 67 public function __construct($instanceid, $page) {
68 $a = new stdClass;
69 $a->instanceid = $instanceid;
2a3b0763 70 $a->url = $page->url->out();
71 parent::__construct('blockdoesnotexistonpage', '', $page->url->out(), $a);
f4d38d20 72 }
73}
74
86b5ea0f 75/**
76 * This class keeps track of the block that should appear on a moodle_page.
bb46a4fa 77 *
d4a03c00 78 * The page to work with as passed to the constructor.
1d00ec6a 79 *
d4a03c00 80 * @copyright 2009 Tim Hunt
81 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
82 * @since Moodle 2.0
86b5ea0f 83 */
d4a03c00 84class block_manager {
86b5ea0f 85
86/// Field declarations =========================================================
d4a03c00 87
88 /** @var moodle_page the moodle_page we aremanaging blocks for. */
86b5ea0f 89 protected $page;
d4a03c00 90
91 /** @var array region name => 1.*/
86b5ea0f 92 protected $regions = array();
d4a03c00 93
94 /** @var string the region where new blocks are added.*/
95 protected $defaultregion = null;
96
97 /** @var array will be $DB->get_records('blocks') */
98 protected $allblocks = null;
99
100 /**
101 * @var array blocks that this user can add to this page. Will be a subset
a2789e34 102 * of $allblocks, but with array keys block->name. Access this via the
103 * {@link get_addable_blocks()} method to ensure it is lazy-loaded.
d4a03c00 104 */
105 protected $addableblocks = null;
08eab897 106
bb46a4fa 107 /**
108 * Will be an array region-name => array(db rows loaded in load_blocks);
d4accfc0 109 * @var array
bb46a4fa 110 */
111 protected $birecordsbyregion = null;
112
113 /**
114 * array region-name => array(block objects); populated as necessary by
115 * the ensure_instances_exist method.
d4accfc0 116 * @var array
bb46a4fa 117 */
118 protected $blockinstances = array();
119
120 /**
d4a03c00 121 * array region-name => array(block_contents objects) what acutally needs to
bb46a4fa 122 * be displayed in each region.
d4accfc0 123 * @var array
bb46a4fa 124 */
125 protected $visibleblockcontent = array();
08eab897 126
d4a03c00 127 /**
128 * array region-name => array(block_contents objects) extra block-like things
129 * to be displayed in each region, before the real blocks.
130 * @var array
131 */
132 protected $extracontent = array();
133
86b5ea0f 134/// Constructor ================================================================
135
136 /**
137 * Constructor.
138 * @param object $page the moodle_page object object we are managing the blocks for,
139 * or a reasonable faxilimily. (See the comment at the top of this classe
d4accfc0 140 * and {@link http://en.wikipedia.org/wiki/Duck_typing})
86b5ea0f 141 */
142 public function __construct($page) {
143 $this->page = $page;
144 }
145
146/// Getter methods =============================================================
147
148 /**
d4accfc0 149 * Get an array of all region names on this page where a block may appear
150 *
86b5ea0f 151 * @return array the internal names of the regions on this page where block may appear.
152 */
153 public function get_regions() {
d4a03c00 154 $this->page->initialise_theme_and_output();
86b5ea0f 155 return array_keys($this->regions);
156 }
157
158 /**
d4accfc0 159 * Get the region name of the region blocks are added to by default
160 *
86b5ea0f 161 * @return string the internal names of the region where new blocks are added
162 * by default, and where any blocks from an unrecognised region are shown.
163 * (Imagine that blocks were added with one theme selected, then you switched
164 * to a theme with different block positions.)
165 */
166 public function get_default_region() {
d4a03c00 167 $this->page->initialise_theme_and_output();
86b5ea0f 168 return $this->defaultregion;
169 }
170
08eab897 171 /**
172 * The list of block types that may be added to this page.
d4accfc0 173 *
727ae436 174 * @return array block name => record from block table.
08eab897 175 */
176 public function get_addable_blocks() {
177 $this->check_is_loaded();
178
179 if (!is_null($this->addableblocks)) {
180 return $this->addableblocks;
181 }
182
183 // Lazy load.
184 $this->addableblocks = array();
185
186 $allblocks = blocks_get_record();
187 if (empty($allblocks)) {
188 return $this->addableblocks;
189 }
190
bb46a4fa 191 $pageformat = $this->page->pagetype;
08eab897 192 foreach($allblocks as $block) {
193 if ($block->visible &&
bb46a4fa 194 (block_method_result($block->name, 'instance_allow_multiple') || !$this->is_block_present($block->id)) &&
a2789e34 195 blocks_name_allowed_in_format($block->name, $pageformat) &&
196 block_method_result($block->name, 'user_can_addto', $this->page)) {
197 $this->addableblocks[$block->name] = $block;
08eab897 198 }
199 }
200
201 return $this->addableblocks;
202 }
203
d4accfc0 204 /**
205 * Find out if a block is present ? just a guess
206 * @todo Write this function and document
207 */
08eab897 208 public function is_block_present($blocktypeid) {
209 // TODO
210 }
211
212 /**
d4accfc0 213 * Find out if a block type is known by the system
214 *
08eab897 215 * @param string $blockname the name of ta type of block.
216 * @param boolean $includeinvisible if false (default) only check 'visible' blocks, that is, blocks enabled by the admin.
217 * @return boolean true if this block in installed.
218 */
219 public function is_known_block_type($blockname, $includeinvisible = false) {
220 $blocks = $this->get_installed_blocks();
221 foreach ($blocks as $block) {
222 if ($block->name == $blockname && ($includeinvisible || $block->visible)) {
223 return true;
224 }
225 }
226 return false;
227 }
228
229 /**
d4accfc0 230 * Find out if a region exists on a page
231 *
08eab897 232 * @param string $region a region name
233 * @return boolean true if this retion exists on this page.
234 */
235 public function is_known_region($region) {
236 return array_key_exists($region, $this->regions);
237 }
238
239 /**
d4accfc0 240 * Get an array of all blocks within a given region
241 *
242 * @param string $region a block region that exists on this page.
08eab897 243 * @return array of block instances.
244 */
245 public function get_blocks_for_region($region) {
246 $this->check_is_loaded();
bb46a4fa 247 $this->ensure_instances_exist($region);
248 return $this->blockinstances[$region];
249 }
250
251 /**
d4accfc0 252 * Returns an array of block content objects that exist in a region
253 *
d4a03c00 254 * @param string $region a block region that exists on this page.
255 * @return array of block block_contents objects for all the blocks in a region.
bb46a4fa 256 */
d4a03c00 257 public function get_content_for_region($region, $output) {
bb46a4fa 258 $this->check_is_loaded();
d4a03c00 259 $this->ensure_content_created($region, $output);
bb46a4fa 260 return $this->visibleblockcontent[$region];
08eab897 261 }
262
d4a03c00 263 /**
264 * Determine whether a region contains anything. (Either any real blocks, or
265 * the add new block UI.)
78d27a90 266 *
267 * (You may wonder why the $output parameter is required. Unfortunately,
268 * becuase of the way that blocks work, the only reliable way to find out
269 * if a block will be visible is to get the content for output, and to
270 * get the content, you need a renderer. Fortunately, this is not a
271 * performance problem, becuase we cache the output that is generated, and
272 * in almost every case where we call region_has_content, we are about to
273 * output the blocks anyway, so we are not doing wasted effort.)
274 *
d4a03c00 275 * @param string $region a block region that exists on this page.
a19f419d 276 * @param object $output a moodle_core_renderer. normally the global $OUTPUT.
d4a03c00 277 * @return boolean Whether there is anything in this region.
278 */
78d27a90 279 public function region_has_content($region, $output) {
d4a03c00 280 if (!$this->is_known_region($region)) {
281 return false;
282 }
283 $this->check_is_loaded();
78d27a90 284 $this->ensure_content_created($region, $output);
d4a03c00 285 if ($this->page->user_is_editing() && $this->page->user_can_edit_blocks()) {
286 // If editing is on, we need all the block regions visible, for the
287 // move blocks UI.
288 return true;
289 }
78d27a90 290 return !empty($this->visibleblockcontent[$region]) || !empty($this->extracontent[$region]);
d4a03c00 291 }
292
08eab897 293 /**
d4accfc0 294 * Get an array of all of the installed blocks.
295 *
08eab897 296 * @return array contents of the block table.
297 */
298 public function get_installed_blocks() {
299 global $DB;
300 if (is_null($this->allblocks)) {
301 $this->allblocks = $DB->get_records('block');
302 }
303 return $this->allblocks;
304 }
305
86b5ea0f 306/// Setter methods =============================================================
307
308 /**
d4accfc0 309 * Add a region to a page
310 *
86b5ea0f 311 * @param string $region add a named region where blocks may appear on the
312 * current page. This is an internal name, like 'side-pre', not a string to
313 * display in the UI.
314 */
315 public function add_region($region) {
316 $this->check_not_yet_loaded();
317 $this->regions[$region] = 1;
318 }
319
320 /**
d4accfc0 321 * Add an array of regions
322 * @see add_region()
323 *
86b5ea0f 324 * @param array $regions this utility method calls add_region for each array element.
325 */
326 public function add_regions($regions) {
327 foreach ($regions as $region) {
328 $this->add_region($region);
329 }
330 }
331
332 /**
d4accfc0 333 * Set the default region for new blocks on the page
334 *
86b5ea0f 335 * @param string $defaultregion the internal names of the region where new
336 * blocks should be added by default, and where any blocks from an
337 * unrecognised region are shown.
338 */
339 public function set_default_region($defaultregion) {
340 $this->check_not_yet_loaded();
08eab897 341 $this->check_region_is_known($defaultregion);
86b5ea0f 342 $this->defaultregion = $defaultregion;
343 }
344
d4a03c00 345 /**
346 * Add something that looks like a block, but which isn't an actual block_instance,
347 * to this page.
348 *
349 * @param block_contents $bc the content of the block like thing.
350 * @param string $region a block region that exists on this page.
351 */
352 public function add_pretend_block($bc, $region) {
353 $this->page->initialise_theme_and_output();
354 $this->check_region_is_known($region);
355 if (array_key_exists($region, $this->visibleblockcontent)) {
356 throw new coding_exception('block_manager has already prepared the blocks in region ' .
357 $region . 'for output. It is too late to add a pretend block.');
358 }
359 $this->extracontent[$region][] = $bc;
360 }
361
08eab897 362/// Actions ====================================================================
363
364 /**
365 * This method actually loads the blocks for our page from the database.
d4accfc0 366 *
ae42ff6f 367 * @param boolean|null $includeinvisible
368 * null (default) - load hidden blocks if $this->page->user_is_editing();
369 * true - load hidden blocks.
370 * false - don't load hidden blocks.
08eab897 371 */
ae42ff6f 372 public function load_blocks($includeinvisible = null) {
d19e8195 373 global $DB, $CFG;
bb46a4fa 374 if (!is_null($this->birecordsbyregion)) {
375 // Already done.
376 return;
377 }
08eab897 378
d19e8195 379 if ($CFG->version < 2009050619) {
380 // Upgrade/install not complete. Don't try too show any blocks.
381 $this->birecordsbyregion = array();
382 return;
383 }
384
d4a03c00 385 // Ensure we have been initialised.
b7009474 386 if (!isset($this->defaultregion)) {
387 $this->page->initialise_theme_and_output();
d4a03c00 388 // If there are still no block regions, then there are no blocks on this page.
389 if (empty($this->regions)) {
390 $this->birecordsbyregion = array();
391 return;
392 }
b7009474 393 }
394
1d13c75c 395 // The code here needs to be consistent with the code in block_load_for_page.
08eab897 396 if (is_null($includeinvisible)) {
397 $includeinvisible = $this->page->user_is_editing();
398 }
399 if ($includeinvisible) {
08eab897 400 $visiblecheck = '';
ae42ff6f 401 } else {
402 $visiblecheck = 'AND (bp.visible = 1 OR bp.visible IS NULL)';
08eab897 403 }
404
405 $context = $this->page->context;
13a0d3d3 406 $contexttest = 'bi.parentcontextid = :contextid2';
08eab897 407 $parentcontextparams = array();
408 $parentcontextids = get_parent_contexts($context);
409 if ($parentcontextids) {
410 list($parentcontexttest, $parentcontextparams) =
411 $DB->get_in_or_equal($parentcontextids, SQL_PARAMS_NAMED, 'parentcontext0000');
13a0d3d3 412 $contexttest = "($contexttest OR (bi.showinsubcontexts = 1 AND bi.parentcontextid $parentcontexttest))";
08eab897 413 }
414
1d13c75c 415 $pagetypepatterns = matching_page_type_patterns($this->page->pagetype);
08eab897 416 list($pagetypepatterntest, $pagetypepatternparams) =
417 $DB->get_in_or_equal($pagetypepatterns, SQL_PARAMS_NAMED, 'pagetypepatterntest0000');
418
419 $params = array(
420 'subpage1' => $this->page->subpage,
421 'subpage2' => $this->page->subpage,
422 'contextid1' => $context->id,
423 'contextid2' => $context->id,
424 'pagetype' => $this->page->pagetype,
e92c286c 425 'contextblock' => CONTEXT_BLOCK,
08eab897 426 );
427 $sql = "SELECT
428 bi.id,
d4a03c00 429 bp.id AS blockpositionid,
08eab897 430 bi.blockname,
13a0d3d3 431 bi.parentcontextid,
08eab897 432 bi.showinsubcontexts,
433 bi.pagetypepattern,
434 bi.subpagepattern,
ae42ff6f 435 bi.defaultregion,
436 bi.defaultweight,
bb46a4fa 437 COALESCE(bp.visible, 1) AS visible,
08eab897 438 COALESCE(bp.region, bi.defaultregion) AS region,
439 COALESCE(bp.weight, bi.defaultweight) AS weight,
e92c286c 440 bi.configdata,
441 ctx.id AS ctxid,
442 ctx.path AS ctxpath,
443 ctx.depth AS ctxdepth,
444 ctx.contextlevel AS ctxlevel
08eab897 445
446 FROM {block_instances} bi
447 JOIN {block} b ON bi.blockname = b.name
448 LEFT JOIN {block_positions} bp ON bp.blockinstanceid = bi.id
449 AND bp.contextid = :contextid1
450 AND bp.pagetype = :pagetype
451 AND bp.subpage = :subpage1
e92c286c 452 JOIN {context} ctx ON ctx.contextlevel = :contextblock
453 AND ctx.instanceid = bi.id
08eab897 454
455 WHERE
456 $contexttest
457 AND bi.pagetypepattern $pagetypepatterntest
458 AND (bi.subpagepattern IS NULL OR bi.subpagepattern = :subpage2)
459 $visiblecheck
460 AND b.visible = 1
461
462 ORDER BY
463 COALESCE(bp.region, bi.defaultregion),
464 COALESCE(bp.weight, bi.defaultweight),
465 bi.id";
466 $blockinstances = $DB->get_recordset_sql($sql, $params + $parentcontextparams + $pagetypepatternparams);
467
bb46a4fa 468 $this->birecordsbyregion = $this->prepare_per_region_arrays();
08eab897 469 $unknown = array();
08eab897 470 foreach ($blockinstances as $bi) {
e92c286c 471 $bi = make_context_subobj($bi);
08eab897 472 if ($this->is_known_region($bi->region)) {
bb46a4fa 473 $this->birecordsbyregion[$bi->region][] = $bi;
08eab897 474 } else {
475 $unknown[] = $bi;
476 }
477 }
d4a03c00 478
479 // Pages don't necessarily have a defaultregion. The one time this can
480 // happen is when there are no theme block regions, but the script itself
481 // has a block region in the main content area.
482 if (!empty($this->defaultregion)) {
483 $this->birecordsbyregion[$this->defaultregion] =
484 array_merge($this->birecordsbyregion[$this->defaultregion], $unknown);
485 }
08eab897 486 }
487
488 /**
489 * Add a block to the current page, or related pages. The block is added to
490 * context $this->page->contextid. If $pagetypepattern $subpagepattern
d4accfc0 491 *
08eab897 492 * @param string $blockname The type of block to add.
493 * @param string $region the block region on this page to add the block to.
494 * @param integer $weight determines the order where this block appears in the region.
495 * @param boolean $showinsubcontexts whether this block appears in subcontexts, or just the current context.
496 * @param string|null $pagetypepattern which page types this block should appear on. Defaults to just the current page type.
497 * @param string|null $subpagepattern which subpage this block should appear on. NULL = any (the default), otherwise only the specified subpage.
498 */
499 public function add_block($blockname, $region, $weight, $showinsubcontexts, $pagetypepattern = NULL, $subpagepattern = NULL) {
500 global $DB;
501 $this->check_known_block_type($blockname);
502 $this->check_region_is_known($region);
503
504 if (empty($pagetypepattern)) {
505 $pagetypepattern = $this->page->pagetype;
506 }
507
508 $blockinstance = new stdClass;
509 $blockinstance->blockname = $blockname;
13a0d3d3 510 $blockinstance->parentcontextid = $this->page->context->id;
08eab897 511 $blockinstance->showinsubcontexts = !empty($showinsubcontexts);
512 $blockinstance->pagetypepattern = $pagetypepattern;
513 $blockinstance->subpagepattern = $subpagepattern;
514 $blockinstance->defaultregion = $region;
515 $blockinstance->defaultweight = $weight;
516 $blockinstance->configdata = '';
feed1900 517 $blockinstance->id = $DB->insert_record('block_instances', $blockinstance);
518
e92c286c 519 // Ensure the block context is created.
520 get_context_instance(CONTEXT_BLOCK, $blockinstance->id);
e03c0c1d 521
feed1900 522 // If the new instance was created, allow it to do additional setup
e92c286c 523 if ($block = block_instance($blockname, $blockinstance)) {
feed1900 524 $block->instance_create();
525 }
08eab897 526 }
527
21d33bdf 528 public function add_block_at_end_of_default_region($blockname) {
529 $defaulregion = $this->get_default_region();
2a3b0763 530
21d33bdf 531 $lastcurrentblock = end($this->birecordsbyregion[$defaulregion]);
2a3b0763 532 if ($lastcurrentblock) {
533 $weight = $lastcurrentblock->weight + 1;
534 } else {
535 $weight = 0;
536 }
537
21d33bdf 538 if ($this->page->subpage) {
539 $subpage = $this->page->subpage;
540 } else {
541 $subpage = null;
542 }
a2789e34 543
544 // Special case. Course view page type include the course format, but we
545 // want to add the block non-format-specifically.
546 $pagetypepattern = $this->page->pagetype;
547 if (strpos($pagetypepattern, 'course-view') === 0) {
548 $pagetypepattern = 'course-view-*';
549 }
550
2a3b0763 551 $this->add_block($blockname, $defaulregion, $weight, false, $pagetypepattern, $subpage);
21d33bdf 552 }
553
9d1d606e 554 /**
555 * Convenience method, calls add_block repeatedly for all the blocks in $blocks.
d4accfc0 556 *
2a3b0763 557 * @param array $blocks array with array keys the region names, and values an array of block names.
9d1d606e 558 * @param string $pagetypepattern optional. Passed to @see add_block()
559 * @param string $subpagepattern optional. Passed to @see add_block()
560 */
561 public function add_blocks($blocks, $pagetypepattern = NULL, $subpagepattern = NULL) {
562 $this->add_regions(array_keys($blocks));
563 foreach ($blocks as $region => $regionblocks) {
564 $weight = 0;
565 foreach ($regionblocks as $blockname) {
566 $this->add_block($blockname, $region, $weight, false, $pagetypepattern, $subpagepattern);
567 $weight += 1;
568 }
569 }
570 }
571
f4d38d20 572 /**
a19f419d 573 * Find a given block by its instance id
d4accfc0 574 *
f4d38d20 575 * @param integer $instanceid
d4accfc0 576 * @return object
f4d38d20 577 */
578 public function find_instance($instanceid) {
579 foreach ($this->regions as $region => $notused) {
580 $this->ensure_instances_exist($region);
581 foreach($this->blockinstances[$region] as $instance) {
582 if ($instance->instance->id == $instanceid) {
583 return $instance;
584 }
585 }
586 }
587 throw new block_not_on_page_exception($instanceid, $this->page);
588 }
589
86b5ea0f 590/// Inner workings =============================================================
591
d4accfc0 592 /**
593 * Check whether the page blocks have been loaded yet
594 *
595 * @return void Throws coding exception if already loaded
596 */
86b5ea0f 597 protected function check_not_yet_loaded() {
bb46a4fa 598 if (!is_null($this->birecordsbyregion)) {
86b5ea0f 599 throw new coding_exception('block_manager has already loaded the blocks, to it is too late to change things that might affect which blocks are visible.');
600 }
601 }
602
d4accfc0 603 /**
604 * Check whether the page blocks have been loaded yet
605 *
606 * Nearly identical to the above function {@link check_not_yet_loaded()} except different message
607 *
608 * @return void Throws coding exception if already loaded
609 */
08eab897 610 protected function check_is_loaded() {
bb46a4fa 611 if (is_null($this->birecordsbyregion)) {
08eab897 612 throw new coding_exception('block_manager has not yet loaded the blocks, to it is too soon to request the information you asked for.');
613 }
614 }
615
d4accfc0 616 /**
617 * Check if a block type is known and usable
618 *
619 * @param string $blockname The block type name to search for
620 * @param bool $includeinvisible Include disabled block types in the intial pass
621 * @return void Coding Exception thrown if unknown or not enabled
622 */
08eab897 623 protected function check_known_block_type($blockname, $includeinvisible = false) {
624 if (!$this->is_known_block_type($blockname, $includeinvisible)) {
625 if ($this->is_known_block_type($blockname, true)) {
626 throw new coding_exception('Unknown block type ' . $blockname);
627 } else {
628 throw new coding_exception('Block type ' . $blockname . ' has been disabled by the administrator.');
629 }
630 }
631 }
632
d4accfc0 633 /**
634 * Check if a region is known by its name
635 *
636 * @param string $region
637 * @return void Coding Exception thrown if the region is not known
638 */
08eab897 639 protected function check_region_is_known($region) {
640 if (!$this->is_known_region($region)) {
641 throw new coding_exception('Trying to reference an unknown block region ' . $region);
642 }
86b5ea0f 643 }
bb46a4fa 644
645 /**
d4accfc0 646 * Returns an array of region names as keys and nested arrays for values
647 *
bb46a4fa 648 * @return array an array where the array keys are the region names, and the array
649 * values are empty arrays.
650 */
651 protected function prepare_per_region_arrays() {
652 $result = array();
653 foreach ($this->regions as $region => $notused) {
654 $result[$region] = array();
655 }
656 return $result;
657 }
658
d4accfc0 659 /**
660 * Create a set of new block instance from a record array
661 *
662 * @param array $birecords An array of block instance records
663 * @return array An array of instantiated block_instance objects
664 */
bb46a4fa 665 protected function create_block_instances($birecords) {
666 $results = array();
667 foreach ($birecords as $record) {
668 $results[] = block_instance($record->blockname, $record, $this->page);
669 }
670 return $results;
671 }
672
4578a5eb 673 /**
674 * Create all the bock instances for all the blocks that were loaded by
675 * load_blocks. This is used, for example, to ensure that all blocks get a
676 * chance to initialise themselves via the {@link block_base::specialize()}
677 * method, before any output is done.
678 */
679 public function create_all_block_instances() {
680 foreach ($this->get_regions() as $region) {
681 $this->ensure_instances_exist($region);
682 }
683 }
684
d4accfc0 685 /**
686 * Return an array of content vars from a set of block instances
687 *
688 * @param array $instances An array of block instances
689 * @return array An array of content vars
690 */
d4a03c00 691 protected function create_block_contents($instances, $output) {
bb46a4fa 692 $results = array();
693 foreach ($instances as $instance) {
d4a03c00 694 $content = $instance->get_content_for_output($output);
bb46a4fa 695 if (!empty($content)) {
696 $results[] = $content;
697 }
698 }
699 return $results;
700 }
701
d4accfc0 702 /**
703 * Ensure block instances exist for a given region
a19f419d 704 *
d4accfc0 705 * @param string $region Check for bi's with the instance with this name
706 */
bb46a4fa 707 protected function ensure_instances_exist($region) {
708 $this->check_region_is_known($region);
709 if (!array_key_exists($region, $this->blockinstances)) {
710 $this->blockinstances[$region] =
711 $this->create_block_instances($this->birecordsbyregion[$region]);
712 }
713 }
714
d4accfc0 715 /**
716 * Ensure that there is some content within the given region
717 *
718 * @param string $region The name of the region to check
719 */
d4a03c00 720 protected function ensure_content_created($region, $output) {
bb46a4fa 721 $this->ensure_instances_exist($region);
722 if (!array_key_exists($region, $this->visibleblockcontent)) {
d4a03c00 723 $contents = array();
724 if (array_key_exists($region, $this->extracontent)) {
725 $contents = $this->extracontent[$region];
726 }
727 $contents = array_merge($contents, $this->create_block_contents($this->blockinstances[$region], $output));
728 if ($region == $this->defaultregion) {
21d33bdf 729 $addblockui = block_add_block_ui($this->page, $output);
d4a03c00 730 if ($addblockui) {
731 $contents[] = $addblockui;
732 }
733 }
734 $this->visibleblockcontent[$region] = $contents;
bb46a4fa 735 }
736 }
a19f419d 737
738/// Process actions from the URL ===============================================
739
740 /**
741 * Process any block actions that were specified in the URL.
742 *
743 * This can only be done given a valid $page object.
744 *
745 * @param moodle_page $page the page to add blocks to.
746 * @return boolean true if anything was done. False if not.
747 */
748 public function process_url_actions() {
749 return $this->process_url_add() || $this->process_url_delete() ||
750 $this->process_url_show_hide() || $this->process_url_edit();
751 }
752
753 /**
754 * Handle adding a block.
755 * @return boolean true if anything was done. False if not.
756 */
757 public function process_url_add() {
758 $blocktype = optional_param('bui_addblock', null, PARAM_SAFEDIR);
759 if (!$blocktype) {
760 return false;
761 }
762
763 confirm_sesskey();
764
d0f0cab7 765 if (!$this->page->user_is_editing() && !$this->page->user_can_edit_blocks()) {
a19f419d 766 throw new moodle_exception('nopermissions', '', $this->page->url->out(), get_string('addblock'));
767 }
768
769 if (!array_key_exists($blocktype, $this->get_addable_blocks())) {
770 throw new moodle_exception('cannotaddthisblocktype', '', $this->page->url->out(), $blocktype);
771 }
772
773 $this->add_block_at_end_of_default_region($blocktype);
774
775 // If the page URL was a guses, it will contain the bui_... param, so we must make sure it is not there.
776 $this->page->ensure_param_not_in_url('bui_addblock');
777
778 return true;
779 }
780
781 /**
782 * Handle deleting a block.
783 * @return boolean true if anything was done. False if not.
784 */
785 public function process_url_delete() {
786 $blockid = optional_param('bui_deleteid', null, PARAM_INTEGER);
787 if (!$blockid) {
788 return false;
789 }
790
791 confirm_sesskey();
792
793 $block = $this->page->blocks->find_instance($blockid);
794
7bbc2890 795 if (!$block->user_can_edit() || !$this->page->user_can_edit_blocks() || !$block->user_can_addto($this->page)) {
a19f419d 796 throw new moodle_exception('nopermissions', '', $this->page->url->out(), get_string('deleteablock'));
797 }
798
799 blocks_delete_instance($block->instance);
800
801 // If the page URL was a guses, it will contain the bui_... param, so we must make sure it is not there.
802 $this->page->ensure_param_not_in_url('bui_deleteid');
803
804 return true;
805 }
806
807 /**
808 * Handle showing or hiding a block.
809 * @return boolean true if anything was done. False if not.
810 */
811 public function process_url_show_hide() {
812 if ($blockid = optional_param('bui_hideid', null, PARAM_INTEGER)) {
813 $newvisibility = 0;
814 } else if ($blockid = optional_param('bui_showid', null, PARAM_INTEGER)) {
815 $newvisibility = 1;
816 } else {
817 return false;
818 }
819
820 confirm_sesskey();
821
822 $block = $this->page->blocks->find_instance($blockid);
823
d14edf06 824 if (!$this->page->user_can_edit_blocks()) {
a19f419d 825 throw new moodle_exception('nopermissions', '', $this->page->url->out(), get_string('hideshowblocks'));
826 }
827
828 blocks_set_visibility($block->instance, $this->page, $newvisibility);
829
830 // If the page URL was a guses, it will contain the bui_... param, so we must make sure it is not there.
831 $this->page->ensure_param_not_in_url('bui_hideid');
832 $this->page->ensure_param_not_in_url('bui_showid');
833
834 return true;
835 }
836
837 /**
838 * Handle showing/processing the submission from the block editing form.
839 * @return boolean true if the form was submitted and the new config saved. Does not
840 * return if the editing form was displayed. False otherwise.
841 */
842 public function process_url_edit() {
843 global $CFG, $DB, $PAGE;
844
845 $blockid = optional_param('bui_editid', null, PARAM_INTEGER);
846 if (!$blockid) {
847 return false;
848 }
849
850 confirm_sesskey();
851 require_once($CFG->dirroot . '/blocks/edit_form.php');
852
853 $block = $this->find_instance($blockid);
854
d14edf06 855 if (!$block->user_can_edit() && !$this->page->user_can_edit_blocks()) {
a19f419d 856 throw new moodle_exception('nopermissions', '', $this->page->url->out(), get_string('editblock'));
857 }
858
859 $editpage = new moodle_page();
860 $editpage->set_generaltype('form');
861 $editpage->set_course($this->page->course);
7bbc2890 862 $editpage->set_context($block->context);
a19f419d 863 $editurlbase = str_replace($CFG->wwwroot . '/', '', $this->page->url->out(true));
864 $editurlparams = $this->page->url->params();
865 $editurlparams['bui_editid'] = $blockid;
866 $editpage->set_url($editurlbase, $editurlparams);
867 $editpage->_block_actions_done = true;
868 // At this point we are either going to redirect, or display the form, so
869 // overwrite global $PAGE ready for this. (Formslib refers to it.)
870 $PAGE = $editpage;
871
872 $formfile = $CFG->dirroot . '/blocks/' . $block->name() . '/edit_form.php';
873 if (is_readable($formfile)) {
874 require_once($formfile);
875 $classname = 'block_' . $block->name() . '_edit_form';
876 } else {
877 $classname = 'block_edit_form';
878 }
879
880 $mform = new $classname($editpage->url, $block, $this->page);
881 $mform->set_data($block->instance);
882
883 if ($mform->is_cancelled()) {
884 redirect($this->page->url);
885
886 } else if ($data = $mform->get_data()) {
887 $bi = new stdClass;
888 $bi->id = $block->instance->id;
889 $bi->showinsubcontexts = $data->bui_showinsubcontexts;
890 $bi->pagetypepattern = $data->bui_pagetypepattern;
891 if (empty($data->bui_subpagepattern) || $data->bui_subpagepattern == '%@NULL@%') {
892 $bi->subpagepattern = null;
893 } else {
894 $bi->subpagepattern = $data->bui_subpagepattern;
895 }
896 $bi->defaultregion = $data->bui_defaultregion;
897 $bi->defaultweight = $data->bui_defaultweight;
898 $DB->update_record('block_instances', $bi);
899
a23bbaa3 900 if (!empty($block->config)) {
901 $config = clone($block->config);
902 } else {
903 $config = new stdClass;
904 }
a19f419d 905 foreach ($data as $configfield => $value) {
906 if (strpos($configfield, 'config_') !== 0) {
907 continue;
908 }
909 $field = substr($configfield, 7);
910 $config->$field = $value;
911 }
912 $block->instance_config_save($config);
913
914 $bp = new stdClass;
915 $bp->visible = $data->bui_visible;
916 $bp->region = $data->bui_region;
917 $bp->weight = $data->bui_weight;
918 $needbprecord = !$data->bui_visible || $data->bui_region != $data->bui_defaultregion ||
919 $data->bui_weight != $data->bui_defaultweight;
920
921 if ($block->instance->blockpositionid && !$needbprecord) {
922 $DB->delete_records('block_positions', array('id' => $block->instance->blockpositionid));
923
924 } else if ($block->instance->blockpositionid && $needbprecord) {
925 $bp->id = $block->instance->blockpositionid;
926 $DB->update_record('block_positions', $bp);
927
928 } else if ($needbprecord) {
929 $bp->blockinstanceid = $block->instance->id;
a23bbaa3 930 $bp->contextid = $this->page->context->id;
a19f419d 931 $bp->pagetype = $this->page->pagetype;
932 if ($this->page->subpage) {
933 $bp->subpage = $this->page->subpage;
934 } else {
a23bbaa3 935 $bp->subpage = '';
a19f419d 936 }
937 $DB->insert_record('block_positions', $bp);
938 }
939
940 redirect($this->page->url);
941
942 } else {
943 $strheading = get_string('editinga', $block->name());
944 if (strpos($strheading, '[[') === 0) {
945 $strheading = get_string('blockconfiga', 'moodle', $block->get_title());
946 }
947
948 $editpage->set_title($strheading);
949 $editpage->set_heading($strheading);
950
951 $output = $editpage->theme->get_renderer('core', $editpage);
952 echo $output->header();
953 echo $output->heading($strheading, 2);
954 $mform->display();
955 echo $output->footer();
956 exit;
957 }
958 }
86b5ea0f 959}
960
08eab897 961/// Helper functions for working with block classes ============================
962
963/**
964 * Call a class method (one that does not requrie a block instance) on a block class.
d4accfc0 965 *
08eab897 966 * @param string $blockname the name of the block.
967 * @param string $method the method name.
968 * @param array $param parameters to pass to the method.
969 * @return mixed whatever the method returns.
970 */
11306331 971function block_method_result($blockname, $method, $param = NULL) {
0f3fe4b6 972 if(!block_load_class($blockname)) {
973 return NULL;
974 }
11306331 975 return call_user_func(array('block_'.$blockname, $method), $param);
0f3fe4b6 976}
977
1d13c75c 978/**
979 * Load a block instance, with position information about where that block appears
980 * on a given page.
981 *
982 * @param integer$blockid the block_instance.id.
983 * @param moodle_page $page the page the block is appearing on.
984 * @return block_base the requested block.
985 */
986function block_load_for_page($blockid, $page) {
987 global $DB;
988
989 // The code here needs to be consistent with the code in block_manager::load_blocks.
990 $params = array(
991 'blockinstanceid' => $blockid,
992 'subpage' => $page->subpage,
993 'contextid' => $page->context->id,
994 'pagetype' => $page->pagetype,
995 'contextblock' => CONTEXT_BLOCK,
996 );
997 $sql = "SELECT
998 bi.id,
999 bp.id AS blockpositionid,
1000 bi.blockname,
1001 bi.parentcontextid,
1002 bi.showinsubcontexts,
1003 bi.pagetypepattern,
1004 bi.subpagepattern,
1005 bi.defaultregion,
1006 bi.defaultweight,
1007 COALESCE(bp.visible, 1) AS visible,
1008 COALESCE(bp.region, bi.defaultregion) AS region,
1009 COALESCE(bp.weight, bi.defaultweight) AS weight,
1010 bi.configdata,
1011 ctx.id AS ctxid,
1012 ctx.path AS ctxpath,
1013 ctx.depth AS ctxdepth,
1014 ctx.contextlevel AS ctxlevel
1015
1016 FROM {block_instances} bi
1017 JOIN {block} b ON bi.blockname = b.name
1018 LEFT JOIN {block_positions} bp ON bp.blockinstanceid = bi.id
1019 AND bp.contextid = :contextid
1020 AND bp.pagetype = :pagetype
1021 AND bp.subpage = :subpage
1022 JOIN {context} ctx ON ctx.contextlevel = :contextblock
1023 AND ctx.instanceid = bi.id
1024
1025 WHERE
1026 bi.id = :blockinstanceid
1027 AND b.visible = 1
1028
1029 ORDER BY
1030 COALESCE(bp.region, bi.defaultregion),
1031 COALESCE(bp.weight, bi.defaultweight),
1032 bi.id";
1033 $bi = $DB->get_record_sql($sql, $params, MUST_EXIST);
1034 $bi = make_context_subobj($bi);
1035 return block_instance($bi->blockname, $bi, $page);
1036}
1037
08eab897 1038/**
1039 * Creates a new object of the specified block class.
d4accfc0 1040 *
08eab897 1041 * @param string $blockname the name of the block.
1042 * @param $instance block_instances DB table row (optional).
bb46a4fa 1043 * @param moodle_page $page the page this block is appearing on.
08eab897 1044 * @return block_base the requested block instance.
1045 */
bb46a4fa 1046function block_instance($blockname, $instance = NULL, $page = NULL) {
0f3fe4b6 1047 if(!block_load_class($blockname)) {
1048 return false;
1049 }
e89d741a 1050 $classname = 'block_'.$blockname;
f032aa7a 1051 $retval = new $classname;
9b4b78fd 1052 if($instance !== NULL) {
bb46a4fa 1053 if (is_null($page)) {
1054 global $PAGE;
1055 $page = $PAGE;
1056 }
1057 $retval->_load_instance($instance, $page);
9b4b78fd 1058 }
1059 return $retval;
0f3fe4b6 1060}
1061
08eab897 1062/**
1063 * Load the block class for a particular type of block.
d4accfc0 1064 *
08eab897 1065 * @param string $blockname the name of the block.
1066 * @return boolean success or failure.
1067 */
0f3fe4b6 1068function block_load_class($blockname) {
1069 global $CFG;
1070
a9033ad5 1071 if(empty($blockname)) {
c7a9e293 1072 return false;
1073 }
1074
e89d741a 1075 $classname = 'block_'.$blockname;
a9033ad5 1076
1077 if(class_exists($classname)) {
1078 return true;
1079 }
1080
1081 require_once($CFG->dirroot.'/blocks/moodleblock.class.php');
e9a20759 1082 @include_once($CFG->dirroot.'/blocks/'.$blockname.'/block_'.$blockname.'.php'); // do not throw errors if block code not present
0f3fe4b6 1083
0f3fe4b6 1084 return class_exists($classname);
1085}
1086
1d13c75c 1087/**
1088 * Given a specific page type, return all the page type patterns that might
1089 * match it.
1090 *
1091 * @param string $pagetype for example 'course-view-weeks' or 'mod-quiz-view'.
1092 * @return array an array of all the page type patterns that might match this page type.
1093 */
1094function matching_page_type_patterns($pagetype) {
1095 $patterns = array($pagetype);
1096 $bits = explode('-', $pagetype);
1097 if (count($bits) == 3 && $bits[0] == 'mod') {
1098 if ($bits[2] == 'view') {
1099 $patterns[] = 'mod-*-view';
1100 } else if ($bits[2] == 'index') {
1101 $patterns[] = 'mod-*-index';
1102 }
1103 }
1104 while (count($bits) > 0) {
1105 $patterns[] = implode('-', $bits) . '-*';
1106 array_pop($bits);
1107 }
1108 $patterns[] = '*';
1109 return $patterns;
1110}
1111
21d33bdf 1112/// Functions update the blocks if required by the request parameters ==========
1113
1114/**
1115 * Return a {@link block_contents} representing the add a new block UI, if
1116 * this user is allowed to see it.
1117 *
1118 * @return block_contents an appropriate block_contents, or null if the user
1119 * cannot add any blocks here.
1120 */
1121function block_add_block_ui($page, $output) {
1122 global $CFG;
1123 if (!$page->user_is_editing() || !$page->user_can_edit_blocks()) {
1124 return null;
1125 }
1126
1127 $bc = new block_contents();
1128 $bc->title = get_string('addblock');
1129 $bc->add_class('block_adminblock');
1130
a2789e34 1131 $missingblocks = $page->blocks->get_addable_blocks();
21d33bdf 1132 if (empty($missingblocks)) {
a2789e34 1133 $bc->content = get_string('noblockstoaddhere');
21d33bdf 1134 return $bc;
1135 }
1136
1137 $menu = array();
a2789e34 1138 foreach ($missingblocks as $block) {
21d33bdf 1139 $blockobject = block_instance($block->name);
1140 if ($blockobject !== false && $blockobject->user_can_addto($page)) {
1141 $menu[$block->name] = $blockobject->get_title();
1142 }
1143 }
1144 asort($menu, SORT_LOCALE_STRING);
1145
1146 // TODO convert to $OUTPUT.
a2789e34 1147 $actionurl = $page->url->out_action() . '&amp;bui_addblock=';
21d33bdf 1148 $bc->content = popup_form($actionurl, $menu, 'add_block', '', get_string('adddots'), '', '', true);
1149 return $bc;
1150}
1151
1936f20b 1152/**
1153 * Get the appropriate list of editing icons for a block. This is used
1154 * to set {@link block_contents::$controls} in {@link block_base::get_contents_for_output()}.
1155 *
1156 * @param $output The core_renderer to use when generating the output. (Need to get icon paths.)
1157 * @return an array in the format for {@link block_contents::$controls}
1158 * @since Moodle 2.0.
1159 */
1160function block_edit_controls($block, $page) {
1161 global $CFG;
1162
1163 $controls = array();
166e3bd1 1164 $actionurl = $page->url->out(false, array('sesskey'=> sesskey()), false);
1936f20b 1165
1166 // Assign roles icon.
1167 if (has_capability('moodle/role:assign', $block->context)) {
4a3b4620 1168 $controls[] = array('url' => $CFG->wwwroot . '/' . $CFG->admin .
166e3bd1 1169 '/roles/assign.php?contextid=' . $block->context->id . '&returnurl=' . urlencode($page->url->out_returnurl()),
1936f20b 1170 'icon' => 'i/roles', 'caption' => get_string('assignroles', 'role'));
1171 }
1172
d14edf06 1173 if ($page->user_can_edit_blocks()) {
1936f20b 1174 // Show/hide icon.
1175 if ($block->instance->visible) {
166e3bd1 1176 $controls[] = array('url' => $actionurl . '&bui_hideid=' . $block->instance->id,
1936f20b 1177 'icon' => 't/hide', 'caption' => get_string('hide'));
1178 } else {
166e3bd1 1179 $controls[] = array('url' => $actionurl . '&bui_showid=' . $block->instance->id,
1936f20b 1180 'icon' => 't/show', 'caption' => get_string('show'));
1181 }
d14edf06 1182 }
1936f20b 1183
d14edf06 1184 if ($page->user_can_edit_blocks() || $block->user_can_edit()) {
73e2e5c0 1185 // Edit config icon - always show - needed for positioning UI.
166e3bd1 1186 $controls[] = array('url' => $actionurl . '&bui_editid=' . $block->instance->id,
73e2e5c0 1187 'icon' => 't/edit', 'caption' => get_string('configuration'));
d14edf06 1188 }
1936f20b 1189
d14edf06 1190 if ($page->user_can_edit_blocks() && $block->user_can_edit() && $block->user_can_addto($page)) {
1936f20b 1191 // Delete icon.
d14edf06 1192 $controls[] = array('url' => $actionurl . '&bui_deleteid=' . $block->instance->id,
1936f20b 1193 'icon' => 't/delete', 'caption' => get_string('delete'));
d14edf06 1194 }
1936f20b 1195
d14edf06 1196 if ($page->user_can_edit_blocks()) {
1936f20b 1197 // Move icon.
1198 $controls[] = array('url' => $page->url->out(false, array('moveblockid' => $block->instance->id)),
1199 'icon' => 't/move', 'caption' => get_string('move'));
1200 }
1201
1202 return $controls;
1203}
1204
21d33bdf 1205// Functions that have been deprecated by block_manager =======================
f032aa7a 1206
08eab897 1207/**
a2789e34 1208 * @deprecated since Moodle 2.0 - use $page->blocks->get_addable_blocks();
d4accfc0 1209 *
08eab897 1210 * This function returns an array with the IDs of any blocks that you can add to your page.
1211 * Parameters are passed by reference for speed; they are not modified at all.
d4accfc0 1212 *
08eab897 1213 * @param $page the page object.
bb46a4fa 1214 * @param $blockmanager Not used.
08eab897 1215 * @return array of block type ids.
1216 */
bb46a4fa 1217function blocks_get_missing(&$page, &$blockmanager) {
a2789e34 1218 debugging('blocks_get_missing is deprecated. Please use $page->blocks->get_addable_blocks() instead.', DEBUG_DEVELOPER);
1219 $blocks = $page->blocks->get_addable_blocks();
1220 $ids = array();
1221 foreach ($blocks as $block) {
1222 $ids[] = $block->id;
1223 }
1224 return $ids;
f032aa7a 1225}
1226
bb46a4fa 1227/**
1228 * Actually delete from the database any blocks that are currently on this page,
1229 * but which should not be there according to blocks_name_allowed_in_format.
d4accfc0 1230 *
1231 * @todo Write/Fix this function. Currently returns immediatly
c679c358 1232 * @param $course
bb46a4fa 1233 */
c679c358 1234function blocks_remove_inappropriate($course) {
bb46a4fa 1235 // TODO
1236 return;
1237 $blockmanager = blocks_get_by_page($page);
f032aa7a 1238
bb46a4fa 1239 if(empty($blockmanager)) {
f032aa7a 1240 return;
1241 }
1242
d529807a 1243 if(($pageformat = $page->pagetype) == NULL) {
f032aa7a 1244 return;
1245 }
1246
f2c6739c 1247 foreach($blockmanager as $region) {
1248 foreach($region as $instance) {
f032aa7a 1249 $block = blocks_get_record($instance->blockid);
5bbbe0be 1250 if(!blocks_name_allowed_in_format($block->name, $pageformat)) {
a2789e34 1251 blocks_delete_instance($instance->instance);
f032aa7a 1252 }
1253 }
1254 }
1255}
1256
d4accfc0 1257/**
1258 * Check that a given name is in a permittable format
1259 *
1260 * @param string $name
1261 * @param string $pageformat
1262 * @return bool
1263 */
5bbbe0be 1264function blocks_name_allowed_in_format($name, $pageformat) {
cd2bc3c9 1265 $accept = NULL;
1266 $maxdepth = -1;
1267 $formats = block_method_result($name, 'applicable_formats');
1268 if (!$formats) {
1269 $formats = array();
1270 }
1271 foreach ($formats as $format => $allowed) {
1272 $formatregex = '/^'.str_replace('*', '[^-]*', $format).'.*$/';
1273 $depth = substr_count($format, '-');
1274 if (preg_match($formatregex, $pageformat) && $depth > $maxdepth) {
1275 $maxdepth = $depth;
1276 $accept = $allowed;
5bbbe0be 1277 }
1278 }
cd2bc3c9 1279 if ($accept === NULL) {
5bbbe0be 1280 $accept = !empty($formats['all']);
1281 }
1282 return $accept;
1283}
1284
feed1900 1285/**
1286 * Delete a block, and associated data.
d4accfc0 1287 *
feed1900 1288 * @param object $instance a row from the block_instances table
d4accfc0 1289 * @param bool $nolongerused legacy parameter. Not used, but kept for bacwards compatibility.
1290 * @param bool $skipblockstables for internal use only. Makes @see blocks_delete_all_for_context() more efficient.
feed1900 1291 */
1292function blocks_delete_instance($instance, $nolongerused = false, $skipblockstables = false) {
f4d38d20 1293 global $DB;
1294
1295 if ($block = block_instance($instance->blockname, $instance)) {
feed1900 1296 $block->instance_delete();
1297 }
1298 delete_context(CONTEXT_BLOCK, $instance->id);
f032aa7a 1299
feed1900 1300 if (!$skipblockstables) {
1301 $DB->delete_records('block_positions', array('blockinstanceid' => $instance->id));
1302 $DB->delete_records('block_instances', array('id' => $instance->id));
b33dd23a 1303 }
feed1900 1304}
b33dd23a 1305
feed1900 1306/**
1307 * Delete all the blocks that belong to a particular context.
d4accfc0 1308 *
d4accfc0 1309 * @param int $contextid the context id.
feed1900 1310 */
1311function blocks_delete_all_for_context($contextid) {
1312 global $DB;
a2789e34 1313 $instances = $DB->get_recordset('block_instances', array('parentcontextid' => $contextid));
feed1900 1314 foreach ($instances as $instance) {
1315 blocks_delete_instance($instance, true);
0d6b9d4f 1316 }
feed1900 1317 $instances->close();
13a0d3d3 1318 $DB->delete_records('block_instances', array('parentcontextid' => $contextid));
feed1900 1319 $DB->delete_records('block_positions', array('contextid' => $contextid));
f032aa7a 1320}
1321
ae42ff6f 1322/**
1323 * Set a block to be visible or hidden on a particular page.
1324 *
1325 * @param object $instance a row from the block_instances, preferably LEFT JOINed with the
1326 * block_positions table as return by block_manager.
1327 * @param moodle_page $page the back to set the visibility with respect to.
1328 * @param integer $newvisibility 1 for visible, 0 for hidden.
1329 */
1330function blocks_set_visibility($instance, $page, $newvisibility) {
1331 global $DB;
1332 if (!empty($instance->blockpositionid)) {
1333 // Already have local information on this page.
1334 $DB->set_field('block_positions', 'visible', $newvisibility, array('id' => $instance->blockpositionid));
1335 return;
1336 }
1337
1338 // Create a new block_positions record.
1339 $bp = new stdClass;
1340 $bp->blockinstanceid = $instance->id;
1341 $bp->contextid = $page->context->id;
1342 $bp->pagetype = $page->pagetype;
1343 if ($page->subpage) {
1344 $bp->subpage = $page->subpage;
1345 }
1346 $bp->visible = $newvisibility;
1347 $bp->region = $instance->defaultregion;
1348 $bp->weight = $instance->defaultweight;
1349 $DB->insert_record('block_positions', $bp);
1350}
1351
d4accfc0 1352/**
d4a03c00 1353 * @deprecated since 2.0
1354 * Delete all the blocks from a particular page.
d4accfc0 1355 *
d4a03c00 1356 * @param string $pagetype the page type.
1357 * @param integer $pageid the page id.
1358 * @return bool success or failure.
d4accfc0 1359 */
d4a03c00 1360function blocks_delete_all_on_page($pagetype, $pageid) {
1361 global $DB;
1362
1363 debugging('Call to deprecated function blocks_delete_all_on_page. ' .
1364 'This function cannot work any more. Doing nothing. ' .
1365 'Please update your code to use a block_manager method $PAGE->blocks->....', DEBUG_DEVELOPER);
1366 return false;
0f3fe4b6 1367}
1368
d4accfc0 1369/**
d4a03c00 1370 * Dispite what this function is called, it seems to be mostly used to populate
1371 * the default blocks when a new course (or whatever) is created.
d4accfc0 1372 *
d4a03c00 1373 * @deprecated since 2.0
d4accfc0 1374 *
d4a03c00 1375 * @param object $page the page to add default blocks to.
1376 * @return boolean success or failure.
d4accfc0 1377 */
d4a03c00 1378function blocks_repopulate_page($page) {
1379 global $CFG;
0f3fe4b6 1380
d4a03c00 1381 debugging('Call to deprecated function blocks_repopulate_page. ' .
1382 'Use a more specific method like blocks_add_default_course_blocks, ' .
1383 'or just call $PAGE->blocks->add_blocks()', DEBUG_DEVELOPER);
d23157d8 1384
d4a03c00 1385 /// If the site override has been defined, it is the only valid one.
1386 if (!empty($CFG->defaultblocks_override)) {
1387 $blocknames = $CFG->defaultblocks_override;
1388 } else {
1389 $blocknames = $page->blocks_get_default();
66492322 1390 }
0f3fe4b6 1391
d4a03c00 1392 $blocks = blocks_parse_default_blocks_list($blocknames);
1393 $page->blocks->add_blocks($blocks);
1394
1395 return true;
0f3fe4b6 1396}
1397
08eab897 1398/**
d4a03c00 1399 * Get the block record for a particular blockid - that is, a particul type os block.
d4accfc0 1400 *
d4accfc0 1401 * @param $int blockid block type id. If null, an array of all block types is returned.
1402 * @param bool $notusedanymore No longer used.
08eab897 1403 * @return array|object row from block table, or all rows.
1404 */
1405function blocks_get_record($blockid = NULL, $notusedanymore = false) {
1406 global $PAGE;
1407 $blocks = $PAGE->blocks->get_installed_blocks();
1408 if ($blockid === NULL) {
1409 return $blocks;
1410 } else if (isset($blocks[$blockid])) {
1411 return $blocks[$blockid];
1412 } else {
1413 return false;
9b4b78fd 1414 }
9b4b78fd 1415}
1416
d4accfc0 1417/**
1418 * Find a given block by its blockid within a provide array
1419 *
1420 * @param int $blockid
1421 * @param array $blocksarray
1422 * @return bool|object Instance if found else false
1423 */
9b4b78fd 1424function blocks_find_block($blockid, $blocksarray) {
0d6b9d4f 1425 if (empty($blocksarray)) {
1426 return false;
1427 }
9b4b78fd 1428 foreach($blocksarray as $blockgroup) {
0d6b9d4f 1429 if (empty($blockgroup)) {
1430 continue;
1431 }
9b4b78fd 1432 foreach($blockgroup as $instance) {
1433 if($instance->blockid == $blockid) {
1434 return $instance;
1435 }
1436 }
1437 }
1438 return false;
1439}
1440
d4a03c00 1441// Functions for programatically adding default blocks to pages ================
0f3fe4b6 1442
9d1d606e 1443/**
1444 * Parse a list of default blocks. See config-dist for a description of the format.
d4accfc0 1445 *
9d1d606e 1446 * @param string $blocksstr
1447 * @return array
1448 */
1449function blocks_parse_default_blocks_list($blocksstr) {
f474a4e5 1450 $blocks = array();
1451 $bits = explode(':', $blocksstr);
1452 if (!empty($bits)) {
1453 $blocks[BLOCK_POS_LEFT] = explode(',', array_shift($bits));
1454 }
1455 if (!empty($bits)) {
1456 $blocks[BLOCK_POS_RIGHT] = explode(',', array_shift($bits));
1457 }
1458 return $blocks;
9d1d606e 1459}
5b224948 1460
9d1d606e 1461/**
1462 * @return array the blocks that should be added to the site course by default.
1463 */
1464function blocks_get_default_site_course_blocks() {
1465 global $CFG;
9b4b78fd 1466
9d1d606e 1467 if (!empty($CFG->defaultblocks_site)) {
f474a4e5 1468 return blocks_parse_default_blocks_list($CFG->defaultblocks_site);
9d1d606e 1469 } else {
f474a4e5 1470 return array(
9d1d606e 1471 BLOCK_POS_LEFT => array('site_main_menu', 'admin_tree'),
1472 BLOCK_POS_RIGHT => array('course_summary', 'calendar_month')
1473 );
9b4b78fd 1474 }
9d1d606e 1475}
1476
1477/**
1478 * Add the default blocks to a course.
d4accfc0 1479 *
9d1d606e 1480 * @param object $course a course object.
1481 */
1482function blocks_add_default_course_blocks($course) {
1483 global $CFG;
1484
1485 if (!empty($CFG->defaultblocks_override)) {
1486 $blocknames = blocks_parse_default_blocks_list($CFG->defaultblocks_override);
1487
1488 } else if ($course->id == SITEID) {
1489 $blocknames = blocks_get_default_site_course_blocks();
1490
1491 } else {
1492 $defaultblocks = 'defaultblocks_' . $course->format;
1493 if (!empty($CFG->$defaultblocks)) {
1494 $blocknames = blocks_parse_default_blocks_list($CFG->$defaultblocks);
1495
1496 } else {
1d00ec6a 1497 $formatconfig = $CFG->dirroot.'/course/format/'.$course->format.'/config.php';
1498 if (is_readable($formatconfig)) {
9d1d606e 1499 require($formatconfig);
1500 }
1501 if (!empty($format['defaultblocks'])) {
1502 $blocknames = blocks_parse_default_blocks_list($format['defaultblocks']);
9b4b78fd 1503
9d1d606e 1504 } else if (!empty($CFG->defaultblocks)){
1505 $blocknames = blocks_parse_default_blocks_list($CFG->defaultblocks);
1506
1507 } else {
1508 $blocknames = array(
1509 BLOCK_POS_LEFT => array('participants', 'activity_modules', 'search_forums', 'admin', 'course_list'),
1510 BLOCK_POS_RIGHT => array('news_items', 'calendar_upcoming', 'recent_activity')
1511 );
1512 }
1513 }
9b4b78fd 1514 }
1515
f474a4e5 1516 if ($course->id == SITEID) {
1517 $pagetypepattern = 'site-index';
1518 } else {
1519 $pagetypepattern = 'course-view-*';
1520 }
1521
9d1d606e 1522 $page = new moodle_page();
1523 $page->set_course($course);
f474a4e5 1524 $page->blocks->add_blocks($blocknames, $pagetypepattern);
9d1d606e 1525}
1526
1527/**
1528 * Add the default system-context blocks. E.g. the admin tree.
1529 */
1530function blocks_add_default_system_blocks() {
1531 $page = new moodle_page();
1532 $page->set_context(get_context_instance(CONTEXT_SYSTEM));
1533 $page->blocks->add_blocks(array(BLOCK_POS_LEFT => array('admin_tree', 'admin_bookmarks')), 'admin-*');
1534}