MDL-27829 blocks: added some brains to generate_page_type_patterns() so that it can...
[moodle.git] / lib / blocklib.php
CommitLineData
d4accfc0 1<?php
2
a19f419d 3// This file is part of Moodle - http://moodle.org/
4//
d4accfc0 5// Moodle is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// Moodle is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
a19f419d 14//
d4accfc0 15// You should have received a copy of the GNU General Public License
16// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
e8c8cee9 17
18/**
d4accfc0 19 * Block Class and Functions
e8c8cee9 20 *
a19f419d 21 * This file defines the {@link block_manager} class,
d4a03c00 22 *
78bfb562
PS
23 * @package core
24 * @subpackage block
25 * @copyright 1999 onwards Martin Dougiamas http://dougiamas.com
26 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
e8c8cee9 27 */
0f3fe4b6 28
78bfb562
PS
29defined('MOODLE_INTERNAL') || die();
30
13a0d3d3 31/**#@+
32 * @deprecated since Moodle 2.0. No longer used.
d4a03c00 33 */
0f3fe4b6 34define('BLOCK_MOVE_LEFT', 0x01);
35define('BLOCK_MOVE_RIGHT', 0x02);
36define('BLOCK_MOVE_UP', 0x04);
37define('BLOCK_MOVE_DOWN', 0x08);
9b4b78fd 38define('BLOCK_CONFIGURE', 0x10);
13a0d3d3 39/**#@-*/
0f3fe4b6 40
13a0d3d3 41/**#@+
42 * Default names for the block regions in the standard theme.
43 */
bb46a4fa 44define('BLOCK_POS_LEFT', 'side-pre');
45define('BLOCK_POS_RIGHT', 'side-post');
13a0d3d3 46/**#@-*/
0e9af917 47
13a0d3d3 48/**#@+
49 * @deprecated since Moodle 2.0. No longer used.
50 */
ee6055eb 51define('BLOCKS_PINNED_TRUE',0);
52define('BLOCKS_PINNED_FALSE',1);
53define('BLOCKS_PINNED_BOTH',2);
13a0d3d3 54/**#@-*/
ee6055eb 55
b1627a92
DC
56define('BUI_CONTEXTS_FRONTPAGE_ONLY', 0);
57define('BUI_CONTEXTS_FRONTPAGE_SUBS', 1);
58define('BUI_CONTEXTS_ENTIRE_SITE', 2);
59
60define('BUI_CONTEXTS_CURRENT', 0);
61define('BUI_CONTEXTS_CURRENT_SUBS', 1);
62
d4accfc0 63/**
d4a03c00 64 * Exception thrown when someone tried to do something with a block that does
65 * not exist on a page.
d4accfc0 66 *
d4a03c00 67 * @copyright 2009 Tim Hunt
68 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
69 * @since Moodle 2.0
d4accfc0 70 */
f4d38d20 71class block_not_on_page_exception extends moodle_exception {
d4accfc0 72 /**
847bed23 73 * Constructor
d4a03c00 74 * @param int $instanceid the block instance id of the block that was looked for.
75 * @param object $page the current page.
d4accfc0 76 */
f4d38d20 77 public function __construct($instanceid, $page) {
78 $a = new stdClass;
79 $a->instanceid = $instanceid;
2a3b0763 80 $a->url = $page->url->out();
81 parent::__construct('blockdoesnotexistonpage', '', $page->url->out(), $a);
f4d38d20 82 }
83}
84
86b5ea0f 85/**
86 * This class keeps track of the block that should appear on a moodle_page.
bb46a4fa 87 *
d4a03c00 88 * The page to work with as passed to the constructor.
1d00ec6a 89 *
d4a03c00 90 * @copyright 2009 Tim Hunt
91 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
92 * @since Moodle 2.0
86b5ea0f 93 */
d4a03c00 94class block_manager {
2cdb8d84 95 /**
96 * The UI normally only shows block weights between -MAX_WEIGHT and MAX_WEIGHT,
97 * although other weights are valid.
98 */
99 const MAX_WEIGHT = 10;
86b5ea0f 100
101/// Field declarations =========================================================
d4a03c00 102
d8ef60bd
SH
103 /**
104 * the moodle_page we are managing blocks for.
105 * @var moodle_page
106 */
86b5ea0f 107 protected $page;
d4a03c00 108
109 /** @var array region name => 1.*/
86b5ea0f 110 protected $regions = array();
d4a03c00 111
112 /** @var string the region where new blocks are added.*/
113 protected $defaultregion = null;
114
115 /** @var array will be $DB->get_records('blocks') */
116 protected $allblocks = null;
117
118 /**
119 * @var array blocks that this user can add to this page. Will be a subset
a2789e34 120 * of $allblocks, but with array keys block->name. Access this via the
121 * {@link get_addable_blocks()} method to ensure it is lazy-loaded.
d4a03c00 122 */
123 protected $addableblocks = null;
08eab897 124
bb46a4fa 125 /**
126 * Will be an array region-name => array(db rows loaded in load_blocks);
d4accfc0 127 * @var array
bb46a4fa 128 */
129 protected $birecordsbyregion = null;
130
131 /**
132 * array region-name => array(block objects); populated as necessary by
133 * the ensure_instances_exist method.
d4accfc0 134 * @var array
bb46a4fa 135 */
136 protected $blockinstances = array();
137
138 /**
847bed23 139 * array region-name => array(block_contents objects) what actually needs to
bb46a4fa 140 * be displayed in each region.
d4accfc0 141 * @var array
bb46a4fa 142 */
143 protected $visibleblockcontent = array();
08eab897 144
d4a03c00 145 /**
146 * array region-name => array(block_contents objects) extra block-like things
147 * to be displayed in each region, before the real blocks.
148 * @var array
149 */
150 protected $extracontent = array();
151
00a24d44 152 /**
847bed23 153 * Used by the block move id, to track whether a block is currently being moved.
00a24d44 154 *
847bed23
PS
155 * When you click on the move icon of a block, first the page needs to reload with
156 * extra UI for choosing a new position for a particular block. In that situation
00a24d44 157 * this field holds the id of the block being moved.
158 *
159 * @var integer|null
160 */
161 protected $movingblock = null;
162
56ed242b
SH
163 /**
164 * Show only fake blocks
165 */
166 protected $fakeblocksonly = false;
167
86b5ea0f 168/// Constructor ================================================================
169
170 /**
171 * Constructor.
172 * @param object $page the moodle_page object object we are managing the blocks for,
847bed23 173 * or a reasonable faxilimily. (See the comment at the top of this class
d4accfc0 174 * and {@link http://en.wikipedia.org/wiki/Duck_typing})
86b5ea0f 175 */
176 public function __construct($page) {
177 $this->page = $page;
178 }
179
180/// Getter methods =============================================================
181
182 /**
d4accfc0 183 * Get an array of all region names on this page where a block may appear
184 *
86b5ea0f 185 * @return array the internal names of the regions on this page where block may appear.
186 */
187 public function get_regions() {
78946b9b 188 if (is_null($this->defaultregion)) {
7d875874 189 $this->page->initialise_theme_and_output();
78946b9b 190 }
86b5ea0f 191 return array_keys($this->regions);
192 }
193
194 /**
d4accfc0 195 * Get the region name of the region blocks are added to by default
196 *
86b5ea0f 197 * @return string the internal names of the region where new blocks are added
198 * by default, and where any blocks from an unrecognised region are shown.
199 * (Imagine that blocks were added with one theme selected, then you switched
200 * to a theme with different block positions.)
201 */
202 public function get_default_region() {
d4a03c00 203 $this->page->initialise_theme_and_output();
86b5ea0f 204 return $this->defaultregion;
205 }
206
08eab897 207 /**
208 * The list of block types that may be added to this page.
d4accfc0 209 *
727ae436 210 * @return array block name => record from block table.
08eab897 211 */
212 public function get_addable_blocks() {
213 $this->check_is_loaded();
214
215 if (!is_null($this->addableblocks)) {
216 return $this->addableblocks;
217 }
218
219 // Lazy load.
220 $this->addableblocks = array();
221
222 $allblocks = blocks_get_record();
223 if (empty($allblocks)) {
224 return $this->addableblocks;
225 }
226
bb46a4fa 227 $pageformat = $this->page->pagetype;
08eab897 228 foreach($allblocks as $block) {
229 if ($block->visible &&
464c7e70 230 (block_method_result($block->name, 'instance_allow_multiple') || !$this->is_block_present($block->name)) &&
a2789e34 231 blocks_name_allowed_in_format($block->name, $pageformat) &&
232 block_method_result($block->name, 'user_can_addto', $this->page)) {
233 $this->addableblocks[$block->name] = $block;
08eab897 234 }
235 }
236
237 return $this->addableblocks;
238 }
239
d4accfc0 240 /**
464c7e70
MD
241 * Given a block name, find out of any of them are currently present in the page
242
243 * @param string $blockname - the basic name of a block (eg "navigation")
244 * @return boolean - is there one of these blocks in the current page?
d4accfc0 245 */
464c7e70
MD
246 public function is_block_present($blockname) {
247 if (empty($this->blockinstances)) {
248 return false;
249 }
250
251 foreach ($this->blockinstances as $region) {
252 foreach ($region as $instance) {
253 if (empty($instance->instance->blockname)) {
254 continue;
255 }
256 if ($instance->instance->blockname == $blockname) {
257 return true;
258 }
259 }
260 }
261 return false;
08eab897 262 }
263
264 /**
d4accfc0 265 * Find out if a block type is known by the system
266 *
847bed23 267 * @param string $blockname the name of the type of block.
08eab897 268 * @param boolean $includeinvisible if false (default) only check 'visible' blocks, that is, blocks enabled by the admin.
269 * @return boolean true if this block in installed.
270 */
271 public function is_known_block_type($blockname, $includeinvisible = false) {
272 $blocks = $this->get_installed_blocks();
273 foreach ($blocks as $block) {
274 if ($block->name == $blockname && ($includeinvisible || $block->visible)) {
275 return true;
276 }
277 }
278 return false;
279 }
280
281 /**
d4accfc0 282 * Find out if a region exists on a page
283 *
08eab897 284 * @param string $region a region name
847bed23 285 * @return boolean true if this region exists on this page.
08eab897 286 */
287 public function is_known_region($region) {
288 return array_key_exists($region, $this->regions);
289 }
290
291 /**
d4accfc0 292 * Get an array of all blocks within a given region
293 *
294 * @param string $region a block region that exists on this page.
08eab897 295 * @return array of block instances.
296 */
297 public function get_blocks_for_region($region) {
298 $this->check_is_loaded();
bb46a4fa 299 $this->ensure_instances_exist($region);
300 return $this->blockinstances[$region];
301 }
302
303 /**
d4accfc0 304 * Returns an array of block content objects that exist in a region
305 *
d4a03c00 306 * @param string $region a block region that exists on this page.
307 * @return array of block block_contents objects for all the blocks in a region.
bb46a4fa 308 */
d4a03c00 309 public function get_content_for_region($region, $output) {
bb46a4fa 310 $this->check_is_loaded();
d4a03c00 311 $this->ensure_content_created($region, $output);
bb46a4fa 312 return $this->visibleblockcontent[$region];
08eab897 313 }
314
00a24d44 315 /**
316 * Helper method used by get_content_for_region.
317 * @param string $region region name
318 * @param float $weight weight. May be fractional, since you may want to move a block
319 * between ones with weight 2 and 3, say ($weight would be 2.5).
320 * @return string URL for moving block $this->movingblock to this position.
321 */
322 protected function get_move_target_url($region, $weight) {
dd72b308 323 return new moodle_url($this->page->url, array('bui_moveid' => $this->movingblock,
b9bc2019 324 'bui_newregion' => $region, 'bui_newweight' => $weight, 'sesskey' => sesskey()));
00a24d44 325 }
326
d4a03c00 327 /**
328 * Determine whether a region contains anything. (Either any real blocks, or
329 * the add new block UI.)
78d27a90 330 *
331 * (You may wonder why the $output parameter is required. Unfortunately,
847bed23 332 * because of the way that blocks work, the only reliable way to find out
78d27a90 333 * if a block will be visible is to get the content for output, and to
334 * get the content, you need a renderer. Fortunately, this is not a
847bed23 335 * performance problem, because we cache the output that is generated, and
78d27a90 336 * in almost every case where we call region_has_content, we are about to
337 * output the blocks anyway, so we are not doing wasted effort.)
338 *
d4a03c00 339 * @param string $region a block region that exists on this page.
78946b9b 340 * @param object $output a core_renderer. normally the global $OUTPUT.
d4a03c00 341 * @return boolean Whether there is anything in this region.
342 */
78d27a90 343 public function region_has_content($region, $output) {
4f0c2d00 344
d4a03c00 345 if (!$this->is_known_region($region)) {
346 return false;
347 }
348 $this->check_is_loaded();
78d27a90 349 $this->ensure_content_created($region, $output);
d4a03c00 350 if ($this->page->user_is_editing() && $this->page->user_can_edit_blocks()) {
351 // If editing is on, we need all the block regions visible, for the
352 // move blocks UI.
353 return true;
354 }
78d27a90 355 return !empty($this->visibleblockcontent[$region]) || !empty($this->extracontent[$region]);
d4a03c00 356 }
357
08eab897 358 /**
d4accfc0 359 * Get an array of all of the installed blocks.
360 *
08eab897 361 * @return array contents of the block table.
362 */
363 public function get_installed_blocks() {
364 global $DB;
365 if (is_null($this->allblocks)) {
366 $this->allblocks = $DB->get_records('block');
367 }
368 return $this->allblocks;
369 }
370
86b5ea0f 371/// Setter methods =============================================================
372
373 /**
d4accfc0 374 * Add a region to a page
375 *
86b5ea0f 376 * @param string $region add a named region where blocks may appear on the
377 * current page. This is an internal name, like 'side-pre', not a string to
378 * display in the UI.
379 */
380 public function add_region($region) {
381 $this->check_not_yet_loaded();
382 $this->regions[$region] = 1;
383 }
384
385 /**
d4accfc0 386 * Add an array of regions
387 * @see add_region()
388 *
86b5ea0f 389 * @param array $regions this utility method calls add_region for each array element.
390 */
391 public function add_regions($regions) {
392 foreach ($regions as $region) {
393 $this->add_region($region);
394 }
395 }
396
397 /**
d4accfc0 398 * Set the default region for new blocks on the page
399 *
86b5ea0f 400 * @param string $defaultregion the internal names of the region where new
401 * blocks should be added by default, and where any blocks from an
402 * unrecognised region are shown.
403 */
404 public function set_default_region($defaultregion) {
405 $this->check_not_yet_loaded();
035b96a9 406 if ($defaultregion) {
407 $this->check_region_is_known($defaultregion);
408 }
86b5ea0f 409 $this->defaultregion = $defaultregion;
410 }
411
d4a03c00 412 /**
413 * Add something that looks like a block, but which isn't an actual block_instance,
414 * to this page.
415 *
d9c26e21 416 * @param block_contents $bc the content of the block-like thing.
d4a03c00 417 * @param string $region a block region that exists on this page.
418 */
d9c26e21 419 public function add_fake_block($bc, $region) {
d4a03c00 420 $this->page->initialise_theme_and_output();
bf2b43df
SH
421 if (!$this->is_known_region($region)) {
422 $region = $this->get_default_region();
423 }
d4a03c00 424 if (array_key_exists($region, $this->visibleblockcontent)) {
425 throw new coding_exception('block_manager has already prepared the blocks in region ' .
d9c26e21 426 $region . 'for output. It is too late to add a fake block.');
d4a03c00 427 }
428 $this->extracontent[$region][] = $bc;
429 }
430
d9c26e21
TH
431 /**
432 * When the block_manager class was created, the {@link add_fake_block()}
433 * was called add_pretend_block, which is inconsisted with
434 * {@link show_only_fake_blocks()}. To fix this inconsistency, this method
435 * was renamed to add_fake_block. Please update your code.
436 * @param block_contents $bc the content of the block-like thing.
437 * @param string $region a block region that exists on this page.
438 */
439 public function add_pretend_block($bc, $region) {
440 debugging(DEBUG_DEVELOPER, 'add_pretend_block has been renamed to add_fake_block. Please rename the method call in your code.');
441 $this->add_fake_block($bc, $region);
442 }
443
7e4617f7
SH
444 /**
445 * Checks to see whether all of the blocks within the given region are docked
446 *
4d6fd15c 447 * @see region_uses_dock
7e4617f7
SH
448 * @param string $region
449 * @return bool True if all of the blocks within that region are docked
450 */
451 public function region_completely_docked($region, $output) {
452 if (!$this->page->theme->enable_dock) {
453 return false;
454 }
455 $this->check_is_loaded();
456 $this->ensure_content_created($region, $output);
457 foreach($this->visibleblockcontent[$region] as $instance) {
458 if (!empty($instance->content) && !get_user_preferences('docked_block_instance_'.$instance->blockinstanceid, 0)) {
459 return false;
460 }
461 }
462 return true;
463 }
464
4d6fd15c
SH
465 /**
466 * Checks to see whether any of the blocks within the given regions are docked
467 *
468 * @see region_completely_docked
469 * @param array|string $regions array of regions (or single region)
470 * @return bool True if any of the blocks within that region are docked
471 */
472 public function region_uses_dock($regions, $output) {
473 if (!$this->page->theme->enable_dock) {
474 return false;
475 }
476 $this->check_is_loaded();
477 foreach((array)$regions as $region) {
478 $this->ensure_content_created($region, $output);
479 foreach($this->visibleblockcontent[$region] as $instance) {
480 if(!empty($instance->content) && get_user_preferences('docked_block_instance_'.$instance->blockinstanceid, 0)) {
481 return true;
482 }
483 }
484 }
485 return false;
486 }
487
08eab897 488/// Actions ====================================================================
489
490 /**
491 * This method actually loads the blocks for our page from the database.
d4accfc0 492 *
ae42ff6f 493 * @param boolean|null $includeinvisible
494 * null (default) - load hidden blocks if $this->page->user_is_editing();
495 * true - load hidden blocks.
496 * false - don't load hidden blocks.
08eab897 497 */
ae42ff6f 498 public function load_blocks($includeinvisible = null) {
d19e8195 499 global $DB, $CFG;
7d875874 500
bb46a4fa 501 if (!is_null($this->birecordsbyregion)) {
502 // Already done.
503 return;
504 }
08eab897 505
d19e8195 506 if ($CFG->version < 2009050619) {
507 // Upgrade/install not complete. Don't try too show any blocks.
508 $this->birecordsbyregion = array();
509 return;
510 }
511
d4a03c00 512 // Ensure we have been initialised.
7d875874 513 if (is_null($this->defaultregion)) {
b7009474 514 $this->page->initialise_theme_and_output();
d4a03c00 515 // If there are still no block regions, then there are no blocks on this page.
516 if (empty($this->regions)) {
517 $this->birecordsbyregion = array();
518 return;
519 }
b7009474 520 }
521
56ed242b
SH
522 // Check if we need to load normal blocks
523 if ($this->fakeblocksonly) {
524 $this->birecordsbyregion = $this->prepare_per_region_arrays();
525 return;
526 }
527
08eab897 528 if (is_null($includeinvisible)) {
529 $includeinvisible = $this->page->user_is_editing();
530 }
531 if ($includeinvisible) {
08eab897 532 $visiblecheck = '';
ae42ff6f 533 } else {
534 $visiblecheck = 'AND (bp.visible = 1 OR bp.visible IS NULL)';
08eab897 535 }
536
537 $context = $this->page->context;
13a0d3d3 538 $contexttest = 'bi.parentcontextid = :contextid2';
08eab897 539 $parentcontextparams = array();
540 $parentcontextids = get_parent_contexts($context);
541 if ($parentcontextids) {
542 list($parentcontexttest, $parentcontextparams) =
cf717dc2 543 $DB->get_in_or_equal($parentcontextids, SQL_PARAMS_NAMED, 'parentcontext');
13a0d3d3 544 $contexttest = "($contexttest OR (bi.showinsubcontexts = 1 AND bi.parentcontextid $parentcontexttest))";
08eab897 545 }
546
1d13c75c 547 $pagetypepatterns = matching_page_type_patterns($this->page->pagetype);
08eab897 548 list($pagetypepatterntest, $pagetypepatternparams) =
cf717dc2 549 $DB->get_in_or_equal($pagetypepatterns, SQL_PARAMS_NAMED, 'pagetypepatterntest');
08eab897 550
faca4f2f 551 list($ccselect, $ccjoin) = context_instance_preload_sql('bi.id', CONTEXT_BLOCK, 'ctx');
4f0c2d00 552
08eab897 553 $params = array(
554 'subpage1' => $this->page->subpage,
555 'subpage2' => $this->page->subpage,
556 'contextid1' => $context->id,
557 'contextid2' => $context->id,
558 'pagetype' => $this->page->pagetype,
559 );
560 $sql = "SELECT
561 bi.id,
d4a03c00 562 bp.id AS blockpositionid,
08eab897 563 bi.blockname,
13a0d3d3 564 bi.parentcontextid,
08eab897 565 bi.showinsubcontexts,
566 bi.pagetypepattern,
567 bi.subpagepattern,
ae42ff6f 568 bi.defaultregion,
569 bi.defaultweight,
bb46a4fa 570 COALESCE(bp.visible, 1) AS visible,
08eab897 571 COALESCE(bp.region, bi.defaultregion) AS region,
572 COALESCE(bp.weight, bi.defaultweight) AS weight,
4f0c2d00
PS
573 bi.configdata
574 $ccselect
08eab897 575
576 FROM {block_instances} bi
577 JOIN {block} b ON bi.blockname = b.name
578 LEFT JOIN {block_positions} bp ON bp.blockinstanceid = bi.id
579 AND bp.contextid = :contextid1
580 AND bp.pagetype = :pagetype
581 AND bp.subpage = :subpage1
4f0c2d00 582 $ccjoin
08eab897 583
584 WHERE
585 $contexttest
586 AND bi.pagetypepattern $pagetypepatterntest
587 AND (bi.subpagepattern IS NULL OR bi.subpagepattern = :subpage2)
588 $visiblecheck
589 AND b.visible = 1
590
591 ORDER BY
592 COALESCE(bp.region, bi.defaultregion),
593 COALESCE(bp.weight, bi.defaultweight),
594 bi.id";
595 $blockinstances = $DB->get_recordset_sql($sql, $params + $parentcontextparams + $pagetypepatternparams);
596
bb46a4fa 597 $this->birecordsbyregion = $this->prepare_per_region_arrays();
08eab897 598 $unknown = array();
08eab897 599 foreach ($blockinstances as $bi) {
4f0c2d00 600 context_instance_preload($bi);
08eab897 601 if ($this->is_known_region($bi->region)) {
bb46a4fa 602 $this->birecordsbyregion[$bi->region][] = $bi;
08eab897 603 } else {
604 $unknown[] = $bi;
605 }
606 }
d4a03c00 607
608 // Pages don't necessarily have a defaultregion. The one time this can
609 // happen is when there are no theme block regions, but the script itself
610 // has a block region in the main content area.
611 if (!empty($this->defaultregion)) {
612 $this->birecordsbyregion[$this->defaultregion] =
613 array_merge($this->birecordsbyregion[$this->defaultregion], $unknown);
614 }
08eab897 615 }
616
617 /**
618 * Add a block to the current page, or related pages. The block is added to
619 * context $this->page->contextid. If $pagetypepattern $subpagepattern
d4accfc0 620 *
08eab897 621 * @param string $blockname The type of block to add.
622 * @param string $region the block region on this page to add the block to.
623 * @param integer $weight determines the order where this block appears in the region.
624 * @param boolean $showinsubcontexts whether this block appears in subcontexts, or just the current context.
625 * @param string|null $pagetypepattern which page types this block should appear on. Defaults to just the current page type.
626 * @param string|null $subpagepattern which subpage this block should appear on. NULL = any (the default), otherwise only the specified subpage.
627 */
628 public function add_block($blockname, $region, $weight, $showinsubcontexts, $pagetypepattern = NULL, $subpagepattern = NULL) {
629 global $DB;
51f96f5b
SM
630 // Allow invisible blocks because this is used when adding default page blocks, which
631 // might include invisible ones if the user makes some default blocks invisible
632 $this->check_known_block_type($blockname, true);
08eab897 633 $this->check_region_is_known($region);
634
635 if (empty($pagetypepattern)) {
636 $pagetypepattern = $this->page->pagetype;
637 }
638
639 $blockinstance = new stdClass;
640 $blockinstance->blockname = $blockname;
13a0d3d3 641 $blockinstance->parentcontextid = $this->page->context->id;
08eab897 642 $blockinstance->showinsubcontexts = !empty($showinsubcontexts);
643 $blockinstance->pagetypepattern = $pagetypepattern;
644 $blockinstance->subpagepattern = $subpagepattern;
645 $blockinstance->defaultregion = $region;
646 $blockinstance->defaultweight = $weight;
647 $blockinstance->configdata = '';
feed1900 648 $blockinstance->id = $DB->insert_record('block_instances', $blockinstance);
649
e92c286c 650 // Ensure the block context is created.
651 get_context_instance(CONTEXT_BLOCK, $blockinstance->id);
e03c0c1d 652
feed1900 653 // If the new instance was created, allow it to do additional setup
e92c286c 654 if ($block = block_instance($blockname, $blockinstance)) {
feed1900 655 $block->instance_create();
656 }
08eab897 657 }
658
21d33bdf 659 public function add_block_at_end_of_default_region($blockname) {
660 $defaulregion = $this->get_default_region();
2a3b0763 661
21d33bdf 662 $lastcurrentblock = end($this->birecordsbyregion[$defaulregion]);
2a3b0763 663 if ($lastcurrentblock) {
664 $weight = $lastcurrentblock->weight + 1;
665 } else {
666 $weight = 0;
667 }
668
21d33bdf 669 if ($this->page->subpage) {
670 $subpage = $this->page->subpage;
671 } else {
672 $subpage = null;
673 }
a2789e34 674
675 // Special case. Course view page type include the course format, but we
676 // want to add the block non-format-specifically.
677 $pagetypepattern = $this->page->pagetype;
678 if (strpos($pagetypepattern, 'course-view') === 0) {
679 $pagetypepattern = 'course-view-*';
680 }
681
2a3b0763 682 $this->add_block($blockname, $defaulregion, $weight, false, $pagetypepattern, $subpage);
21d33bdf 683 }
684
9d1d606e 685 /**
686 * Convenience method, calls add_block repeatedly for all the blocks in $blocks.
d4accfc0 687 *
2a3b0763 688 * @param array $blocks array with array keys the region names, and values an array of block names.
9d1d606e 689 * @param string $pagetypepattern optional. Passed to @see add_block()
690 * @param string $subpagepattern optional. Passed to @see add_block()
691 */
7d2a0492 692 public function add_blocks($blocks, $pagetypepattern = NULL, $subpagepattern = NULL, $showinsubcontexts=false, $weight=0) {
9d1d606e 693 $this->add_regions(array_keys($blocks));
694 foreach ($blocks as $region => $regionblocks) {
695 $weight = 0;
696 foreach ($regionblocks as $blockname) {
7d2a0492 697 $this->add_block($blockname, $region, $weight, $showinsubcontexts, $pagetypepattern, $subpagepattern);
9d1d606e 698 $weight += 1;
699 }
700 }
701 }
702
2cdb8d84 703 /**
704 * Move a block to a new position on this page.
705 *
706 * If this block cannot appear on any other pages, then we change defaultposition/weight
847bed23 707 * in the block_instances table. Otherwise we just set the position on this page.
2cdb8d84 708 *
709 * @param $blockinstanceid the block instance id.
710 * @param $newregion the new region name.
711 * @param $newweight the new weight.
712 */
713 public function reposition_block($blockinstanceid, $newregion, $newweight) {
714 global $DB;
715
716 $this->check_region_is_known($newregion);
717 $inst = $this->find_instance($blockinstanceid);
718
719 $bi = $inst->instance;
720 if ($bi->weight == $bi->defaultweight && $bi->region == $bi->defaultregion &&
721 !$bi->showinsubcontexts && strpos($bi->pagetypepattern, '*') === false &&
722 (!$this->page->subpage || $bi->subpagepattern)) {
723
724 // Set default position
725 $newbi = new stdClass;
726 $newbi->id = $bi->id;
727 $newbi->defaultregion = $newregion;
728 $newbi->defaultweight = $newweight;
729 $DB->update_record('block_instances', $newbi);
730
731 if ($bi->blockpositionid) {
732 $bp = new stdClass;
733 $bp->id = $bi->blockpositionid;
734 $bp->region = $newregion;
735 $bp->weight = $newweight;
736 $DB->update_record('block_positions', $bp);
737 }
738
739 } else {
740 // Just set position on this page.
741 $bp = new stdClass;
742 $bp->region = $newregion;
743 $bp->weight = $newweight;
744
745 if ($bi->blockpositionid) {
746 $bp->id = $bi->blockpositionid;
747 $DB->update_record('block_positions', $bp);
748
749 } else {
750 $bp->blockinstanceid = $bi->id;
751 $bp->contextid = $this->page->context->id;
752 $bp->pagetype = $this->page->pagetype;
753 if ($this->page->subpage) {
754 $bp->subpage = $this->page->subpage;
755 } else {
756 $bp->subpage = '';
757 }
758 $bp->visible = $bi->visible;
759 $DB->insert_record('block_positions', $bp);
760 }
761 }
762 }
763
f4d38d20 764 /**
a19f419d 765 * Find a given block by its instance id
d4accfc0 766 *
f4d38d20 767 * @param integer $instanceid
d4accfc0 768 * @return object
f4d38d20 769 */
770 public function find_instance($instanceid) {
771 foreach ($this->regions as $region => $notused) {
772 $this->ensure_instances_exist($region);
773 foreach($this->blockinstances[$region] as $instance) {
774 if ($instance->instance->id == $instanceid) {
775 return $instance;
776 }
777 }
778 }
779 throw new block_not_on_page_exception($instanceid, $this->page);
780 }
781
86b5ea0f 782/// Inner workings =============================================================
783
d4accfc0 784 /**
785 * Check whether the page blocks have been loaded yet
786 *
787 * @return void Throws coding exception if already loaded
788 */
86b5ea0f 789 protected function check_not_yet_loaded() {
bb46a4fa 790 if (!is_null($this->birecordsbyregion)) {
86b5ea0f 791 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.');
792 }
793 }
794
d4accfc0 795 /**
796 * Check whether the page blocks have been loaded yet
797 *
798 * Nearly identical to the above function {@link check_not_yet_loaded()} except different message
799 *
800 * @return void Throws coding exception if already loaded
801 */
08eab897 802 protected function check_is_loaded() {
bb46a4fa 803 if (is_null($this->birecordsbyregion)) {
08eab897 804 throw new coding_exception('block_manager has not yet loaded the blocks, to it is too soon to request the information you asked for.');
805 }
806 }
807
d4accfc0 808 /**
809 * Check if a block type is known and usable
810 *
811 * @param string $blockname The block type name to search for
847bed23 812 * @param bool $includeinvisible Include disabled block types in the initial pass
d4accfc0 813 * @return void Coding Exception thrown if unknown or not enabled
814 */
08eab897 815 protected function check_known_block_type($blockname, $includeinvisible = false) {
816 if (!$this->is_known_block_type($blockname, $includeinvisible)) {
817 if ($this->is_known_block_type($blockname, true)) {
818 throw new coding_exception('Unknown block type ' . $blockname);
819 } else {
820 throw new coding_exception('Block type ' . $blockname . ' has been disabled by the administrator.');
821 }
822 }
823 }
824
d4accfc0 825 /**
826 * Check if a region is known by its name
827 *
828 * @param string $region
829 * @return void Coding Exception thrown if the region is not known
830 */
08eab897 831 protected function check_region_is_known($region) {
832 if (!$this->is_known_region($region)) {
833 throw new coding_exception('Trying to reference an unknown block region ' . $region);
834 }
86b5ea0f 835 }
bb46a4fa 836
837 /**
d4accfc0 838 * Returns an array of region names as keys and nested arrays for values
839 *
bb46a4fa 840 * @return array an array where the array keys are the region names, and the array
841 * values are empty arrays.
842 */
843 protected function prepare_per_region_arrays() {
844 $result = array();
845 foreach ($this->regions as $region => $notused) {
846 $result[$region] = array();
847 }
848 return $result;
849 }
850
d4accfc0 851 /**
852 * Create a set of new block instance from a record array
853 *
854 * @param array $birecords An array of block instance records
855 * @return array An array of instantiated block_instance objects
856 */
bb46a4fa 857 protected function create_block_instances($birecords) {
858 $results = array();
859 foreach ($birecords as $record) {
d836aa4b 860 if ($blockobject = block_instance($record->blockname, $record, $this->page)) {
861 $results[] = $blockobject;
862 }
bb46a4fa 863 }
864 return $results;
865 }
866
4578a5eb 867 /**
847bed23 868 * Create all the block instances for all the blocks that were loaded by
4578a5eb 869 * load_blocks. This is used, for example, to ensure that all blocks get a
870 * chance to initialise themselves via the {@link block_base::specialize()}
871 * method, before any output is done.
872 */
873 public function create_all_block_instances() {
874 foreach ($this->get_regions() as $region) {
875 $this->ensure_instances_exist($region);
876 }
877 }
878
d4accfc0 879 /**
00a24d44 880 * Return an array of content objects from a set of block instances
d4accfc0 881 *
882 * @param array $instances An array of block instances
78946b9b 883 * @param renderer_base The renderer to use.
00a24d44 884 * @param string $region the region name.
885 * @return array An array of block_content (and possibly block_move_target) objects.
d4accfc0 886 */
00a24d44 887 protected function create_block_contents($instances, $output, $region) {
bb46a4fa 888 $results = array();
00a24d44 889
890 $lastweight = 0;
891 $lastblock = 0;
892 if ($this->movingblock) {
893 $first = reset($instances);
894 if ($first) {
895 $lastweight = $first->instance->weight - 2;
896 }
897
898 $strmoveblockhere = get_string('moveblockhere', 'block');
899 }
900
bb46a4fa 901 foreach ($instances as $instance) {
d4a03c00 902 $content = $instance->get_content_for_output($output);
00a24d44 903 if (empty($content)) {
904 continue;
905 }
906
907 if ($this->movingblock && $lastweight != $instance->instance->weight &&
908 $content->blockinstanceid != $this->movingblock && $lastblock != $this->movingblock) {
dd72b308 909 $results[] = new block_move_target($strmoveblockhere, $this->get_move_target_url($region, ($lastweight + $instance->instance->weight)/2));
00a24d44 910 }
911
912 if ($content->blockinstanceid == $this->movingblock) {
913 $content->add_class('beingmoved');
914 $content->annotation .= get_string('movingthisblockcancel', 'block',
75015e5f 915 html_writer::link($this->page->url, get_string('cancel')));
bb46a4fa 916 }
00a24d44 917
918 $results[] = $content;
919 $lastweight = $instance->instance->weight;
920 $lastblock = $instance->instance->id;
921 }
922
923 if ($this->movingblock && $lastblock != $this->movingblock) {
dd72b308 924 $results[] = new block_move_target($strmoveblockhere, $this->get_move_target_url($region, $lastweight + 1));
bb46a4fa 925 }
926 return $results;
927 }
928
d4accfc0 929 /**
930 * Ensure block instances exist for a given region
a19f419d 931 *
d4accfc0 932 * @param string $region Check for bi's with the instance with this name
933 */
bb46a4fa 934 protected function ensure_instances_exist($region) {
935 $this->check_region_is_known($region);
936 if (!array_key_exists($region, $this->blockinstances)) {
937 $this->blockinstances[$region] =
938 $this->create_block_instances($this->birecordsbyregion[$region]);
939 }
940 }
941
d4accfc0 942 /**
943 * Ensure that there is some content within the given region
944 *
945 * @param string $region The name of the region to check
946 */
d4a03c00 947 protected function ensure_content_created($region, $output) {
bb46a4fa 948 $this->ensure_instances_exist($region);
949 if (!array_key_exists($region, $this->visibleblockcontent)) {
d4a03c00 950 $contents = array();
951 if (array_key_exists($region, $this->extracontent)) {
952 $contents = $this->extracontent[$region];
953 }
00a24d44 954 $contents = array_merge($contents, $this->create_block_contents($this->blockinstances[$region], $output, $region));
d4a03c00 955 if ($region == $this->defaultregion) {
21d33bdf 956 $addblockui = block_add_block_ui($this->page, $output);
d4a03c00 957 if ($addblockui) {
958 $contents[] = $addblockui;
959 }
960 }
961 $this->visibleblockcontent[$region] = $contents;
bb46a4fa 962 }
963 }
a19f419d 964
965/// Process actions from the URL ===============================================
966
00a24d44 967 /**
968 * Get the appropriate list of editing icons for a block. This is used
969 * to set {@link block_contents::$controls} in {@link block_base::get_contents_for_output()}.
970 *
971 * @param $output The core_renderer to use when generating the output. (Need to get icon paths.)
972 * @return an array in the format for {@link block_contents::$controls}
973 */
974 public function edit_controls($block) {
975 global $CFG;
976
5afbd0e7 977 if (!isset($CFG->undeletableblocktypes) || (!is_array($CFG->undeletableblocktypes) && !is_string($CFG->undeletableblocktypes))) {
3406acde 978 $CFG->undeletableblocktypes = array('navigation','settings');
5afbd0e7 979 } else if (is_string($CFG->undeletableblocktypes)) {
980 $CFG->undeletableblocktypes = explode(',', $CFG->undeletableblocktypes);
981 }
982
00a24d44 983 $controls = array();
b9bc2019 984 $actionurl = $this->page->url->out(false, array('sesskey'=> sesskey()));
00a24d44 985
986 // Assign roles icon.
987 if (has_capability('moodle/role:assign', $block->context)) {
a4a04ada
PS
988 //TODO: please note it is sloppy to pass urls through page parameters!!
989 // it is shortened because some web servers (e.g. IIS by default) give
990 // a 'security' error if you try to pass a full URL as a GET parameter in another URL.
4f0c2d00 991
bc0663ab 992 $return = $this->page->url->out(false);
a4a04ada 993 $return = str_replace($CFG->wwwroot . '/', '', $return);
4f0c2d00 994
00a24d44 995 $controls[] = array('url' => $CFG->wwwroot . '/' . $CFG->admin .
a4a04ada 996 '/roles/assign.php?contextid=' . $block->context->id . '&returnurl=' . urlencode($return),
00a24d44 997 'icon' => 'i/roles', 'caption' => get_string('assignroles', 'role'));
998 }
999
d8ef60bd 1000 if ($this->page->user_can_edit_blocks() && $block->instance_can_be_hidden()) {
00a24d44 1001 // Show/hide icon.
1002 if ($block->instance->visible) {
1003 $controls[] = array('url' => $actionurl . '&bui_hideid=' . $block->instance->id,
1004 'icon' => 't/hide', 'caption' => get_string('hide'));
1005 } else {
1006 $controls[] = array('url' => $actionurl . '&bui_showid=' . $block->instance->id,
1007 'icon' => 't/show', 'caption' => get_string('show'));
1008 }
1009 }
1010
1011 if ($this->page->user_can_edit_blocks() || $block->user_can_edit()) {
1012 // Edit config icon - always show - needed for positioning UI.
1013 $controls[] = array('url' => $actionurl . '&bui_editid=' . $block->instance->id,
1014 'icon' => 't/edit', 'caption' => get_string('configuration'));
1015 }
1016
1017 if ($this->page->user_can_edit_blocks() && $block->user_can_edit() && $block->user_can_addto($this->page)) {
5afbd0e7 1018 if (!in_array($block->instance->blockname, $CFG->undeletableblocktypes)) {
1019 // Delete icon.
1020 $controls[] = array('url' => $actionurl . '&bui_deleteid=' . $block->instance->id,
1021 'icon' => 't/delete', 'caption' => get_string('delete'));
1022 }
00a24d44 1023 }
1024
1025 if ($this->page->user_can_edit_blocks()) {
1026 // Move icon.
1027 $controls[] = array('url' => $actionurl . '&bui_moveid=' . $block->instance->id,
1028 'icon' => 't/move', 'caption' => get_string('move'));
1029 }
1030
1031 return $controls;
1032 }
1033
a19f419d 1034 /**
1035 * Process any block actions that were specified in the URL.
1036 *
1037 * This can only be done given a valid $page object.
1038 *
1039 * @param moodle_page $page the page to add blocks to.
1040 * @return boolean true if anything was done. False if not.
1041 */
1042 public function process_url_actions() {
00a24d44 1043 if (!$this->page->user_is_editing()) {
1044 return false;
1045 }
a19f419d 1046 return $this->process_url_add() || $this->process_url_delete() ||
00a24d44 1047 $this->process_url_show_hide() || $this->process_url_edit() ||
1048 $this->process_url_move();
a19f419d 1049 }
1050
1051 /**
1052 * Handle adding a block.
1053 * @return boolean true if anything was done. False if not.
1054 */
1055 public function process_url_add() {
1056 $blocktype = optional_param('bui_addblock', null, PARAM_SAFEDIR);
1057 if (!$blocktype) {
1058 return false;
1059 }
1060
c74eec3b 1061 require_sesskey();
a19f419d 1062
1d7e341e 1063 if (!$this->page->user_can_edit_blocks()) {
a19f419d 1064 throw new moodle_exception('nopermissions', '', $this->page->url->out(), get_string('addblock'));
1065 }
1066
1067 if (!array_key_exists($blocktype, $this->get_addable_blocks())) {
1068 throw new moodle_exception('cannotaddthisblocktype', '', $this->page->url->out(), $blocktype);
1069 }
1070
1071 $this->add_block_at_end_of_default_region($blocktype);
1072
847bed23 1073 // If the page URL was a guess, it will contain the bui_... param, so we must make sure it is not there.
a19f419d 1074 $this->page->ensure_param_not_in_url('bui_addblock');
1075
1076 return true;
1077 }
1078
1079 /**
1080 * Handle deleting a block.
1081 * @return boolean true if anything was done. False if not.
1082 */
1083 public function process_url_delete() {
1084 $blockid = optional_param('bui_deleteid', null, PARAM_INTEGER);
1085 if (!$blockid) {
1086 return false;
1087 }
1088
c74eec3b 1089 require_sesskey();
a19f419d 1090
1091 $block = $this->page->blocks->find_instance($blockid);
1092
7bbc2890 1093 if (!$block->user_can_edit() || !$this->page->user_can_edit_blocks() || !$block->user_can_addto($this->page)) {
a19f419d 1094 throw new moodle_exception('nopermissions', '', $this->page->url->out(), get_string('deleteablock'));
1095 }
1096
1097 blocks_delete_instance($block->instance);
1098
847bed23 1099 // If the page URL was a guess, it will contain the bui_... param, so we must make sure it is not there.
a19f419d 1100 $this->page->ensure_param_not_in_url('bui_deleteid');
1101
1102 return true;
1103 }
1104
1105 /**
1106 * Handle showing or hiding a block.
1107 * @return boolean true if anything was done. False if not.
1108 */
1109 public function process_url_show_hide() {
1110 if ($blockid = optional_param('bui_hideid', null, PARAM_INTEGER)) {
1111 $newvisibility = 0;
1112 } else if ($blockid = optional_param('bui_showid', null, PARAM_INTEGER)) {
1113 $newvisibility = 1;
1114 } else {
1115 return false;
1116 }
1117
c74eec3b 1118 require_sesskey();
a19f419d 1119
1120 $block = $this->page->blocks->find_instance($blockid);
1121
d14edf06 1122 if (!$this->page->user_can_edit_blocks()) {
a19f419d 1123 throw new moodle_exception('nopermissions', '', $this->page->url->out(), get_string('hideshowblocks'));
d8ef60bd
SH
1124 } else if (!$block->instance_can_be_hidden()) {
1125 return false;
a19f419d 1126 }
1127
1128 blocks_set_visibility($block->instance, $this->page, $newvisibility);
1129
1130 // If the page URL was a guses, it will contain the bui_... param, so we must make sure it is not there.
1131 $this->page->ensure_param_not_in_url('bui_hideid');
1132 $this->page->ensure_param_not_in_url('bui_showid');
1133
1134 return true;
1135 }
1136
1137 /**
1138 * Handle showing/processing the submission from the block editing form.
1139 * @return boolean true if the form was submitted and the new config saved. Does not
1140 * return if the editing form was displayed. False otherwise.
1141 */
1142 public function process_url_edit() {
0f63f271 1143 global $CFG, $DB, $PAGE, $OUTPUT;
a19f419d 1144
1145 $blockid = optional_param('bui_editid', null, PARAM_INTEGER);
1146 if (!$blockid) {
1147 return false;
1148 }
1149
c74eec3b 1150 require_sesskey();
a19f419d 1151 require_once($CFG->dirroot . '/blocks/edit_form.php');
1152
1153 $block = $this->find_instance($blockid);
1154
d14edf06 1155 if (!$block->user_can_edit() && !$this->page->user_can_edit_blocks()) {
a19f419d 1156 throw new moodle_exception('nopermissions', '', $this->page->url->out(), get_string('editblock'));
1157 }
1158
1159 $editpage = new moodle_page();
3c14795a 1160 $editpage->set_pagelayout('admin');
a19f419d 1161 $editpage->set_course($this->page->course);
b1627a92
DC
1162 //$editpage->set_context($block->context);
1163 $editpage->set_context($this->page->context);
88f77c3c
SH
1164 if ($this->page->cm) {
1165 $editpage->set_cm($this->page->cm);
1166 }
24e4119a 1167 $editurlbase = str_replace($CFG->wwwroot . '/', '/', $this->page->url->out_omit_querystring());
a19f419d 1168 $editurlparams = $this->page->url->params();
1169 $editurlparams['bui_editid'] = $blockid;
1170 $editpage->set_url($editurlbase, $editurlparams);
05c92729 1171 $editpage->set_block_actions_done();
a19f419d 1172 // At this point we are either going to redirect, or display the form, so
1173 // overwrite global $PAGE ready for this. (Formslib refers to it.)
1174 $PAGE = $editpage;
0f63f271
AD
1175 //some functions like MoodleQuickForm::addHelpButton use $OUTPUT so we need to replace that to
1176 $output = $editpage->get_renderer('core');
1177 $OUTPUT = $output;
a19f419d 1178
1179 $formfile = $CFG->dirroot . '/blocks/' . $block->name() . '/edit_form.php';
1180 if (is_readable($formfile)) {
1181 require_once($formfile);
1182 $classname = 'block_' . $block->name() . '_edit_form';
34a988e2
MD
1183 if (!class_exists($classname)) {
1184 $classname = 'block_edit_form';
1185 }
a19f419d 1186 } else {
1187 $classname = 'block_edit_form';
1188 }
1189
1190 $mform = new $classname($editpage->url, $block, $this->page);
1191 $mform->set_data($block->instance);
1192
1193 if ($mform->is_cancelled()) {
1194 redirect($this->page->url);
1195
1196 } else if ($data = $mform->get_data()) {
1197 $bi = new stdClass;
1198 $bi->id = $block->instance->id;
a19f419d 1199 $bi->pagetypepattern = $data->bui_pagetypepattern;
1200 if (empty($data->bui_subpagepattern) || $data->bui_subpagepattern == '%@NULL@%') {
1201 $bi->subpagepattern = null;
1202 } else {
1203 $bi->subpagepattern = $data->bui_subpagepattern;
1204 }
0aed347f
MD
1205
1206 $parentcontext = get_context_instance_by_id($data->bui_parentcontextid);
1207 $systemcontext = get_context_instance(CONTEXT_SYSTEM);
1208
1209 // Updating stickiness and contexts. See MDL-21375 for details.
1210 if (has_capability('moodle/site:manageblocks', $parentcontext)) { // Check permissions in destination
1211 // Explicitly set the context
1212 $bi->parentcontextid = $parentcontext->id;
1213
1214 // If the context type is > 0 then we'll explicitly set the block as sticky, otherwise not
1215 $bi->showinsubcontexts = (int)(!empty($data->bui_contexts));
1216
1217 // If the block wants to be system-wide, then explicitly set that
b1627a92 1218 if ($data->bui_contexts == BUI_CONTEXTS_ENTIRE_SITE) { // Only possible on a frontpage or system page
0aed347f 1219 $bi->parentcontextid = $systemcontext->id;
b1627a92 1220 $bi->showinsubcontexts = 1;
0aed347f
MD
1221
1222 } else { // The block doesn't want to be system-wide, so let's ensure that
1223 if ($parentcontext->id == $systemcontext->id) { // We need to move it to the front page
1224 $frontpagecontext = get_context_instance(CONTEXT_COURSE, SITEID);
1225 $bi->parentcontextid = $frontpagecontext->id;
1226 $bi->pagetypepattern = '*'; // Just in case
1227 }
02ba576c
MD
1228 }
1229 }
0aed347f 1230
b1627a92
DC
1231 $bits = explode('-', $bi->pagetypepattern);
1232 // hacks for some contexts
1233 if (($parentcontext->contextlevel == CONTEXT_COURSE) && ($parentcontext->instanceid != SITEID)) {
1234 // For course context
1235 // is page type pattern is mod-*, change showinsubcontext to 1
1236 if ($bits[0] == 'mod' || $bi->pagetypepattern == '*') {
1237 $bi->showinsubcontexts = 1;
1238 } else {
1239 $bi->showinsubcontexts = 0;
1240 }
1241 } else if ($parentcontext->contextlevel == CONTEXT_USER) {
1242 // for user context
1243 // subpagepattern should be null
1244 if ($bits[0] == 'user' or $bits[0] == 'my') {
1245 // we don't need subpagepattern in usercontext
1246 $bi->subpagepattern = null;
1247 }
1248 }
1249
a19f419d 1250 $bi->defaultregion = $data->bui_defaultregion;
1251 $bi->defaultweight = $data->bui_defaultweight;
1252 $DB->update_record('block_instances', $bi);
1253
a23bbaa3 1254 if (!empty($block->config)) {
1255 $config = clone($block->config);
1256 } else {
1257 $config = new stdClass;
1258 }
a19f419d 1259 foreach ($data as $configfield => $value) {
1260 if (strpos($configfield, 'config_') !== 0) {
1261 continue;
1262 }
1263 $field = substr($configfield, 7);
1264 $config->$field = $value;
1265 }
1266 $block->instance_config_save($config);
1267
1268 $bp = new stdClass;
1269 $bp->visible = $data->bui_visible;
1270 $bp->region = $data->bui_region;
1271 $bp->weight = $data->bui_weight;
1272 $needbprecord = !$data->bui_visible || $data->bui_region != $data->bui_defaultregion ||
1273 $data->bui_weight != $data->bui_defaultweight;
1274
1275 if ($block->instance->blockpositionid && !$needbprecord) {
1276 $DB->delete_records('block_positions', array('id' => $block->instance->blockpositionid));
1277
1278 } else if ($block->instance->blockpositionid && $needbprecord) {
1279 $bp->id = $block->instance->blockpositionid;
1280 $DB->update_record('block_positions', $bp);
1281
1282 } else if ($needbprecord) {
1283 $bp->blockinstanceid = $block->instance->id;
a23bbaa3 1284 $bp->contextid = $this->page->context->id;
a19f419d 1285 $bp->pagetype = $this->page->pagetype;
1286 if ($this->page->subpage) {
1287 $bp->subpage = $this->page->subpage;
1288 } else {
a23bbaa3 1289 $bp->subpage = '';
a19f419d 1290 }
1291 $DB->insert_record('block_positions', $bp);
1292 }
1293
1294 redirect($this->page->url);
1295
1296 } else {
69c14bbd 1297 $strheading = get_string('blockconfiga', 'moodle', $block->get_title());
a19f419d 1298 $editpage->set_title($strheading);
1299 $editpage->set_heading($strheading);
b1627a92
DC
1300 $bits = explode('-', $this->page->pagetype);
1301 if ($bits[0] == 'tag' && !empty($this->page->subpage)) {
1302 // better navbar for tag pages
1303 $editpage->navbar->add(get_string('tags'), new moodle_url('/tag/'));
1304 $tag = tag_get('id', $this->page->subpage, '*');
1305 // tag search page doesn't have subpageid
1306 if ($tag) {
1307 $editpage->navbar->add($tag->name, new moodle_url('/tag/index.php', array('id'=>$tag->id)));
1308 }
1309 }
1310 $editpage->navbar->add($block->get_title());
1311 $editpage->navbar->add(get_string('configuration'));
a19f419d 1312 echo $output->header();
1313 echo $output->heading($strheading, 2);
1314 $mform->display();
1315 echo $output->footer();
1316 exit;
1317 }
1318 }
00a24d44 1319
1320 /**
1321 * Handle showing/processing the submission from the block editing form.
1322 * @return boolean true if the form was submitted and the new config saved. Does not
1323 * return if the editing form was displayed. False otherwise.
1324 */
1325 public function process_url_move() {
1326 global $CFG, $DB, $PAGE;
1327
1328 $blockid = optional_param('bui_moveid', null, PARAM_INTEGER);
1329 if (!$blockid) {
1330 return false;
1331 }
1332
c74eec3b 1333 require_sesskey();
00a24d44 1334
1335 $block = $this->find_instance($blockid);
1336
1337 if (!$this->page->user_can_edit_blocks()) {
1338 throw new moodle_exception('nopermissions', '', $this->page->url->out(), get_string('editblock'));
1339 }
1340
1341 $newregion = optional_param('bui_newregion', '', PARAM_ALPHANUMEXT);
1342 $newweight = optional_param('bui_newweight', null, PARAM_FLOAT);
1343 if (!$newregion || is_null($newweight)) {
1344 // Don't have a valid target position yet, must be just starting the move.
1345 $this->movingblock = $blockid;
1346 $this->page->ensure_param_not_in_url('bui_moveid');
1347 return false;
1348 }
1349
2cdb8d84 1350 if (!$this->is_known_region($newregion)) {
1351 throw new moodle_exception('unknownblockregion', '', $this->page->url, $newregion);
1352 }
1353
1354 // Move this block. This may involve moving other nearby blocks.
1355 $blocks = $this->birecordsbyregion[$newregion];
1356
f4e6a86e 1357 $maxweight = self::MAX_WEIGHT;
1358 $minweight = -self::MAX_WEIGHT;
1359
1360 // Initialise the used weights and spareweights array with the default values
2cdb8d84 1361 $spareweights = array();
1362 $usedweights = array();
f4e6a86e 1363 for ($i = $minweight; $i <= $maxweight; $i++) {
2cdb8d84 1364 $spareweights[$i] = $i;
1365 $usedweights[$i] = array();
1366 }
f4e6a86e 1367
1368 // Check each block and sort out where we have used weights
2cdb8d84 1369 foreach ($blocks as $bi) {
f4e6a86e 1370 if ($bi->weight > $maxweight) {
1371 // If this statement is true then the blocks weight is more than the
1372 // current maximum. To ensure that we can get the best block position
1373 // we will initialise elements within the usedweights and spareweights
1374 // arrays between the blocks weight (which will then be the new max) and
1375 // the current max
1376 $parseweight = $bi->weight;
1377 while (!array_key_exists($parseweight, $usedweights)) {
1378 $usedweights[$parseweight] = array();
1379 $spareweights[$parseweight] = $parseweight;
1380 $parseweight--;
1381 }
1382 $maxweight = $bi->weight;
1383 } else if ($bi->weight < $minweight) {
1384 // As above except this time the blocks weight is LESS than the
1385 // the current minimum, so we will initialise the array from the
1386 // blocks weight (new minimum) to the current minimum
1387 $parseweight = $bi->weight;
1388 while (!array_key_exists($parseweight, $usedweights)) {
1389 $usedweights[$parseweight] = array();
1390 $spareweights[$parseweight] = $parseweight;
1391 $parseweight++;
1392 }
1393 $minweight = $bi->weight;
1394 }
1395 if ($bi->id != $block->instance->id) {
1396 unset($spareweights[$bi->weight]);
1397 $usedweights[$bi->weight][] = $bi->id;
2cdb8d84 1398 }
2cdb8d84 1399 }
1400
f4e6a86e 1401 // First we find the nearest gap in the list of weights.
2cdb8d84 1402 $bestdistance = max(abs($newweight - self::MAX_WEIGHT), abs($newweight + self::MAX_WEIGHT)) + 1;
1403 $bestgap = null;
1404 foreach ($spareweights as $spareweight) {
1405 if (abs($newweight - $spareweight) < $bestdistance) {
1406 $bestdistance = abs($newweight - $spareweight);
1407 $bestgap = $spareweight;
1408 }
1409 }
1410
1411 // If there is no gap, we have to go outside -self::MAX_WEIGHT .. self::MAX_WEIGHT.
1412 if (is_null($bestgap)) {
1413 $bestgap = self::MAX_WEIGHT + 1;
1414 while (!empty($usedweights[$bestgap])) {
1415 $bestgap++;
1416 }
1417 }
1418
1419 // Now we know the gap we are aiming for, so move all the blocks along.
1420 if ($bestgap < $newweight) {
1421 $newweight = floor($newweight);
1422 for ($weight = $bestgap + 1; $weight <= $newweight; $weight++) {
1423 foreach ($usedweights[$weight] as $biid) {
1424 $this->reposition_block($biid, $newregion, $weight - 1);
1425 }
1426 }
1427 $this->reposition_block($block->instance->id, $newregion, $newweight);
1428 } else {
1429 $newweight = ceil($newweight);
1430 for ($weight = $bestgap - 1; $weight >= $newweight; $weight--) {
1431 foreach ($usedweights[$weight] as $biid) {
1432 $this->reposition_block($biid, $newregion, $weight + 1);
1433 }
1434 }
1435 $this->reposition_block($block->instance->id, $newregion, $newweight);
1436 }
6f5e0852 1437
00a24d44 1438 $this->page->ensure_param_not_in_url('bui_moveid');
1439 $this->page->ensure_param_not_in_url('bui_newregion');
1440 $this->page->ensure_param_not_in_url('bui_newweight');
1441 return true;
1442 }
56ed242b
SH
1443
1444 /**
1445 * Turns the display of normal blocks either on or off.
78bfb562 1446 *
56ed242b
SH
1447 * @param bool $setting
1448 */
1449 public function show_only_fake_blocks($setting = true) {
1450 $this->fakeblocksonly = $setting;
1451 }
86b5ea0f 1452}
1453
08eab897 1454/// Helper functions for working with block classes ============================
1455
1456/**
847bed23 1457 * Call a class method (one that does not require a block instance) on a block class.
d4accfc0 1458 *
08eab897 1459 * @param string $blockname the name of the block.
1460 * @param string $method the method name.
1461 * @param array $param parameters to pass to the method.
1462 * @return mixed whatever the method returns.
1463 */
11306331 1464function block_method_result($blockname, $method, $param = NULL) {
0f3fe4b6 1465 if(!block_load_class($blockname)) {
1466 return NULL;
1467 }
11306331 1468 return call_user_func(array('block_'.$blockname, $method), $param);
0f3fe4b6 1469}
1470
08eab897 1471/**
365a5941 1472 * Creates a new instance of the specified block class.
d4accfc0 1473 *
08eab897 1474 * @param string $blockname the name of the block.
1475 * @param $instance block_instances DB table row (optional).
bb46a4fa 1476 * @param moodle_page $page the page this block is appearing on.
08eab897 1477 * @return block_base the requested block instance.
1478 */
bb46a4fa 1479function block_instance($blockname, $instance = NULL, $page = NULL) {
0f3fe4b6 1480 if(!block_load_class($blockname)) {
1481 return false;
1482 }
e89d741a 1483 $classname = 'block_'.$blockname;
f032aa7a 1484 $retval = new $classname;
9b4b78fd 1485 if($instance !== NULL) {
bb46a4fa 1486 if (is_null($page)) {
1487 global $PAGE;
1488 $page = $PAGE;
1489 }
1490 $retval->_load_instance($instance, $page);
9b4b78fd 1491 }
1492 return $retval;
0f3fe4b6 1493}
1494
08eab897 1495/**
1496 * Load the block class for a particular type of block.
d4accfc0 1497 *
08eab897 1498 * @param string $blockname the name of the block.
1499 * @return boolean success or failure.
1500 */
0f3fe4b6 1501function block_load_class($blockname) {
1502 global $CFG;
1503
a9033ad5 1504 if(empty($blockname)) {
c7a9e293 1505 return false;
1506 }
1507
e89d741a 1508 $classname = 'block_'.$blockname;
a9033ad5 1509
1510 if(class_exists($classname)) {
1511 return true;
1512 }
1513
d836aa4b 1514 $blockpath = $CFG->dirroot.'/blocks/'.$blockname.'/block_'.$blockname.'.php';
1515
1516 if (file_exists($blockpath)) {
1517 require_once($CFG->dirroot.'/blocks/moodleblock.class.php');
1518 include_once($blockpath);
1519 }else{
15822fe2 1520 //debugging("$blockname code does not exist in $blockpath", DEBUG_DEVELOPER);
d836aa4b 1521 return false;
1522 }
0f3fe4b6 1523
0f3fe4b6 1524 return class_exists($classname);
1525}
1526
1d13c75c 1527/**
1528 * Given a specific page type, return all the page type patterns that might
1529 * match it.
1530 *
1531 * @param string $pagetype for example 'course-view-weeks' or 'mod-quiz-view'.
1532 * @return array an array of all the page type patterns that might match this page type.
1533 */
1534function matching_page_type_patterns($pagetype) {
1535 $patterns = array($pagetype);
1536 $bits = explode('-', $pagetype);
1537 if (count($bits) == 3 && $bits[0] == 'mod') {
1538 if ($bits[2] == 'view') {
1539 $patterns[] = 'mod-*-view';
1540 } else if ($bits[2] == 'index') {
1541 $patterns[] = 'mod-*-index';
1542 }
1543 }
1544 while (count($bits) > 0) {
1545 $patterns[] = implode('-', $bits) . '-*';
1546 array_pop($bits);
1547 }
4d74c876 1548 $patterns[] = '*';
1d13c75c 1549 return $patterns;
1550}
1551
b1627a92
DC
1552/**
1553 * Given a specific page type, parent context and currect context, return all the page type patterns
1554 * that might be used by this block.
1555 *
1556 * @param string $pagetype for example 'course-view-weeks' or 'mod-quiz-view'.
1557 * @param stdClass $parentcontext Block's parent context
1558 * @param stdClass $currentcontext Current context of block
1559 * @return array an array of all the page type patterns that might match this page type.
1560 */
1561function generate_page_type_patterns($pagetype, $parentcontext = null, $currentcontext = null) {
1562 global $CFG;
32a510de 1563
b1627a92 1564 $bits = explode('-', $pagetype);
32a510de 1565
32a510de
SH
1566 $function = 'default_pagetypelist';
1567
1568 $core = get_core_subsystems();
1569 $plugins = get_plugin_types();
1570
b822fc85
AD
1571 //progressively strip pieces off the page type looking for a match
1572 //eg: for pagetype "course-report-outline-index" try coursereportoutlineindex
1573 // then coursereportoutline then coursereport then course
1574 $componentarray = null;
1575 for($i = count($bits); $i > 0; $i--) {
1576 $componentarray = array_slice($bits, 0, $i);
1577 $component = implode($componentarray);
1578
1579 // Look for special case components like course reports
1580 $libfile = $CFG->dirroot.'/'.implode('/', $componentarray).'/lib.php';
32a510de
SH
1581 if (file_exists($libfile)) {
1582 require_once($libfile);
1583 if (function_exists($component.'_pagetypelist')) {
1584 $function = $component.'_pagetypelist';
b822fc85
AD
1585 break;
1586 }
1587 }
1588
1589 // Then check to see if the component is a core component
1590 if (array_key_exists($component, $core) && !empty($core[$component])) {
1591 $libfile = $CFG->dirroot.'/'.$core[$component].'/lib.php';
1592 if (file_exists($libfile)) {
1593 require_once($libfile);
1594 if (function_exists($component.'_pagetypelist')) {
1595 $function = $component.'_pagetypelist';
1596 break;
1597 }
b1627a92 1598 }
32a510de 1599 }
b822fc85
AD
1600
1601 // If its not a special or core component check to see if it is a plugin component
1602 if (array_key_exists($component, $plugins) && !empty($plugins[$component])) {
1603 $function = 'plugin_pagetypelist';
1604 if (function_exists($component.'_pagetypelist')) {
1605 $function = $component.'_pagetypelist';
1606 break;
1607 }
32a510de
SH
1608 }
1609 }
b822fc85 1610
32a510de
SH
1611 // Call the most appropriate function we could determine
1612 $patterns = $function($pagetype, $parentcontext, $currentcontext);
1613 if (empty($patterns)) {
1614 // If there are no patterns default to just the current pattern.
1615 $patterns = array($pagetype => $pagetype);
b1627a92 1616 }
32a510de
SH
1617 return $patterns;
1618}
b1627a92 1619
32a510de
SH
1620/**
1621 * Generates a default page type list when a more appropriate callback cannot be decided upon.
1622 *
1623 * @param string $pagetype
1624 * @param stdClass $parentcontext
1625 * @param stdClass $currentcontext
1626 * @return array
1627 */
1628function default_pagetypelist($pagetype, $parentcontext = null, $currentcontext = null) {
b1627a92
DC
1629 // Generate page type patterns based on current page type if
1630 // callbacks haven't been defined
32a510de
SH
1631 $patterns = array($pagetype => $pagetype);
1632 $bits = explode('-', $pagetype);
b1627a92
DC
1633 while (count($bits) > 0) {
1634 $pattern = implode('-', $bits) . '-*';
1635 $pagetypestringname = 'page-'.str_replace('*', 'x', $pattern);
1636 // guessing page type description
1637 if (get_string_manager()->string_exists($pagetypestringname, 'pagetype')) {
1638 $patterns[$pattern] = get_string($pagetypestringname, 'pagetype');
1639 } else {
1640 $patterns[$pattern] = $pattern;
1641 }
1642 array_pop($bits);
1643 }
1644 $patterns['*'] = get_string('page-x', 'pagetype');
1645 return $patterns;
1646}
1647
32a510de
SH
1648/**
1649 * Generates a page type list for plugins
1650 *
1651 * @param string $pagetype
1652 * @param stdClass $parentcontext
1653 * @param stdClass $currentcontext
1654 * @return array
1655 */
1656function plugin_pagetypelist($pagetype, $parentcontext = null, $currentcontext = null) {
fb794ab7
AD
1657 global $CFG;
1658
32a510de
SH
1659 // for modules
1660 $bits = explode('-', $pagetype);
1661 $plugintype = $bits[0];
1662 $pluginname = $bits[1];
1663 $directory = get_plugin_directory($plugintype, $pluginname);
1664 if (empty($directory)) {
1665 return array();
1666 }
1667 $libfile = $directory.'/lib.php';
1668 require_once($libfile);
1669 $function = $pluginname.'_pagetypelist';
1670 if (!function_exists($function)) {
1671 return array();
1672 }
1673 $patterns = $function($pagetype, $parentcontext, $currentcontext);
1674 if ($parentcontext->contextlevel == CONTEXT_COURSE) {
1675 // including course page type
1676 require_once("$CFG->dirroot/course/lib.php");
fb794ab7 1677 $patterns = array_merge(course_pagetypelist($pagetype, $parentcontext, $currentcontext), $patterns);
32a510de
SH
1678 }
1679 return $patterns;
1680}
1681
84a1bea9
AD
1682/**
1683 * Generates the page type list for the my moodle page
1684 *
1685 * @param string $pagetype
1686 * @param stdClass $parentcontext
1687 * @param stdClass $currentcontext
1688 * @return array
1689 */
1690function my_pagetypelist($pagetype, $parentcontext = null, $currentcontext = null) {
1691 return array('my-index' => 'my-index');
1692}
1693
32a510de
SH
1694/**
1695 * Generates the page type list for a module by either locating and using the modules callback
1696 * or by generating a default list.
1697 *
1698 * @param string $pagetype
1699 * @param stdClass $parentcontext
1700 * @param stdClass $currentcontext
1701 * @return array
1702 */
1703function mod_pagetypelist($pagetype, $parentcontext = null, $currentcontext = null) {
1704 $patterns = plugin_pagetypelist($pagetype, $parentcontext, $currentcontext);
1705 if (empty($patterns)) {
1706 // if modules don't have callbacks
1707 // generate two default page type patterns for modules only
1708 $bits = explode('-', $pagetype);
1709 $patterns = array($pagetype => $pagetype);
1710 if ($bits[2] == 'view') {
1711 $patterns['mod-*-view'] = get_string('page-mod-x-view', 'pagetype');
1712 } else if ($bits[2] == 'index') {
1713 $patterns['mod-*-index'] = get_string('page-mod-x-index', 'pagetype');
1714 }
1715 }
1716 return $patterns;
1717}
21d33bdf 1718/// Functions update the blocks if required by the request parameters ==========
1719
1720/**
1721 * Return a {@link block_contents} representing the add a new block UI, if
1722 * this user is allowed to see it.
1723 *
1724 * @return block_contents an appropriate block_contents, or null if the user
1725 * cannot add any blocks here.
1726 */
1727function block_add_block_ui($page, $output) {
d81b05e7 1728 global $CFG, $OUTPUT;
21d33bdf 1729 if (!$page->user_is_editing() || !$page->user_can_edit_blocks()) {
1730 return null;
1731 }
1732
1733 $bc = new block_contents();
1734 $bc->title = get_string('addblock');
1735 $bc->add_class('block_adminblock');
1736
a2789e34 1737 $missingblocks = $page->blocks->get_addable_blocks();
21d33bdf 1738 if (empty($missingblocks)) {
a2789e34 1739 $bc->content = get_string('noblockstoaddhere');
21d33bdf 1740 return $bc;
1741 }
1742
1743 $menu = array();
a2789e34 1744 foreach ($missingblocks as $block) {
21d33bdf 1745 $blockobject = block_instance($block->name);
1746 if ($blockobject !== false && $blockobject->user_can_addto($page)) {
1747 $menu[$block->name] = $blockobject->get_title();
1748 }
1749 }
c6947ba7 1750 textlib_get_instance()->asort($menu);
21d33bdf 1751
8afba50b 1752 $actionurl = new moodle_url($page->url, array('sesskey'=>sesskey()));
f8dab966
PS
1753 $select = new single_select($actionurl, 'bui_addblock', $menu, null, array(''=>get_string('adddots')), 'add_block');
1754 $bc->content = $OUTPUT->render($select);
21d33bdf 1755 return $bc;
1756}
1757
21d33bdf 1758// Functions that have been deprecated by block_manager =======================
f032aa7a 1759
08eab897 1760/**
a2789e34 1761 * @deprecated since Moodle 2.0 - use $page->blocks->get_addable_blocks();
d4accfc0 1762 *
08eab897 1763 * This function returns an array with the IDs of any blocks that you can add to your page.
1764 * Parameters are passed by reference for speed; they are not modified at all.
d4accfc0 1765 *
08eab897 1766 * @param $page the page object.
bb46a4fa 1767 * @param $blockmanager Not used.
08eab897 1768 * @return array of block type ids.
1769 */
bb46a4fa 1770function blocks_get_missing(&$page, &$blockmanager) {
a2789e34 1771 debugging('blocks_get_missing is deprecated. Please use $page->blocks->get_addable_blocks() instead.', DEBUG_DEVELOPER);
1772 $blocks = $page->blocks->get_addable_blocks();
1773 $ids = array();
1774 foreach ($blocks as $block) {
1775 $ids[] = $block->id;
1776 }
1777 return $ids;
f032aa7a 1778}
1779
bb46a4fa 1780/**
1781 * Actually delete from the database any blocks that are currently on this page,
1782 * but which should not be there according to blocks_name_allowed_in_format.
d4accfc0 1783 *
847bed23 1784 * @todo Write/Fix this function. Currently returns immediately
c679c358 1785 * @param $course
bb46a4fa 1786 */
c679c358 1787function blocks_remove_inappropriate($course) {
bb46a4fa 1788 // TODO
1789 return;
7604f5c1 1790 /*
bb46a4fa 1791 $blockmanager = blocks_get_by_page($page);
f032aa7a 1792
78946b9b 1793 if (empty($blockmanager)) {
f032aa7a 1794 return;
1795 }
1796
78946b9b 1797 if (($pageformat = $page->pagetype) == NULL) {
f032aa7a 1798 return;
1799 }
1800
f2c6739c 1801 foreach($blockmanager as $region) {
1802 foreach($region as $instance) {
f032aa7a 1803 $block = blocks_get_record($instance->blockid);
5bbbe0be 1804 if(!blocks_name_allowed_in_format($block->name, $pageformat)) {
a2789e34 1805 blocks_delete_instance($instance->instance);
f032aa7a 1806 }
1807 }
7604f5c1 1808 }*/
f032aa7a 1809}
1810
d4accfc0 1811/**
1812 * Check that a given name is in a permittable format
1813 *
1814 * @param string $name
1815 * @param string $pageformat
1816 * @return bool
1817 */
5bbbe0be 1818function blocks_name_allowed_in_format($name, $pageformat) {
cd2bc3c9 1819 $accept = NULL;
1820 $maxdepth = -1;
1821 $formats = block_method_result($name, 'applicable_formats');
1822 if (!$formats) {
1823 $formats = array();
1824 }
1825 foreach ($formats as $format => $allowed) {
1826 $formatregex = '/^'.str_replace('*', '[^-]*', $format).'.*$/';
1827 $depth = substr_count($format, '-');
1828 if (preg_match($formatregex, $pageformat) && $depth > $maxdepth) {
1829 $maxdepth = $depth;
1830 $accept = $allowed;
5bbbe0be 1831 }
1832 }
cd2bc3c9 1833 if ($accept === NULL) {
5bbbe0be 1834 $accept = !empty($formats['all']);
1835 }
1836 return $accept;
1837}
1838
feed1900 1839/**
1840 * Delete a block, and associated data.
d4accfc0 1841 *
feed1900 1842 * @param object $instance a row from the block_instances table
847bed23 1843 * @param bool $nolongerused legacy parameter. Not used, but kept for backwards compatibility.
d4accfc0 1844 * @param bool $skipblockstables for internal use only. Makes @see blocks_delete_all_for_context() more efficient.
feed1900 1845 */
1846function blocks_delete_instance($instance, $nolongerused = false, $skipblockstables = false) {
f4d38d20 1847 global $DB;
1848
1849 if ($block = block_instance($instance->blockname, $instance)) {
feed1900 1850 $block->instance_delete();
1851 }
1852 delete_context(CONTEXT_BLOCK, $instance->id);
f032aa7a 1853
feed1900 1854 if (!$skipblockstables) {
1855 $DB->delete_records('block_positions', array('blockinstanceid' => $instance->id));
1856 $DB->delete_records('block_instances', array('id' => $instance->id));
e2f4557a 1857 $DB->delete_records_list('user_preferences', 'name', array('block'.$instance->id.'hidden','docked_block_instance_'.$instance->id));
b33dd23a 1858 }
feed1900 1859}
b33dd23a 1860
feed1900 1861/**
1862 * Delete all the blocks that belong to a particular context.
d4accfc0 1863 *
d4accfc0 1864 * @param int $contextid the context id.
feed1900 1865 */
1866function blocks_delete_all_for_context($contextid) {
1867 global $DB;
a2789e34 1868 $instances = $DB->get_recordset('block_instances', array('parentcontextid' => $contextid));
feed1900 1869 foreach ($instances as $instance) {
1870 blocks_delete_instance($instance, true);
0d6b9d4f 1871 }
feed1900 1872 $instances->close();
13a0d3d3 1873 $DB->delete_records('block_instances', array('parentcontextid' => $contextid));
feed1900 1874 $DB->delete_records('block_positions', array('contextid' => $contextid));
f032aa7a 1875}
1876
ae42ff6f 1877/**
1878 * Set a block to be visible or hidden on a particular page.
1879 *
1880 * @param object $instance a row from the block_instances, preferably LEFT JOINed with the
1881 * block_positions table as return by block_manager.
1882 * @param moodle_page $page the back to set the visibility with respect to.
1883 * @param integer $newvisibility 1 for visible, 0 for hidden.
1884 */
1885function blocks_set_visibility($instance, $page, $newvisibility) {
1886 global $DB;
1887 if (!empty($instance->blockpositionid)) {
1888 // Already have local information on this page.
1889 $DB->set_field('block_positions', 'visible', $newvisibility, array('id' => $instance->blockpositionid));
1890 return;
1891 }
1892
1893 // Create a new block_positions record.
1894 $bp = new stdClass;
1895 $bp->blockinstanceid = $instance->id;
1896 $bp->contextid = $page->context->id;
1897 $bp->pagetype = $page->pagetype;
1898 if ($page->subpage) {
1899 $bp->subpage = $page->subpage;
1900 }
1901 $bp->visible = $newvisibility;
1902 $bp->region = $instance->defaultregion;
1903 $bp->weight = $instance->defaultweight;
1904 $DB->insert_record('block_positions', $bp);
1905}
1906
d4accfc0 1907/**
d4a03c00 1908 * @deprecated since 2.0
1909 * Delete all the blocks from a particular page.
d4accfc0 1910 *
d4a03c00 1911 * @param string $pagetype the page type.
1912 * @param integer $pageid the page id.
1913 * @return bool success or failure.
d4accfc0 1914 */
d4a03c00 1915function blocks_delete_all_on_page($pagetype, $pageid) {
1916 global $DB;
1917
1918 debugging('Call to deprecated function blocks_delete_all_on_page. ' .
1919 'This function cannot work any more. Doing nothing. ' .
1920 'Please update your code to use a block_manager method $PAGE->blocks->....', DEBUG_DEVELOPER);
1921 return false;
0f3fe4b6 1922}
1923
d4accfc0 1924/**
d4a03c00 1925 * Dispite what this function is called, it seems to be mostly used to populate
1926 * the default blocks when a new course (or whatever) is created.
d4accfc0 1927 *
d4a03c00 1928 * @deprecated since 2.0
d4accfc0 1929 *
d4a03c00 1930 * @param object $page the page to add default blocks to.
1931 * @return boolean success or failure.
d4accfc0 1932 */
d4a03c00 1933function blocks_repopulate_page($page) {
1934 global $CFG;
0f3fe4b6 1935
d4a03c00 1936 debugging('Call to deprecated function blocks_repopulate_page. ' .
1937 'Use a more specific method like blocks_add_default_course_blocks, ' .
1938 'or just call $PAGE->blocks->add_blocks()', DEBUG_DEVELOPER);
d23157d8 1939
d4a03c00 1940 /// If the site override has been defined, it is the only valid one.
1941 if (!empty($CFG->defaultblocks_override)) {
1942 $blocknames = $CFG->defaultblocks_override;
1943 } else {
1944 $blocknames = $page->blocks_get_default();
66492322 1945 }
0f3fe4b6 1946
d4a03c00 1947 $blocks = blocks_parse_default_blocks_list($blocknames);
1948 $page->blocks->add_blocks($blocks);
1949
1950 return true;
0f3fe4b6 1951}
1952
08eab897 1953/**
847bed23 1954 * Get the block record for a particular blockid - that is, a particular type os block.
d4accfc0 1955 *
d4accfc0 1956 * @param $int blockid block type id. If null, an array of all block types is returned.
1957 * @param bool $notusedanymore No longer used.
08eab897 1958 * @return array|object row from block table, or all rows.
1959 */
1960function blocks_get_record($blockid = NULL, $notusedanymore = false) {
1961 global $PAGE;
1962 $blocks = $PAGE->blocks->get_installed_blocks();
1963 if ($blockid === NULL) {
1964 return $blocks;
1965 } else if (isset($blocks[$blockid])) {
1966 return $blocks[$blockid];
1967 } else {
1968 return false;
9b4b78fd 1969 }
9b4b78fd 1970}
1971
d4accfc0 1972/**
1973 * Find a given block by its blockid within a provide array
1974 *
1975 * @param int $blockid
1976 * @param array $blocksarray
1977 * @return bool|object Instance if found else false
1978 */
9b4b78fd 1979function blocks_find_block($blockid, $blocksarray) {
0d6b9d4f 1980 if (empty($blocksarray)) {
1981 return false;
1982 }
9b4b78fd 1983 foreach($blocksarray as $blockgroup) {
0d6b9d4f 1984 if (empty($blockgroup)) {
1985 continue;
1986 }
9b4b78fd 1987 foreach($blockgroup as $instance) {
1988 if($instance->blockid == $blockid) {
1989 return $instance;
1990 }
1991 }
1992 }
1993 return false;
1994}
1995
d4a03c00 1996// Functions for programatically adding default blocks to pages ================
0f3fe4b6 1997
9d1d606e 1998/**
1999 * Parse a list of default blocks. See config-dist for a description of the format.
d4accfc0 2000 *
9d1d606e 2001 * @param string $blocksstr
2002 * @return array
2003 */
2004function blocks_parse_default_blocks_list($blocksstr) {
f474a4e5 2005 $blocks = array();
2006 $bits = explode(':', $blocksstr);
2007 if (!empty($bits)) {
7d2a0492 2008 $leftbits = trim(array_shift($bits));
2009 if ($leftbits != '') {
2010 $blocks[BLOCK_POS_LEFT] = explode(',', $leftbits);
2011 }
f474a4e5 2012 }
2013 if (!empty($bits)) {
7d2a0492 2014 $rightbits =trim(array_shift($bits));
2015 if ($rightbits != '') {
2016 $blocks[BLOCK_POS_RIGHT] = explode(',', $rightbits);
2017 }
f474a4e5 2018 }
2019 return $blocks;
9d1d606e 2020}
5b224948 2021
9d1d606e 2022/**
2023 * @return array the blocks that should be added to the site course by default.
2024 */
2025function blocks_get_default_site_course_blocks() {
2026 global $CFG;
9b4b78fd 2027
9d1d606e 2028 if (!empty($CFG->defaultblocks_site)) {
f474a4e5 2029 return blocks_parse_default_blocks_list($CFG->defaultblocks_site);
9d1d606e 2030 } else {
f474a4e5 2031 return array(
7d2a0492 2032 BLOCK_POS_LEFT => array('site_main_menu'),
9d1d606e 2033 BLOCK_POS_RIGHT => array('course_summary', 'calendar_month')
2034 );
9b4b78fd 2035 }
9d1d606e 2036}
2037
2038/**
2039 * Add the default blocks to a course.
d4accfc0 2040 *
9d1d606e 2041 * @param object $course a course object.
2042 */
2043function blocks_add_default_course_blocks($course) {
2044 global $CFG;
2045
2046 if (!empty($CFG->defaultblocks_override)) {
2047 $blocknames = blocks_parse_default_blocks_list($CFG->defaultblocks_override);
2048
2049 } else if ($course->id == SITEID) {
2050 $blocknames = blocks_get_default_site_course_blocks();
2051
2052 } else {
2053 $defaultblocks = 'defaultblocks_' . $course->format;
2054 if (!empty($CFG->$defaultblocks)) {
2055 $blocknames = blocks_parse_default_blocks_list($CFG->$defaultblocks);
2056
2057 } else {
1d00ec6a 2058 $formatconfig = $CFG->dirroot.'/course/format/'.$course->format.'/config.php';
9311b925 2059 $format = array(); // initialize array in external file
1d00ec6a 2060 if (is_readable($formatconfig)) {
9311b925 2061 include($formatconfig);
9d1d606e 2062 }
2063 if (!empty($format['defaultblocks'])) {
2064 $blocknames = blocks_parse_default_blocks_list($format['defaultblocks']);
9b4b78fd 2065
9d1d606e 2066 } else if (!empty($CFG->defaultblocks)){
2067 $blocknames = blocks_parse_default_blocks_list($CFG->defaultblocks);
2068
2069 } else {
2070 $blocknames = array(
7d2a0492 2071 BLOCK_POS_LEFT => array(),
2072 BLOCK_POS_RIGHT => array('search_forums', 'news_items', 'calendar_upcoming', 'recent_activity')
9d1d606e 2073 );
2074 }
2075 }
9b4b78fd 2076 }
2077
f474a4e5 2078 if ($course->id == SITEID) {
2079 $pagetypepattern = 'site-index';
2080 } else {
2081 $pagetypepattern = 'course-view-*';
2082 }
9d1d606e 2083 $page = new moodle_page();
2084 $page->set_course($course);
f474a4e5 2085 $page->blocks->add_blocks($blocknames, $pagetypepattern);
9d1d606e 2086}
2087
2088/**
2089 * Add the default system-context blocks. E.g. the admin tree.
2090 */
2091function blocks_add_default_system_blocks() {
03d9401e
MD
2092 global $DB;
2093
9d1d606e 2094 $page = new moodle_page();
2095 $page->set_context(get_context_instance(CONTEXT_SYSTEM));
3406acde 2096 $page->blocks->add_blocks(array(BLOCK_POS_LEFT => array('navigation', 'settings')), '*', null, true);
7d2a0492 2097 $page->blocks->add_blocks(array(BLOCK_POS_LEFT => array('admin_bookmarks')), 'admin-*', null, null, 2);
03d9401e
MD
2098
2099 if ($defaultmypage = $DB->get_record('my_pages', array('userid'=>null, 'name'=>'__default', 'private'=>1))) {
2100 $subpagepattern = $defaultmypage->id;
2101 } else {
2102 $subpagepattern = null;
2103 }
2104
0184a3fd 2105 $page->blocks->add_blocks(array(BLOCK_POS_RIGHT => array('private_files', 'online_users'), 'content' => array('course_overview')), 'my-index', $subpagepattern, false);
9d1d606e 2106}