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