blocks: MDL-19010 Delete weird line of legacy code.
[moodle.git] / lib / blocklib.php
CommitLineData
d4accfc0 1<?php
2
3// This file is part of Moodle - http://moodle.org/
4//
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.
14//
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 *
d4a03c00 21 * This file defines the {@link block_manager} class,
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;
70 $a->url = $page->url;
71 parent::__construct('blockdoesnotexistonpage', '', $page->url, $a);
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
102 * of $allblocks. Access this via the {@link get_addable_blocks()} method
103 * to ensure it is lazy-loaded.
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 *
08eab897 174 * @return array block id => record from block table.
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)) &&
08eab897 195 blocks_name_allowed_in_format($block->name, $pageformat)) {
196 $this->addableblocks[$block->id] = $block;
197 }
198 }
199
200 return $this->addableblocks;
201 }
202
d4accfc0 203 /**
204 * Find out if a block is present ? just a guess
205 * @todo Write this function and document
206 */
08eab897 207 public function is_block_present($blocktypeid) {
208 // TODO
209 }
210
211 /**
d4accfc0 212 * Find out if a block type is known by the system
213 *
08eab897 214 * @param string $blockname the name of ta type of block.
215 * @param boolean $includeinvisible if false (default) only check 'visible' blocks, that is, blocks enabled by the admin.
216 * @return boolean true if this block in installed.
217 */
218 public function is_known_block_type($blockname, $includeinvisible = false) {
219 $blocks = $this->get_installed_blocks();
220 foreach ($blocks as $block) {
221 if ($block->name == $blockname && ($includeinvisible || $block->visible)) {
222 return true;
223 }
224 }
225 return false;
226 }
227
228 /**
d4accfc0 229 * Find out if a region exists on a page
230 *
08eab897 231 * @param string $region a region name
232 * @return boolean true if this retion exists on this page.
233 */
234 public function is_known_region($region) {
235 return array_key_exists($region, $this->regions);
236 }
237
238 /**
d4accfc0 239 * Get an array of all blocks within a given region
240 *
241 * @param string $region a block region that exists on this page.
08eab897 242 * @return array of block instances.
243 */
244 public function get_blocks_for_region($region) {
245 $this->check_is_loaded();
bb46a4fa 246 $this->ensure_instances_exist($region);
247 return $this->blockinstances[$region];
248 }
249
250 /**
d4accfc0 251 * Returns an array of block content objects that exist in a region
252 *
d4a03c00 253 * @param string $region a block region that exists on this page.
254 * @return array of block block_contents objects for all the blocks in a region.
bb46a4fa 255 */
d4a03c00 256 public function get_content_for_region($region, $output) {
bb46a4fa 257 $this->check_is_loaded();
d4a03c00 258 $this->ensure_content_created($region, $output);
bb46a4fa 259 return $this->visibleblockcontent[$region];
08eab897 260 }
261
d4a03c00 262 /**
263 * Determine whether a region contains anything. (Either any real blocks, or
264 * the add new block UI.)
265 * @param string $region a block region that exists on this page.
266 * @return boolean Whether there is anything in this region.
267 */
268 public function region_has_content($region) {
269 if (!$this->is_known_region($region)) {
270 return false;
271 }
272 $this->check_is_loaded();
fc2593fe 273 $this->ensure_instances_exist($region);
d4a03c00 274 if ($this->page->user_is_editing() && $this->page->user_can_edit_blocks()) {
275 // If editing is on, we need all the block regions visible, for the
276 // move blocks UI.
277 return true;
278 }
279 return !empty($this->blockinstances[$region]) || !empty($this->extracontent[$region]);
280 }
281
08eab897 282 /**
d4accfc0 283 * Get an array of all of the installed blocks.
284 *
08eab897 285 * @return array contents of the block table.
286 */
287 public function get_installed_blocks() {
288 global $DB;
289 if (is_null($this->allblocks)) {
290 $this->allblocks = $DB->get_records('block');
291 }
292 return $this->allblocks;
293 }
294
86b5ea0f 295/// Setter methods =============================================================
296
297 /**
d4accfc0 298 * Add a region to a page
299 *
86b5ea0f 300 * @param string $region add a named region where blocks may appear on the
301 * current page. This is an internal name, like 'side-pre', not a string to
302 * display in the UI.
303 */
304 public function add_region($region) {
305 $this->check_not_yet_loaded();
306 $this->regions[$region] = 1;
307 }
308
309 /**
d4accfc0 310 * Add an array of regions
311 * @see add_region()
312 *
86b5ea0f 313 * @param array $regions this utility method calls add_region for each array element.
314 */
315 public function add_regions($regions) {
316 foreach ($regions as $region) {
317 $this->add_region($region);
318 }
319 }
320
321 /**
d4accfc0 322 * Set the default region for new blocks on the page
323 *
86b5ea0f 324 * @param string $defaultregion the internal names of the region where new
325 * blocks should be added by default, and where any blocks from an
326 * unrecognised region are shown.
327 */
328 public function set_default_region($defaultregion) {
329 $this->check_not_yet_loaded();
08eab897 330 $this->check_region_is_known($defaultregion);
86b5ea0f 331 $this->defaultregion = $defaultregion;
332 }
333
d4a03c00 334 /**
335 * Add something that looks like a block, but which isn't an actual block_instance,
336 * to this page.
337 *
338 * @param block_contents $bc the content of the block like thing.
339 * @param string $region a block region that exists on this page.
340 */
341 public function add_pretend_block($bc, $region) {
342 $this->page->initialise_theme_and_output();
343 $this->check_region_is_known($region);
344 if (array_key_exists($region, $this->visibleblockcontent)) {
345 throw new coding_exception('block_manager has already prepared the blocks in region ' .
346 $region . 'for output. It is too late to add a pretend block.');
347 }
348 $this->extracontent[$region][] = $bc;
349 }
350
08eab897 351/// Actions ====================================================================
352
353 /**
354 * This method actually loads the blocks for our page from the database.
d4accfc0 355 *
d4accfc0 356 * @param bool|null $includeinvisible
08eab897 357 */
358 public function load_blocks($includeinvisible = NULL) {
d19e8195 359 global $DB, $CFG;
bb46a4fa 360 if (!is_null($this->birecordsbyregion)) {
361 // Already done.
362 return;
363 }
08eab897 364
d19e8195 365 if ($CFG->version < 2009050619) {
366 // Upgrade/install not complete. Don't try too show any blocks.
367 $this->birecordsbyregion = array();
368 return;
369 }
370
d4a03c00 371 // Ensure we have been initialised.
b7009474 372 if (!isset($this->defaultregion)) {
373 $this->page->initialise_theme_and_output();
d4a03c00 374 // If there are still no block regions, then there are no blocks on this page.
375 if (empty($this->regions)) {
376 $this->birecordsbyregion = array();
377 return;
378 }
b7009474 379 }
380
08eab897 381 if (is_null($includeinvisible)) {
382 $includeinvisible = $this->page->user_is_editing();
383 }
384 if ($includeinvisible) {
385 $visiblecheck = 'AND (bp.visible = 1 OR bp.visible IS NULL)';
386 } else {
387 $visiblecheck = '';
388 }
389
390 $context = $this->page->context;
13a0d3d3 391 $contexttest = 'bi.parentcontextid = :contextid2';
08eab897 392 $parentcontextparams = array();
393 $parentcontextids = get_parent_contexts($context);
394 if ($parentcontextids) {
395 list($parentcontexttest, $parentcontextparams) =
396 $DB->get_in_or_equal($parentcontextids, SQL_PARAMS_NAMED, 'parentcontext0000');
13a0d3d3 397 $contexttest = "($contexttest OR (bi.showinsubcontexts = 1 AND bi.parentcontextid $parentcontexttest))";
08eab897 398 }
399
400 $pagetypepatterns = $this->matching_page_type_patterns($this->page->pagetype);
401 list($pagetypepatterntest, $pagetypepatternparams) =
402 $DB->get_in_or_equal($pagetypepatterns, SQL_PARAMS_NAMED, 'pagetypepatterntest0000');
403
404 $params = array(
405 'subpage1' => $this->page->subpage,
406 'subpage2' => $this->page->subpage,
407 'contextid1' => $context->id,
408 'contextid2' => $context->id,
409 'pagetype' => $this->page->pagetype,
e92c286c 410 'contextblock' => CONTEXT_BLOCK,
08eab897 411 );
412 $sql = "SELECT
413 bi.id,
d4a03c00 414 bp.id AS blockpositionid,
08eab897 415 bi.blockname,
13a0d3d3 416 bi.parentcontextid,
08eab897 417 bi.showinsubcontexts,
418 bi.pagetypepattern,
419 bi.subpagepattern,
bb46a4fa 420 COALESCE(bp.visible, 1) AS visible,
08eab897 421 COALESCE(bp.region, bi.defaultregion) AS region,
422 COALESCE(bp.weight, bi.defaultweight) AS weight,
e92c286c 423 bi.configdata,
424 ctx.id AS ctxid,
425 ctx.path AS ctxpath,
426 ctx.depth AS ctxdepth,
427 ctx.contextlevel AS ctxlevel
08eab897 428
429 FROM {block_instances} bi
430 JOIN {block} b ON bi.blockname = b.name
431 LEFT JOIN {block_positions} bp ON bp.blockinstanceid = bi.id
432 AND bp.contextid = :contextid1
433 AND bp.pagetype = :pagetype
434 AND bp.subpage = :subpage1
e92c286c 435 JOIN {context} ctx ON ctx.contextlevel = :contextblock
436 AND ctx.instanceid = bi.id
08eab897 437
438 WHERE
439 $contexttest
440 AND bi.pagetypepattern $pagetypepatterntest
441 AND (bi.subpagepattern IS NULL OR bi.subpagepattern = :subpage2)
442 $visiblecheck
443 AND b.visible = 1
444
445 ORDER BY
446 COALESCE(bp.region, bi.defaultregion),
447 COALESCE(bp.weight, bi.defaultweight),
448 bi.id";
449 $blockinstances = $DB->get_recordset_sql($sql, $params + $parentcontextparams + $pagetypepatternparams);
450
bb46a4fa 451 $this->birecordsbyregion = $this->prepare_per_region_arrays();
08eab897 452 $unknown = array();
08eab897 453 foreach ($blockinstances as $bi) {
e92c286c 454 $bi = make_context_subobj($bi);
08eab897 455 if ($this->is_known_region($bi->region)) {
bb46a4fa 456 $this->birecordsbyregion[$bi->region][] = $bi;
08eab897 457 } else {
458 $unknown[] = $bi;
459 }
460 }
d4a03c00 461
462 // Pages don't necessarily have a defaultregion. The one time this can
463 // happen is when there are no theme block regions, but the script itself
464 // has a block region in the main content area.
465 if (!empty($this->defaultregion)) {
466 $this->birecordsbyregion[$this->defaultregion] =
467 array_merge($this->birecordsbyregion[$this->defaultregion], $unknown);
468 }
08eab897 469 }
470
471 /**
472 * Add a block to the current page, or related pages. The block is added to
473 * context $this->page->contextid. If $pagetypepattern $subpagepattern
d4accfc0 474 *
08eab897 475 * @param string $blockname The type of block to add.
476 * @param string $region the block region on this page to add the block to.
477 * @param integer $weight determines the order where this block appears in the region.
478 * @param boolean $showinsubcontexts whether this block appears in subcontexts, or just the current context.
479 * @param string|null $pagetypepattern which page types this block should appear on. Defaults to just the current page type.
480 * @param string|null $subpagepattern which subpage this block should appear on. NULL = any (the default), otherwise only the specified subpage.
481 */
482 public function add_block($blockname, $region, $weight, $showinsubcontexts, $pagetypepattern = NULL, $subpagepattern = NULL) {
483 global $DB;
484 $this->check_known_block_type($blockname);
485 $this->check_region_is_known($region);
486
487 if (empty($pagetypepattern)) {
488 $pagetypepattern = $this->page->pagetype;
489 }
490
491 $blockinstance = new stdClass;
492 $blockinstance->blockname = $blockname;
13a0d3d3 493 $blockinstance->parentcontextid = $this->page->context->id;
08eab897 494 $blockinstance->showinsubcontexts = !empty($showinsubcontexts);
495 $blockinstance->pagetypepattern = $pagetypepattern;
496 $blockinstance->subpagepattern = $subpagepattern;
497 $blockinstance->defaultregion = $region;
498 $blockinstance->defaultweight = $weight;
499 $blockinstance->configdata = '';
feed1900 500 $blockinstance->id = $DB->insert_record('block_instances', $blockinstance);
501
e92c286c 502 // Ensure the block context is created.
503 get_context_instance(CONTEXT_BLOCK, $blockinstance->id);
e03c0c1d 504
feed1900 505 // If the new instance was created, allow it to do additional setup
e92c286c 506 if ($block = block_instance($blockname, $blockinstance)) {
feed1900 507 $block->instance_create();
508 }
08eab897 509 }
510
21d33bdf 511 public function add_block_at_end_of_default_region($blockname) {
512 $defaulregion = $this->get_default_region();
513 $lastcurrentblock = end($this->birecordsbyregion[$defaulregion]);
514 if ($this->page->subpage) {
515 $subpage = $this->page->subpage;
516 } else {
517 $subpage = null;
518 }
519 $this->add_block($blockname, $defaulregion, $lastcurrentblock->weight + 1, false, $this->page->pagetype, $subpage);
520 }
521
9d1d606e 522 /**
523 * Convenience method, calls add_block repeatedly for all the blocks in $blocks.
d4accfc0 524 *
9d1d606e 525 * @param array $blocks array with arrray keys the region names, and values an array of block names.
526 * @param string $pagetypepattern optional. Passed to @see add_block()
527 * @param string $subpagepattern optional. Passed to @see add_block()
528 */
529 public function add_blocks($blocks, $pagetypepattern = NULL, $subpagepattern = NULL) {
530 $this->add_regions(array_keys($blocks));
531 foreach ($blocks as $region => $regionblocks) {
532 $weight = 0;
533 foreach ($regionblocks as $blockname) {
534 $this->add_block($blockname, $region, $weight, false, $pagetypepattern, $subpagepattern);
535 $weight += 1;
536 }
537 }
538 }
539
f4d38d20 540 /**
d4accfc0 541 * Find a given block by its instance id
542 *
f4d38d20 543 * @param integer $instanceid
d4accfc0 544 * @return object
f4d38d20 545 */
546 public function find_instance($instanceid) {
547 foreach ($this->regions as $region => $notused) {
548 $this->ensure_instances_exist($region);
549 foreach($this->blockinstances[$region] as $instance) {
550 if ($instance->instance->id == $instanceid) {
551 return $instance;
552 }
553 }
554 }
555 throw new block_not_on_page_exception($instanceid, $this->page);
556 }
557
86b5ea0f 558/// Inner workings =============================================================
559
08eab897 560 /**
561 * Given a specific page type, return all the page type patterns that might
562 * match it.
d4accfc0 563 *
08eab897 564 * @param string $pagetype for example 'course-view-weeks' or 'mod-quiz-view'.
565 * @return array an array of all the page type patterns that might match this page type.
566 */
567 protected function matching_page_type_patterns($pagetype) {
568 $patterns = array($pagetype, '*');
569 $bits = explode('-', $pagetype);
570 if (count($bits) == 3 && $bits[0] == 'mod') {
571 if ($bits[2] == 'view') {
572 $patterns[] = 'mod-*-view';
573 } else if ($bits[2] == 'index') {
574 $patterns[] = 'mod-*-index';
575 }
576 }
577 while (count($bits) > 0) {
578 $patterns[] = implode('-', $bits) . '-*';
579 array_pop($bits);
580 }
581 return $patterns;
582 }
583
d4accfc0 584 /**
585 * Check whether the page blocks have been loaded yet
586 *
587 * @return void Throws coding exception if already loaded
588 */
86b5ea0f 589 protected function check_not_yet_loaded() {
bb46a4fa 590 if (!is_null($this->birecordsbyregion)) {
86b5ea0f 591 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.');
592 }
593 }
594
d4accfc0 595 /**
596 * Check whether the page blocks have been loaded yet
597 *
598 * Nearly identical to the above function {@link check_not_yet_loaded()} except different message
599 *
600 * @return void Throws coding exception if already loaded
601 */
08eab897 602 protected function check_is_loaded() {
bb46a4fa 603 if (is_null($this->birecordsbyregion)) {
08eab897 604 throw new coding_exception('block_manager has not yet loaded the blocks, to it is too soon to request the information you asked for.');
605 }
606 }
607
d4accfc0 608 /**
609 * Check if a block type is known and usable
610 *
611 * @param string $blockname The block type name to search for
612 * @param bool $includeinvisible Include disabled block types in the intial pass
613 * @return void Coding Exception thrown if unknown or not enabled
614 */
08eab897 615 protected function check_known_block_type($blockname, $includeinvisible = false) {
616 if (!$this->is_known_block_type($blockname, $includeinvisible)) {
617 if ($this->is_known_block_type($blockname, true)) {
618 throw new coding_exception('Unknown block type ' . $blockname);
619 } else {
620 throw new coding_exception('Block type ' . $blockname . ' has been disabled by the administrator.');
621 }
622 }
623 }
624
d4accfc0 625 /**
626 * Check if a region is known by its name
627 *
628 * @param string $region
629 * @return void Coding Exception thrown if the region is not known
630 */
08eab897 631 protected function check_region_is_known($region) {
632 if (!$this->is_known_region($region)) {
633 throw new coding_exception('Trying to reference an unknown block region ' . $region);
634 }
86b5ea0f 635 }
bb46a4fa 636
637 /**
d4accfc0 638 * Returns an array of region names as keys and nested arrays for values
639 *
bb46a4fa 640 * @return array an array where the array keys are the region names, and the array
641 * values are empty arrays.
642 */
643 protected function prepare_per_region_arrays() {
644 $result = array();
645 foreach ($this->regions as $region => $notused) {
646 $result[$region] = array();
647 }
648 return $result;
649 }
650
d4accfc0 651 /**
652 * Create a set of new block instance from a record array
653 *
654 * @param array $birecords An array of block instance records
655 * @return array An array of instantiated block_instance objects
656 */
bb46a4fa 657 protected function create_block_instances($birecords) {
658 $results = array();
659 foreach ($birecords as $record) {
660 $results[] = block_instance($record->blockname, $record, $this->page);
661 }
662 return $results;
663 }
664
d4accfc0 665 /**
666 * Return an array of content vars from a set of block instances
667 *
668 * @param array $instances An array of block instances
669 * @return array An array of content vars
670 */
d4a03c00 671 protected function create_block_contents($instances, $output) {
bb46a4fa 672 $results = array();
673 foreach ($instances as $instance) {
d4a03c00 674 $content = $instance->get_content_for_output($output);
bb46a4fa 675 if (!empty($content)) {
676 $results[] = $content;
677 }
678 }
679 return $results;
680 }
681
d4accfc0 682 /**
683 * Ensure block instances exist for a given region
684 *
685 * @param string $region Check for bi's with the instance with this name
686 */
bb46a4fa 687 protected function ensure_instances_exist($region) {
688 $this->check_region_is_known($region);
689 if (!array_key_exists($region, $this->blockinstances)) {
690 $this->blockinstances[$region] =
691 $this->create_block_instances($this->birecordsbyregion[$region]);
692 }
693 }
694
d4accfc0 695 /**
696 * Ensure that there is some content within the given region
697 *
698 * @param string $region The name of the region to check
699 */
d4a03c00 700 protected function ensure_content_created($region, $output) {
bb46a4fa 701 $this->ensure_instances_exist($region);
702 if (!array_key_exists($region, $this->visibleblockcontent)) {
d4a03c00 703 $contents = array();
704 if (array_key_exists($region, $this->extracontent)) {
705 $contents = $this->extracontent[$region];
706 }
707 $contents = array_merge($contents, $this->create_block_contents($this->blockinstances[$region], $output));
708 if ($region == $this->defaultregion) {
21d33bdf 709 $addblockui = block_add_block_ui($this->page, $output);
d4a03c00 710 if ($addblockui) {
711 $contents[] = $addblockui;
712 }
713 }
714 $this->visibleblockcontent[$region] = $contents;
bb46a4fa 715 }
716 }
86b5ea0f 717}
718
08eab897 719/// Helper functions for working with block classes ============================
720
721/**
722 * Call a class method (one that does not requrie a block instance) on a block class.
d4accfc0 723 *
08eab897 724 * @param string $blockname the name of the block.
725 * @param string $method the method name.
726 * @param array $param parameters to pass to the method.
727 * @return mixed whatever the method returns.
728 */
11306331 729function block_method_result($blockname, $method, $param = NULL) {
0f3fe4b6 730 if(!block_load_class($blockname)) {
731 return NULL;
732 }
11306331 733 return call_user_func(array('block_'.$blockname, $method), $param);
0f3fe4b6 734}
735
08eab897 736/**
737 * Creates a new object of the specified block class.
d4accfc0 738 *
08eab897 739 * @param string $blockname the name of the block.
740 * @param $instance block_instances DB table row (optional).
bb46a4fa 741 * @param moodle_page $page the page this block is appearing on.
08eab897 742 * @return block_base the requested block instance.
743 */
bb46a4fa 744function block_instance($blockname, $instance = NULL, $page = NULL) {
0f3fe4b6 745 if(!block_load_class($blockname)) {
746 return false;
747 }
e89d741a 748 $classname = 'block_'.$blockname;
f032aa7a 749 $retval = new $classname;
9b4b78fd 750 if($instance !== NULL) {
bb46a4fa 751 if (is_null($page)) {
752 global $PAGE;
753 $page = $PAGE;
754 }
755 $retval->_load_instance($instance, $page);
9b4b78fd 756 }
757 return $retval;
0f3fe4b6 758}
759
08eab897 760/**
761 * Load the block class for a particular type of block.
d4accfc0 762 *
08eab897 763 * @param string $blockname the name of the block.
764 * @return boolean success or failure.
765 */
0f3fe4b6 766function block_load_class($blockname) {
767 global $CFG;
768
a9033ad5 769 if(empty($blockname)) {
c7a9e293 770 return false;
771 }
772
e89d741a 773 $classname = 'block_'.$blockname;
a9033ad5 774
775 if(class_exists($classname)) {
776 return true;
777 }
778
779 require_once($CFG->dirroot.'/blocks/moodleblock.class.php');
e9a20759 780 @include_once($CFG->dirroot.'/blocks/'.$blockname.'/block_'.$blockname.'.php'); // do not throw errors if block code not present
0f3fe4b6 781
0f3fe4b6 782 return class_exists($classname);
783}
784
21d33bdf 785/// Functions update the blocks if required by the request parameters ==========
786
787/**
788 * Return a {@link block_contents} representing the add a new block UI, if
789 * this user is allowed to see it.
790 *
791 * @return block_contents an appropriate block_contents, or null if the user
792 * cannot add any blocks here.
793 */
794function block_add_block_ui($page, $output) {
795 global $CFG;
796 if (!$page->user_is_editing() || !$page->user_can_edit_blocks()) {
797 return null;
798 }
799
800 $bc = new block_contents();
801 $bc->title = get_string('addblock');
802 $bc->add_class('block_adminblock');
803
804 $missingblocks = array_keys($page->blocks->get_addable_blocks());
805 if (empty($missingblocks)) {
806 $bc->title = get_string('noblockstoaddhere');
807 return $bc;
808 }
809
810 $menu = array();
811 foreach ($missingblocks as $blockid) {
812 $block = blocks_get_record($blockid);
813 $blockobject = block_instance($block->name);
814 if ($blockobject !== false && $blockobject->user_can_addto($page)) {
815 $menu[$block->name] = $blockobject->get_title();
816 }
817 }
818 asort($menu, SORT_LOCALE_STRING);
819
820 // TODO convert to $OUTPUT.
821 $actionurl = $page->url->out_action(). '&amp;bui_addblock=';
822 $returnurlparam = '&amp;returnurl=' . urlencode($page->url->out_returnurl());
823 $bc->content = popup_form($actionurl, $menu, 'add_block', '', get_string('adddots'), '', '', true);
824 return $bc;
825}
826
827/**
828 * Process any block actions that were specified in the URL.
829 *
830 * This can only be done given a valid $page object.
831 *
832 * @return boolean true if anything was done. False if not.
833 */
834function block_process_url_actions($page) {
835 return block_process_url_add($page);
836}
837
838/**
839 * Process any block actions that were specified in the URL.
840 *
841 * This can only be done given a valid $page object.
842 *
843 * @return boolean true if anything was done. False if not.
844 */
845function block_process_url_add($page) {
846 $blocktype = optional_param('bui_addblock', null, PARAM_SAFEDIR);
847 if (!$blocktype) {
848 return false;
849 }
850
851 $page->blocks->add_block_at_end_of_default_region($blocktype);
852 return true;
853}
854
855
856
857// Functions that have been deprecated by block_manager =======================
f032aa7a 858
08eab897 859/**
860 * @deprecated since Moodle 2.0 - use $page->blocks->get
d4accfc0 861 *
08eab897 862 * This function returns an array with the IDs of any blocks that you can add to your page.
863 * Parameters are passed by reference for speed; they are not modified at all.
d4accfc0 864 *
08eab897 865 * @param $page the page object.
bb46a4fa 866 * @param $blockmanager Not used.
08eab897 867 * @return array of block type ids.
868 */
bb46a4fa 869function blocks_get_missing(&$page, &$blockmanager) {
08eab897 870 return array_keys($page->blocks->get_addable_blocks());
f032aa7a 871}
872
bb46a4fa 873/**
874 * Actually delete from the database any blocks that are currently on this page,
875 * but which should not be there according to blocks_name_allowed_in_format.
d4accfc0 876 *
877 * @todo Write/Fix this function. Currently returns immediatly
c679c358 878 * @param $course
bb46a4fa 879 */
c679c358 880function blocks_remove_inappropriate($course) {
bb46a4fa 881 // TODO
882 return;
883 $blockmanager = blocks_get_by_page($page);
f032aa7a 884
bb46a4fa 885 if(empty($blockmanager)) {
f032aa7a 886 return;
887 }
888
d529807a 889 if(($pageformat = $page->pagetype) == NULL) {
f032aa7a 890 return;
891 }
892
f2c6739c 893 foreach($blockmanager as $region) {
894 foreach($region as $instance) {
f032aa7a 895 $block = blocks_get_record($instance->blockid);
5bbbe0be 896 if(!blocks_name_allowed_in_format($block->name, $pageformat)) {
8a47e075 897 blocks_delete_instance($instance);
f032aa7a 898 }
899 }
900 }
901}
902
d4accfc0 903/**
904 * Check that a given name is in a permittable format
905 *
906 * @param string $name
907 * @param string $pageformat
908 * @return bool
909 */
5bbbe0be 910function blocks_name_allowed_in_format($name, $pageformat) {
cd2bc3c9 911 $accept = NULL;
912 $maxdepth = -1;
913 $formats = block_method_result($name, 'applicable_formats');
914 if (!$formats) {
915 $formats = array();
916 }
917 foreach ($formats as $format => $allowed) {
918 $formatregex = '/^'.str_replace('*', '[^-]*', $format).'.*$/';
919 $depth = substr_count($format, '-');
920 if (preg_match($formatregex, $pageformat) && $depth > $maxdepth) {
921 $maxdepth = $depth;
922 $accept = $allowed;
5bbbe0be 923 }
924 }
cd2bc3c9 925 if ($accept === NULL) {
5bbbe0be 926 $accept = !empty($formats['all']);
927 }
928 return $accept;
929}
930
feed1900 931/**
932 * Delete a block, and associated data.
d4accfc0 933 *
feed1900 934 * @param object $instance a row from the block_instances table
d4accfc0 935 * @param bool $nolongerused legacy parameter. Not used, but kept for bacwards compatibility.
936 * @param bool $skipblockstables for internal use only. Makes @see blocks_delete_all_for_context() more efficient.
feed1900 937 */
938function blocks_delete_instance($instance, $nolongerused = false, $skipblockstables = false) {
f4d38d20 939 global $DB;
940
941 if ($block = block_instance($instance->blockname, $instance)) {
feed1900 942 $block->instance_delete();
943 }
944 delete_context(CONTEXT_BLOCK, $instance->id);
f032aa7a 945
feed1900 946 if (!$skipblockstables) {
947 $DB->delete_records('block_positions', array('blockinstanceid' => $instance->id));
948 $DB->delete_records('block_instances', array('id' => $instance->id));
b33dd23a 949 }
feed1900 950}
b33dd23a 951
feed1900 952/**
953 * Delete all the blocks that belong to a particular context.
d4accfc0 954 *
d4accfc0 955 * @param int $contextid the context id.
feed1900 956 */
957function blocks_delete_all_for_context($contextid) {
958 global $DB;
959 $instances = $DB->get_recordset('block_instances', array('contextid' => $contextid));
960 foreach ($instances as $instance) {
961 blocks_delete_instance($instance, true);
0d6b9d4f 962 }
feed1900 963 $instances->close();
13a0d3d3 964 $DB->delete_records('block_instances', array('parentcontextid' => $contextid));
feed1900 965 $DB->delete_records('block_positions', array('contextid' => $contextid));
f032aa7a 966}
967
d4accfc0 968/**
d4a03c00 969 * @deprecated since 2.0
970 * Delete all the blocks from a particular page.
d4accfc0 971 *
d4a03c00 972 * @param string $pagetype the page type.
973 * @param integer $pageid the page id.
974 * @return bool success or failure.
d4accfc0 975 */
d4a03c00 976function blocks_delete_all_on_page($pagetype, $pageid) {
977 global $DB;
978
979 debugging('Call to deprecated function blocks_delete_all_on_page. ' .
980 'This function cannot work any more. Doing nothing. ' .
981 'Please update your code to use a block_manager method $PAGE->blocks->....', DEBUG_DEVELOPER);
982 return false;
0f3fe4b6 983}
984
d4accfc0 985/**
d4a03c00 986 * Dispite what this function is called, it seems to be mostly used to populate
987 * the default blocks when a new course (or whatever) is created.
d4accfc0 988 *
d4a03c00 989 * @deprecated since 2.0
d4accfc0 990 *
d4a03c00 991 * @param object $page the page to add default blocks to.
992 * @return boolean success or failure.
d4accfc0 993 */
d4a03c00 994function blocks_repopulate_page($page) {
995 global $CFG;
0f3fe4b6 996
d4a03c00 997 debugging('Call to deprecated function blocks_repopulate_page. ' .
998 'Use a more specific method like blocks_add_default_course_blocks, ' .
999 'or just call $PAGE->blocks->add_blocks()', DEBUG_DEVELOPER);
d23157d8 1000
d4a03c00 1001 /// If the site override has been defined, it is the only valid one.
1002 if (!empty($CFG->defaultblocks_override)) {
1003 $blocknames = $CFG->defaultblocks_override;
1004 } else {
1005 $blocknames = $page->blocks_get_default();
66492322 1006 }
0f3fe4b6 1007
d4a03c00 1008 $blocks = blocks_parse_default_blocks_list($blocknames);
1009 $page->blocks->add_blocks($blocks);
1010
1011 return true;
0f3fe4b6 1012}
1013
08eab897 1014/**
d4a03c00 1015 * Get the block record for a particular blockid - that is, a particul type os block.
d4accfc0 1016 *
d4accfc0 1017 * @param $int blockid block type id. If null, an array of all block types is returned.
1018 * @param bool $notusedanymore No longer used.
08eab897 1019 * @return array|object row from block table, or all rows.
1020 */
1021function blocks_get_record($blockid = NULL, $notusedanymore = false) {
1022 global $PAGE;
1023 $blocks = $PAGE->blocks->get_installed_blocks();
1024 if ($blockid === NULL) {
1025 return $blocks;
1026 } else if (isset($blocks[$blockid])) {
1027 return $blocks[$blockid];
1028 } else {
1029 return false;
9b4b78fd 1030 }
9b4b78fd 1031}
1032
d4accfc0 1033/**
1034 * Find a given block by its blockid within a provide array
1035 *
1036 * @param int $blockid
1037 * @param array $blocksarray
1038 * @return bool|object Instance if found else false
1039 */
9b4b78fd 1040function blocks_find_block($blockid, $blocksarray) {
0d6b9d4f 1041 if (empty($blocksarray)) {
1042 return false;
1043 }
9b4b78fd 1044 foreach($blocksarray as $blockgroup) {
0d6b9d4f 1045 if (empty($blockgroup)) {
1046 continue;
1047 }
9b4b78fd 1048 foreach($blockgroup as $instance) {
1049 if($instance->blockid == $blockid) {
1050 return $instance;
1051 }
1052 }
1053 }
1054 return false;
1055}
1056
d4accfc0 1057/**
d4a03c00 1058 * TODO Document this function, description
d4accfc0 1059 *
d4accfc0 1060 * @param object $page The page object
1061 * @param object $blockmanager The block manager object
1062 * @param string $blockaction One of [config, add, delete]
1063 * @param int|object $instanceorid The instance id or a block_instance object
1064 * @param bool $pinned
1065 * @param bool $redirect To redirect or not to that is the question but you should stick with true
1066 */
bb46a4fa 1067function blocks_execute_action($page, &$blockmanager, $blockaction, $instanceorid, $pinned=false, $redirect=true) {
58ff964f 1068 global $CFG, $USER, $DB;
9b4b78fd 1069
feed1900 1070 if (!in_array($blockaction, array('config', 'add', 'delete'))) {
1071 throw new moodle_exception('Sorry, blocks editing is currently broken. Will be fixed. See MDL-19010.');
1072 }
1073
a9c75a9c 1074 if (is_int($instanceorid)) {
9b4b78fd 1075 $blockid = $instanceorid;
a9c75a9c 1076 } else if (is_object($instanceorid)) {
9b4b78fd 1077 $instance = $instanceorid;
1078 }
0f3fe4b6 1079
1080 switch($blockaction) {
9b4b78fd 1081 case 'config':
11306331 1082 // First of all check to see if the block wants to be edited
e03c0c1d 1083 if(!$instance->user_can_edit()) {
11306331 1084 break;
9b4b78fd 1085 }
11306331 1086
e82d6cac 1087 // Now get the title and AFTER that load up the instance
e03c0c1d 1088 $blocktitle = $instance->get_title();
afd1ec02 1089
27ec21a0 1090 // Define the data we're going to silently include in the instance config form here,
9b4b78fd 1091 // so we can strip them from the submitted data BEFORE serializing it.
1092 $hiddendata = array(
19f5b2db 1093 'sesskey' => sesskey(),
e03c0c1d 1094 'instanceid' => $instance->instance->id,
9b4b78fd 1095 'blockaction' => 'config'
1096 );
f032aa7a 1097
1098 // To this data, add anything the page itself needs to display
ad52c04f 1099 $hiddendata = $page->url->params($hiddendata);
9b4b78fd 1100
294ce987 1101 if ($data = data_submitted()) {
9b4b78fd 1102 $remove = array_keys($hiddendata);
1103 foreach($remove as $item) {
1104 unset($data->$item);
0f3fe4b6 1105 }
e03c0c1d 1106 $instance->instance_config_save($data);
1107 redirect($page->url->out());
1108
1109 } else {
f032aa7a 1110 // We need to show the config screen, so we highjack the display logic and then die
e82d6cac 1111 $strheading = get_string('blockconfiga', 'moodle', $blocktitle);
e03c0c1d 1112 $nav = build_navigation($strheading, $page->cm);
1113 print_header($strheading, $strheading, $nav);
b9709905 1114
e03c0c1d 1115 echo '<div class="block-config" id="'.$instance->name().'">'; /// Make CSS easier
0be6f678 1116
edb42f09 1117 print_heading($strheading);
ad52c04f 1118 echo '<form method="post" name="block-config" action="'. $page->url->out(false) .'">';
9b4b78fd 1119 echo '<p>';
e03c0c1d 1120 echo $page->url->hidden_params_out(array(), 0, $hiddendata);
9b4b78fd 1121 echo '</p>';
e03c0c1d 1122 $instance->instance_config_print();
9b4b78fd 1123 echo '</form>';
b9709905 1124
1125 echo '</div>';
e03c0c1d 1126 global $PAGE;
1127 $PAGE->set_docs_path('blocks/' . $instance->name());
9b4b78fd 1128 print_footer();
e03c0c1d 1129 die; // Do not go on with the other page-related stuff
0f3fe4b6 1130 }
1131 break;
9b4b78fd 1132 case 'toggle':
1133 if(empty($instance)) {
e49ef64a 1134 print_error('invalidblockinstance', '', '', $blockaction);
0f3fe4b6 1135 }
9b4b78fd 1136 $instance->visible = ($instance->visible) ? 0 : 1;
0d6b9d4f 1137 if (!empty($pinned)) {
66b10689 1138 $DB->update_record('block_pinned_old', $instance);
0d6b9d4f 1139 } else {
66b10689 1140 $DB->update_record('block_instance_old', $instance);
0d6b9d4f 1141 }
9b4b78fd 1142 break;
1143 case 'delete':
1144 if(empty($instance)) {
e49ef64a 1145 print_error('invalidblockinstance', '', '', $blockaction);
0f3fe4b6 1146 }
f4d38d20 1147 blocks_delete_instance($instance->instance, $pinned);
0f3fe4b6 1148 break;
1149 case 'moveup':
9b4b78fd 1150 if(empty($instance)) {
e49ef64a 1151 print_error('invalidblockinstance', '', '', $blockaction);
9b4b78fd 1152 }
f032aa7a 1153
1154 if($instance->weight == 0) {
1155 // The block is the first one, so a move "up" probably means it changes position
1156 // Where is the instance going to be moved?
1157 $newpos = $page->blocks_move_position($instance, BLOCK_MOVE_UP);
bb46a4fa 1158 $newweight = (empty($blockmanager[$newpos]) ? 0 : max(array_keys($blockmanager[$newpos])) + 1);
f032aa7a 1159
0d6b9d4f 1160 blocks_execute_repositioning($instance, $newpos, $newweight, $pinned);
89a5baab 1161 }
f032aa7a 1162 else {
1163 // The block is just moving upwards in the same position.
1164 // This configuration will make sure that even if somehow the weights
1165 // become not continuous, block move operations will eventually bring
1166 // the situation back to normal without printing any warnings.
bb46a4fa 1167 if(!empty($blockmanager[$instance->position][$instance->weight - 1])) {
1168 $other = $blockmanager[$instance->position][$instance->weight - 1];
f032aa7a 1169 }
1170 if(!empty($other)) {
1171 ++$other->weight;
0d6b9d4f 1172 if (!empty($pinned)) {
66b10689 1173 $DB->update_record('block_pinned_old', $other);
0d6b9d4f 1174 } else {
66b10689 1175 $DB->update_record('block_instance_old', $other);
afd1ec02 1176 }
f032aa7a 1177 }
1178 --$instance->weight;
0d6b9d4f 1179 if (!empty($pinned)) {
66b10689 1180 $DB->update_record('block_pinned_old', $instance);
0d6b9d4f 1181 } else {
66b10689 1182 $DB->update_record('block_instance_old', $instance);
0d6b9d4f 1183 }
0f3fe4b6 1184 }
1185 break;
1186 case 'movedown':
9b4b78fd 1187 if(empty($instance)) {
e49ef64a 1188 print_error('invalidblockinstance', '', '', $blockaction);
9b4b78fd 1189 }
f032aa7a 1190
bb46a4fa 1191 if($instance->weight == max(array_keys($blockmanager[$instance->position]))) {
f032aa7a 1192 // The block is the last one, so a move "down" probably means it changes position
1193 // Where is the instance going to be moved?
1194 $newpos = $page->blocks_move_position($instance, BLOCK_MOVE_DOWN);
bb46a4fa 1195 $newweight = (empty($blockmanager[$newpos]) ? 0 : max(array_keys($blockmanager[$newpos])) + 1);
f032aa7a 1196
0d6b9d4f 1197 blocks_execute_repositioning($instance, $newpos, $newweight, $pinned);
89a5baab 1198 }
f032aa7a 1199 else {
1200 // The block is just moving downwards in the same position.
1201 // This configuration will make sure that even if somehow the weights
1202 // become not continuous, block move operations will eventually bring
1203 // the situation back to normal without printing any warnings.
bb46a4fa 1204 if(!empty($blockmanager[$instance->position][$instance->weight + 1])) {
1205 $other = $blockmanager[$instance->position][$instance->weight + 1];
f032aa7a 1206 }
1207 if(!empty($other)) {
1208 --$other->weight;
0d6b9d4f 1209 if (!empty($pinned)) {
66b10689 1210 $DB->update_record('block_pinned_old', $other);
0d6b9d4f 1211 } else {
66b10689 1212 $DB->update_record('block_instance_old', $other);
0d6b9d4f 1213 }
f032aa7a 1214 }
1215 ++$instance->weight;
0d6b9d4f 1216 if (!empty($pinned)) {
66b10689 1217 $DB->update_record('block_pinned_old', $instance);
0d6b9d4f 1218 } else {
66b10689 1219 $DB->update_record('block_instance_old', $instance);
0d6b9d4f 1220 }
0f3fe4b6 1221 }
1222 break;
9b4b78fd 1223 case 'moveleft':
1224 if(empty($instance)) {
e49ef64a 1225 print_error('invalidblockinstance', '', '', $blockaction);
9b4b78fd 1226 }
f032aa7a 1227
1228 // Where is the instance going to be moved?
1229 $newpos = $page->blocks_move_position($instance, BLOCK_MOVE_LEFT);
bb46a4fa 1230 $newweight = (empty($blockmanager[$newpos]) ? 0 : max(array_keys($blockmanager[$newpos])) + 1);
f032aa7a 1231
0d6b9d4f 1232 blocks_execute_repositioning($instance, $newpos, $newweight, $pinned);
0f3fe4b6 1233 break;
9b4b78fd 1234 case 'moveright':
1235 if(empty($instance)) {
e49ef64a 1236 print_error('invalidblockinstance', '', '', $blockaction);
9b4b78fd 1237 }
f032aa7a 1238
1239 // Where is the instance going to be moved?
1240 $newpos = $page->blocks_move_position($instance, BLOCK_MOVE_RIGHT);
bb46a4fa 1241 $newweight = (empty($blockmanager[$newpos]) ? 0 : max(array_keys($blockmanager[$newpos])) + 1);
f032aa7a 1242
0d6b9d4f 1243 blocks_execute_repositioning($instance, $newpos, $newweight, $pinned);
9b4b78fd 1244 break;
1245 case 'add':
1246 // Add a new instance of this block, if allowed
1247 $block = blocks_get_record($blockid);
0f3fe4b6 1248
feed1900 1249 if (empty($block) || !$block->visible) {
3cacefda 1250 // Only allow adding if the block exists and is enabled
11306331 1251 break;
9b4b78fd 1252 }
0f3fe4b6 1253
feed1900 1254 if (!$block->multiple && blocks_find_block($blockid, $blockmanager) !== false) {
89a5baab 1255 // If no multiples are allowed and we already have one, return now
11306331 1256 break;
1257 }
1258
feed1900 1259 if (!block_method_result($block->name, 'user_can_addto', $page)) {
11306331 1260 // If the block doesn't want to be added...
1261 break;
89a5baab 1262 }
1263
feed1900 1264 $region = $page->blocks->get_default_region();
7130fb21 1265 $weight = $DB->get_field_sql("SELECT MAX(defaultweight) FROM {block_instances}
13a0d3d3 1266 WHERE parentcontextid = ? AND defaultregion = ?", array($page->context->id, $region));
feed1900 1267 $pagetypepattern = $page->pagetype;
1268 if (strpos($pagetypepattern, 'course-view') === 0) {
1269 $pagetypepattern = 'course-view-*';
b33dd23a 1270 }
feed1900 1271 $page->blocks->add_block($block->name, $region, $weight, false, $pagetypepattern);
9b4b78fd 1272 break;
1273 }
f032aa7a 1274
b1631fef 1275 if ($redirect) {
1276 // In order to prevent accidental duplicate actions, redirect to a page with a clean url
ad52c04f 1277 redirect($page->url->out());
b1631fef 1278 }
f032aa7a 1279}
1280
d4accfc0 1281/**
d4a03c00 1282 * TODO deprecate
1283 *
d4accfc0 1284 * You can use this to get the blocks to respond to URL actions without much hassle
1285 *
d4accfc0 1286 * @param object $PAGE
1287 * @param object $blockmanager
1288 * @param bool $pinned
1289 */
bb46a4fa 1290function blocks_execute_url_action(&$PAGE, &$blockmanager,$pinned=false) {
02cc05a7 1291 $blockaction = optional_param('blockaction', '', PARAM_ALPHA);
da71112b 1292
3edc57e1 1293 if (empty($blockaction) || !$PAGE->user_allowed_editing() || !confirm_sesskey()) {
da71112b 1294 return;
1295 }
1296
1297 $instanceid = optional_param('instanceid', 0, PARAM_INT);
1298 $blockid = optional_param('blockid', 0, PARAM_INT);
afd1ec02 1299
da71112b 1300 if (!empty($blockid)) {
bb46a4fa 1301 blocks_execute_action($PAGE, $blockmanager, strtolower($blockaction), $blockid, $pinned);
f4d38d20 1302 } else if (!empty($instanceid)) {
1303 $instance = $blockmanager->find_instance($instanceid);
bb46a4fa 1304 blocks_execute_action($PAGE, $blockmanager, strtolower($blockaction), $instance, $pinned);
da71112b 1305 }
1306}
1307
d4accfc0 1308/**
d4a03c00 1309 * TODO deprecate
d4accfc0 1310 * This shouldn't be used externally at all, it's here for use by blocks_execute_action()
1311 * in order to reduce code repetition.
1312 *
1313 * @todo Remove exception when MDL-19010 is fixed
1314 *
1315 * global object
1316 * @param $instance
1317 * @param $newpos
1318 * @param string|int $newweight
1319 * @param bool $pinned
1320 */
29ca8b88 1321function blocks_execute_repositioning(&$instance, $newpos, $newweight, $pinned=false) {
58ff964f 1322 global $DB;
f032aa7a 1323
feed1900 1324 throw new moodle_exception('Sorry, blocks editing is currently broken. Will be fixed. See MDL-19010.');
1325
c4308cfa 1326 // If it's staying where it is, don't do anything, unless overridden
29ca8b88 1327 if ($newpos == $instance->position) {
f032aa7a 1328 return;
1329 }
1330
1331 // Close the weight gap we 'll leave behind
0d6b9d4f 1332 if (!empty($pinned)) {
66b10689 1333 $sql = "UPDATE {block_instance_old}
58ff964f 1334 SET weight = weight - 1
1335 WHERE pagetype = ? AND position = ? AND weight > ?";
1336 $params = array($instance->pagetype, $instance->position, $instance->weight);
1337
0d6b9d4f 1338 } else {
66b10689 1339 $sql = "UPDATE {block_instance_old}
58ff964f 1340 SET weight = weight - 1
1341 WHERE pagetype = ? AND pageid = ?
1342 AND position = ? AND weight > ?";
1343 $params = array($instance->pagetype, $instance->pageid, $instance->position, $instance->weight);
0d6b9d4f 1344 }
58ff964f 1345 $DB->execute($sql, $params);
f032aa7a 1346
1347 $instance->position = $newpos;
1348 $instance->weight = $newweight;
1349
0d6b9d4f 1350 if (!empty($pinned)) {
66b10689 1351 $DB->update_record('block_pinned_old', $instance);
0d6b9d4f 1352 } else {
66b10689 1353 $DB->update_record('block_instance_old', $instance);
0d6b9d4f 1354 }
1355}
1356
29ca8b88 1357
1358/**
d4a03c00 1359 * TODO deprecate
29ca8b88 1360 * Moves a block to the new position (column) and weight (sort order).
d4accfc0 1361 *
d4accfc0 1362 * @param object $instance The block instance to be moved.
1363 * @param string $destpos BLOCK_POS_LEFT or BLOCK_POS_RIGHT. The destination column.
1364 * @param string $destweight The destination sort order. If NULL, we add to the end
1365 * of the destination column.
1366 * @param bool $pinned Are we moving pinned blocks? We can only move pinned blocks
1367 * to a new position withing the pinned list. Likewise, we
1368 * can only moved non-pinned blocks to a new position within
1369 * the non-pinned list.
1370 * @return boolean success or failure
29ca8b88 1371 */
1372function blocks_move_block($page, &$instance, $destpos, $destweight=NULL, $pinned=false) {
58ff964f 1373 global $CFG, $DB;
afd1ec02 1374
feed1900 1375 throw new moodle_exception('Sorry, blocks editing is currently broken. Will be fixed. See MDL-19010.');
1376
29ca8b88 1377 if ($pinned) {
d4a03c00 1378 $blocklist = array(); //blocks_get_pinned($page);
29ca8b88 1379 } else {
d4a03c00 1380 $blocklist = array(); //blocks_get_by_page($page);
29ca8b88 1381 }
afd1ec02 1382
29ca8b88 1383 if ($blocklist[$instance->position][$instance->weight]->id != $instance->id) {
1384 // The source block instance is not where we think it is.
c4308cfa 1385 return false;
d23157d8 1386 }
afd1ec02 1387
29ca8b88 1388 // First we close the gap that will be left behind when we take out the
1389 // block from it's current column.
1390 if ($pinned) {
66b10689 1391 $closegapsql = "UPDATE {block_instance_old}
afd1ec02 1392 SET weight = weight - 1
58ff964f 1393 WHERE weight > ? AND position = ? AND pagetype = ?";
1394 $params = array($instance->weight, $instance->position, $instance->pagetype);
e028ed34 1395 } else {
66b10689 1396 $closegapsql = "UPDATE {block_instance_old}
afd1ec02 1397 SET weight = weight - 1
58ff964f 1398 WHERE weight > ? AND position = ?
1399 AND pagetype = ? AND pageid = ?";
1400 $params = array($instance->weight, $instance->position, $instance->pagetype, $instance->pageid);
29ca8b88 1401 }
58ff964f 1402 if (!$DB->execute($closegapsql, $params)) {
29ca8b88 1403 return false;
77e65ff7 1404 }
afd1ec02 1405
29ca8b88 1406 // Now let's make space for the block being moved.
1407 if ($pinned) {
66b10689 1408 $opengapsql = "UPDATE {block_instance_old}
afd1ec02 1409 SET weight = weight + 1
58ff964f 1410 WHERE weight >= ? AND position = ? AND pagetype = ?";
1411 $params = array($destweight, $destpos, $instance->pagetype);
d23157d8 1412 } else {
66b10689 1413 $opengapsql = "UPDATE {block_instance_old}
58ff964f 1414 SET weight = weight + 1
1415 WHERE weight >= ? AND position = ?
1416 AND pagetype = ? AND pageid = ?";
1417 $params = array($destweight, $destpos, $instance->pagetype, $instance->pageid);
29ca8b88 1418 }
655b09ca 1419 if (!$DB->execute($opengapsql, $params)) {
29ca8b88 1420 return false;
c4308cfa 1421 }
afd1ec02 1422
29ca8b88 1423 // Move the block.
1424 $instance->position = $destpos;
1425 $instance->weight = $destweight;
e028ed34 1426
29ca8b88 1427 if ($pinned) {
66b10689 1428 $table = 'block_pinned_old';
29ca8b88 1429 } else {
66b10689 1430 $table = 'block_instance_old';
29ca8b88 1431 }
58ff964f 1432 return $DB->update_record($table, $instance);
e028ed34 1433}
1434
d4a03c00 1435// Functions for programatically adding default blocks to pages ================
0f3fe4b6 1436
9d1d606e 1437/**
1438 * Parse a list of default blocks. See config-dist for a description of the format.
d4accfc0 1439 *
9d1d606e 1440 * @param string $blocksstr
1441 * @return array
1442 */
1443function blocks_parse_default_blocks_list($blocksstr) {
f474a4e5 1444 $blocks = array();
1445 $bits = explode(':', $blocksstr);
1446 if (!empty($bits)) {
1447 $blocks[BLOCK_POS_LEFT] = explode(',', array_shift($bits));
1448 }
1449 if (!empty($bits)) {
1450 $blocks[BLOCK_POS_RIGHT] = explode(',', array_shift($bits));
1451 }
1452 return $blocks;
9d1d606e 1453}
5b224948 1454
9d1d606e 1455/**
1456 * @return array the blocks that should be added to the site course by default.
1457 */
1458function blocks_get_default_site_course_blocks() {
1459 global $CFG;
9b4b78fd 1460
9d1d606e 1461 if (!empty($CFG->defaultblocks_site)) {
f474a4e5 1462 return blocks_parse_default_blocks_list($CFG->defaultblocks_site);
9d1d606e 1463 } else {
f474a4e5 1464 return array(
9d1d606e 1465 BLOCK_POS_LEFT => array('site_main_menu', 'admin_tree'),
1466 BLOCK_POS_RIGHT => array('course_summary', 'calendar_month')
1467 );
9b4b78fd 1468 }
9d1d606e 1469}
1470
1471/**
1472 * Add the default blocks to a course.
d4accfc0 1473 *
9d1d606e 1474 * @param object $course a course object.
1475 */
1476function blocks_add_default_course_blocks($course) {
1477 global $CFG;
1478
1479 if (!empty($CFG->defaultblocks_override)) {
1480 $blocknames = blocks_parse_default_blocks_list($CFG->defaultblocks_override);
1481
1482 } else if ($course->id == SITEID) {
1483 $blocknames = blocks_get_default_site_course_blocks();
1484
1485 } else {
1486 $defaultblocks = 'defaultblocks_' . $course->format;
1487 if (!empty($CFG->$defaultblocks)) {
1488 $blocknames = blocks_parse_default_blocks_list($CFG->$defaultblocks);
1489
1490 } else {
1d00ec6a 1491 $formatconfig = $CFG->dirroot.'/course/format/'.$course->format.'/config.php';
1492 if (is_readable($formatconfig)) {
9d1d606e 1493 require($formatconfig);
1494 }
1495 if (!empty($format['defaultblocks'])) {
1496 $blocknames = blocks_parse_default_blocks_list($format['defaultblocks']);
9b4b78fd 1497
9d1d606e 1498 } else if (!empty($CFG->defaultblocks)){
1499 $blocknames = blocks_parse_default_blocks_list($CFG->defaultblocks);
1500
1501 } else {
1502 $blocknames = array(
1503 BLOCK_POS_LEFT => array('participants', 'activity_modules', 'search_forums', 'admin', 'course_list'),
1504 BLOCK_POS_RIGHT => array('news_items', 'calendar_upcoming', 'recent_activity')
1505 );
1506 }
1507 }
9b4b78fd 1508 }
1509
f474a4e5 1510 if ($course->id == SITEID) {
1511 $pagetypepattern = 'site-index';
1512 } else {
1513 $pagetypepattern = 'course-view-*';
1514 }
1515
9d1d606e 1516 $page = new moodle_page();
1517 $page->set_course($course);
f474a4e5 1518 $page->blocks->add_blocks($blocknames, $pagetypepattern);
9d1d606e 1519}
1520
1521/**
1522 * Add the default system-context blocks. E.g. the admin tree.
1523 */
1524function blocks_add_default_system_blocks() {
1525 $page = new moodle_page();
1526 $page->set_context(get_context_instance(CONTEXT_SYSTEM));
1527 $page->blocks->add_blocks(array(BLOCK_POS_LEFT => array('admin_tree', 'admin_bookmarks')), 'admin-*');
1528}