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