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