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