3 // This file is part of Moodle - http://moodle.org/
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.
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.
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/>.
19 * Block Class and Functions
21 * This file defines the {@link block_manager} class,
25 * @copyright 1999 onwards Martin Dougiamas http://dougiamas.com
26 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
29 defined('MOODLE_INTERNAL') ||
die();
32 * @deprecated since Moodle 2.0. No longer used.
34 define('BLOCK_MOVE_LEFT', 0x01);
35 define('BLOCK_MOVE_RIGHT', 0x02);
36 define('BLOCK_MOVE_UP', 0x04);
37 define('BLOCK_MOVE_DOWN', 0x08);
38 define('BLOCK_CONFIGURE', 0x10);
42 * Default names for the block regions in the standard theme.
44 define('BLOCK_POS_LEFT', 'side-pre');
45 define('BLOCK_POS_RIGHT', 'side-post');
49 * @deprecated since Moodle 2.0. No longer used.
51 define('BLOCKS_PINNED_TRUE',0);
52 define('BLOCKS_PINNED_FALSE',1);
53 define('BLOCKS_PINNED_BOTH',2);
57 * Exception thrown when someone tried to do something with a block that does
58 * not exist on a page.
60 * @copyright 2009 Tim Hunt
61 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
64 class block_not_on_page_exception
extends moodle_exception
{
67 * @param int $instanceid the block instance id of the block that was looked for.
68 * @param object $page the current page.
70 public function __construct($instanceid, $page) {
72 $a->instanceid
= $instanceid;
73 $a->url
= $page->url
->out();
74 parent
::__construct('blockdoesnotexistonpage', '', $page->url
->out(), $a);
79 * This class keeps track of the block that should appear on a moodle_page.
81 * The page to work with as passed to the constructor.
83 * @copyright 2009 Tim Hunt
84 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
89 * The UI normally only shows block weights between -MAX_WEIGHT and MAX_WEIGHT,
90 * although other weights are valid.
92 const MAX_WEIGHT
= 10;
94 /// Field declarations =========================================================
97 * the moodle_page we are managing blocks for.
102 /** @var array region name => 1.*/
103 protected $regions = array();
105 /** @var string the region where new blocks are added.*/
106 protected $defaultregion = null;
108 /** @var array will be $DB->get_records('blocks') */
109 protected $allblocks = null;
112 * @var array blocks that this user can add to this page. Will be a subset
113 * of $allblocks, but with array keys block->name. Access this via the
114 * {@link get_addable_blocks()} method to ensure it is lazy-loaded.
116 protected $addableblocks = null;
119 * Will be an array region-name => array(db rows loaded in load_blocks);
122 protected $birecordsbyregion = null;
125 * array region-name => array(block objects); populated as necessary by
126 * the ensure_instances_exist method.
129 protected $blockinstances = array();
132 * array region-name => array(block_contents objects) what actually needs to
133 * be displayed in each region.
136 protected $visibleblockcontent = array();
139 * array region-name => array(block_contents objects) extra block-like things
140 * to be displayed in each region, before the real blocks.
143 protected $extracontent = array();
146 * Used by the block move id, to track whether a block is currently being moved.
148 * When you click on the move icon of a block, first the page needs to reload with
149 * extra UI for choosing a new position for a particular block. In that situation
150 * this field holds the id of the block being moved.
154 protected $movingblock = null;
157 * Show only fake blocks
159 protected $fakeblocksonly = false;
161 /// Constructor ================================================================
165 * @param object $page the moodle_page object object we are managing the blocks for,
166 * or a reasonable faxilimily. (See the comment at the top of this class
167 * and {@link http://en.wikipedia.org/wiki/Duck_typing})
169 public function __construct($page) {
173 /// Getter methods =============================================================
176 * Get an array of all region names on this page where a block may appear
178 * @return array the internal names of the regions on this page where block may appear.
180 public function get_regions() {
181 if (is_null($this->defaultregion
)) {
182 $this->page
->initialise_theme_and_output();
184 return array_keys($this->regions
);
188 * Get the region name of the region blocks are added to by default
190 * @return string the internal names of the region where new blocks are added
191 * by default, and where any blocks from an unrecognised region are shown.
192 * (Imagine that blocks were added with one theme selected, then you switched
193 * to a theme with different block positions.)
195 public function get_default_region() {
196 $this->page
->initialise_theme_and_output();
197 return $this->defaultregion
;
201 * The list of block types that may be added to this page.
203 * @return array block name => record from block table.
205 public function get_addable_blocks() {
206 $this->check_is_loaded();
208 if (!is_null($this->addableblocks
)) {
209 return $this->addableblocks
;
213 $this->addableblocks
= array();
215 $allblocks = blocks_get_record();
216 if (empty($allblocks)) {
217 return $this->addableblocks
;
220 $pageformat = $this->page
->pagetype
;
221 foreach($allblocks as $block) {
222 if ($block->visible
&&
223 (block_method_result($block->name
, 'instance_allow_multiple') ||
!$this->is_block_present($block->name
)) &&
224 blocks_name_allowed_in_format($block->name
, $pageformat) &&
225 block_method_result($block->name
, 'user_can_addto', $this->page
)) {
226 $this->addableblocks
[$block->name
] = $block;
230 return $this->addableblocks
;
234 * Given a block name, find out of any of them are currently present in the page
236 * @param string $blockname - the basic name of a block (eg "navigation")
237 * @return boolean - is there one of these blocks in the current page?
239 public function is_block_present($blockname) {
240 if (empty($this->blockinstances
)) {
244 foreach ($this->blockinstances
as $region) {
245 foreach ($region as $instance) {
246 if (empty($instance->instance
->blockname
)) {
249 if ($instance->instance
->blockname
== $blockname) {
258 * Find out if a block type is known by the system
260 * @param string $blockname the name of the type of block.
261 * @param boolean $includeinvisible if false (default) only check 'visible' blocks, that is, blocks enabled by the admin.
262 * @return boolean true if this block in installed.
264 public function is_known_block_type($blockname, $includeinvisible = false) {
265 $blocks = $this->get_installed_blocks();
266 foreach ($blocks as $block) {
267 if ($block->name
== $blockname && ($includeinvisible ||
$block->visible
)) {
275 * Find out if a region exists on a page
277 * @param string $region a region name
278 * @return boolean true if this region exists on this page.
280 public function is_known_region($region) {
281 return array_key_exists($region, $this->regions
);
285 * Get an array of all blocks within a given region
287 * @param string $region a block region that exists on this page.
288 * @return array of block instances.
290 public function get_blocks_for_region($region) {
291 $this->check_is_loaded();
292 $this->ensure_instances_exist($region);
293 return $this->blockinstances
[$region];
297 * Returns an array of block content objects that exist in a region
299 * @param string $region a block region that exists on this page.
300 * @return array of block block_contents objects for all the blocks in a region.
302 public function get_content_for_region($region, $output) {
303 $this->check_is_loaded();
304 $this->ensure_content_created($region, $output);
305 return $this->visibleblockcontent
[$region];
309 * Helper method used by get_content_for_region.
310 * @param string $region region name
311 * @param float $weight weight. May be fractional, since you may want to move a block
312 * between ones with weight 2 and 3, say ($weight would be 2.5).
313 * @return string URL for moving block $this->movingblock to this position.
315 protected function get_move_target_url($region, $weight) {
316 return new moodle_url($this->page
->url
, array('bui_moveid' => $this->movingblock
,
317 'bui_newregion' => $region, 'bui_newweight' => $weight, 'sesskey' => sesskey()));
321 * Determine whether a region contains anything. (Either any real blocks, or
322 * the add new block UI.)
324 * (You may wonder why the $output parameter is required. Unfortunately,
325 * because of the way that blocks work, the only reliable way to find out
326 * if a block will be visible is to get the content for output, and to
327 * get the content, you need a renderer. Fortunately, this is not a
328 * performance problem, because we cache the output that is generated, and
329 * in almost every case where we call region_has_content, we are about to
330 * output the blocks anyway, so we are not doing wasted effort.)
332 * @param string $region a block region that exists on this page.
333 * @param object $output a core_renderer. normally the global $OUTPUT.
334 * @return boolean Whether there is anything in this region.
336 public function region_has_content($region, $output) {
338 if (!$this->is_known_region($region)) {
341 $this->check_is_loaded();
342 $this->ensure_content_created($region, $output);
343 if ($this->page
->user_is_editing() && $this->page
->user_can_edit_blocks()) {
344 // If editing is on, we need all the block regions visible, for the
348 return !empty($this->visibleblockcontent
[$region]) ||
!empty($this->extracontent
[$region]);
352 * Get an array of all of the installed blocks.
354 * @return array contents of the block table.
356 public function get_installed_blocks() {
358 if (is_null($this->allblocks
)) {
359 $this->allblocks
= $DB->get_records('block');
361 return $this->allblocks
;
364 /// Setter methods =============================================================
367 * Add a region to a page
369 * @param string $region add a named region where blocks may appear on the
370 * current page. This is an internal name, like 'side-pre', not a string to
373 public function add_region($region) {
374 $this->check_not_yet_loaded();
375 $this->regions
[$region] = 1;
379 * Add an array of regions
382 * @param array $regions this utility method calls add_region for each array element.
384 public function add_regions($regions) {
385 foreach ($regions as $region) {
386 $this->add_region($region);
391 * Set the default region for new blocks on the page
393 * @param string $defaultregion the internal names of the region where new
394 * blocks should be added by default, and where any blocks from an
395 * unrecognised region are shown.
397 public function set_default_region($defaultregion) {
398 $this->check_not_yet_loaded();
399 if ($defaultregion) {
400 $this->check_region_is_known($defaultregion);
402 $this->defaultregion
= $defaultregion;
406 * Add something that looks like a block, but which isn't an actual block_instance,
409 * @param block_contents $bc the content of the block-like thing.
410 * @param string $region a block region that exists on this page.
412 public function add_fake_block($bc, $region) {
413 $this->page
->initialise_theme_and_output();
414 if (!$this->is_known_region($region)) {
415 $region = $this->get_default_region();
417 if (array_key_exists($region, $this->visibleblockcontent
)) {
418 throw new coding_exception('block_manager has already prepared the blocks in region ' .
419 $region . 'for output. It is too late to add a fake block.');
421 $this->extracontent
[$region][] = $bc;
425 * When the block_manager class was created, the {@link add_fake_block()}
426 * was called add_pretend_block, which is inconsisted with
427 * {@link show_only_fake_blocks()}. To fix this inconsistency, this method
428 * was renamed to add_fake_block. Please update your code.
429 * @param block_contents $bc the content of the block-like thing.
430 * @param string $region a block region that exists on this page.
432 public function add_pretend_block($bc, $region) {
433 debugging(DEBUG_DEVELOPER
, 'add_pretend_block has been renamed to add_fake_block. Please rename the method call in your code.');
434 $this->add_fake_block($bc, $region);
438 * Checks to see whether all of the blocks within the given region are docked
440 * @see region_uses_dock
441 * @param string $region
442 * @return bool True if all of the blocks within that region are docked
444 public function region_completely_docked($region, $output) {
445 if (!$this->page
->theme
->enable_dock
) {
448 $this->check_is_loaded();
449 $this->ensure_content_created($region, $output);
450 foreach($this->visibleblockcontent
[$region] as $instance) {
451 if (!empty($instance->content
) && !get_user_preferences('docked_block_instance_'.$instance->blockinstanceid
, 0)) {
459 * Checks to see whether any of the blocks within the given regions are docked
461 * @see region_completely_docked
462 * @param array|string $regions array of regions (or single region)
463 * @return bool True if any of the blocks within that region are docked
465 public function region_uses_dock($regions, $output) {
466 if (!$this->page
->theme
->enable_dock
) {
469 $this->check_is_loaded();
470 foreach((array)$regions as $region) {
471 $this->ensure_content_created($region, $output);
472 foreach($this->visibleblockcontent
[$region] as $instance) {
473 if(!empty($instance->content
) && get_user_preferences('docked_block_instance_'.$instance->blockinstanceid
, 0)) {
481 /// Actions ====================================================================
484 * This method actually loads the blocks for our page from the database.
486 * @param boolean|null $includeinvisible
487 * null (default) - load hidden blocks if $this->page->user_is_editing();
488 * true - load hidden blocks.
489 * false - don't load hidden blocks.
491 public function load_blocks($includeinvisible = null) {
494 if (!is_null($this->birecordsbyregion
)) {
499 if ($CFG->version
< 2009050619) {
500 // Upgrade/install not complete. Don't try too show any blocks.
501 $this->birecordsbyregion
= array();
505 // Ensure we have been initialised.
506 if (is_null($this->defaultregion
)) {
507 $this->page
->initialise_theme_and_output();
508 // If there are still no block regions, then there are no blocks on this page.
509 if (empty($this->regions
)) {
510 $this->birecordsbyregion
= array();
515 // Check if we need to load normal blocks
516 if ($this->fakeblocksonly
) {
517 $this->birecordsbyregion
= $this->prepare_per_region_arrays();
521 if (is_null($includeinvisible)) {
522 $includeinvisible = $this->page
->user_is_editing();
524 if ($includeinvisible) {
527 $visiblecheck = 'AND (bp.visible = 1 OR bp.visible IS NULL)';
530 $context = $this->page
->context
;
531 $contexttest = 'bi.parentcontextid = :contextid2';
532 $parentcontextparams = array();
533 $parentcontextids = get_parent_contexts($context);
534 if ($parentcontextids) {
535 list($parentcontexttest, $parentcontextparams) =
536 $DB->get_in_or_equal($parentcontextids, SQL_PARAMS_NAMED
, 'parentcontext');
537 $contexttest = "($contexttest OR (bi.showinsubcontexts = 1 AND bi.parentcontextid $parentcontexttest))";
540 $pagetypepatterns = matching_page_type_patterns($this->page
->pagetype
);
541 list($pagetypepatterntest, $pagetypepatternparams) =
542 $DB->get_in_or_equal($pagetypepatterns, SQL_PARAMS_NAMED
, 'pagetypepatterntest');
544 list($ccselect, $ccjoin) = context_instance_preload_sql('bi.id', CONTEXT_BLOCK
, 'ctx');
547 'subpage1' => $this->page
->subpage
,
548 'subpage2' => $this->page
->subpage
,
549 'contextid1' => $context->id
,
550 'contextid2' => $context->id
,
551 'pagetype' => $this->page
->pagetype
,
553 if ($this->page
->subpage
=== '') {
554 $params['subpage1'] = $DB->sql_empty();
555 $params['subpage2'] = $DB->sql_empty();
559 bp.id AS blockpositionid,
562 bi.showinsubcontexts,
567 COALESCE(bp.visible, 1) AS visible,
568 COALESCE(bp.region, bi.defaultregion) AS region,
569 COALESCE(bp.weight, bi.defaultweight) AS weight,
573 FROM {block_instances} bi
574 JOIN {block} b ON bi.blockname = b.name
575 LEFT JOIN {block_positions} bp ON bp.blockinstanceid = bi.id
576 AND bp.contextid = :contextid1
577 AND bp.pagetype = :pagetype
578 AND bp.subpage = :subpage1
583 AND bi.pagetypepattern $pagetypepatterntest
584 AND (bi.subpagepattern IS NULL OR bi.subpagepattern = :subpage2)
589 COALESCE(bp.region, bi.defaultregion),
590 COALESCE(bp.weight, bi.defaultweight),
592 $blockinstances = $DB->get_recordset_sql($sql, $params +
$parentcontextparams +
$pagetypepatternparams);
594 $this->birecordsbyregion
= $this->prepare_per_region_arrays();
596 foreach ($blockinstances as $bi) {
597 context_instance_preload($bi);
598 if ($this->is_known_region($bi->region
)) {
599 $this->birecordsbyregion
[$bi->region
][] = $bi;
605 // Pages don't necessarily have a defaultregion. The one time this can
606 // happen is when there are no theme block regions, but the script itself
607 // has a block region in the main content area.
608 if (!empty($this->defaultregion
)) {
609 $this->birecordsbyregion
[$this->defaultregion
] =
610 array_merge($this->birecordsbyregion
[$this->defaultregion
], $unknown);
615 * Add a block to the current page, or related pages. The block is added to
616 * context $this->page->contextid. If $pagetypepattern $subpagepattern
618 * @param string $blockname The type of block to add.
619 * @param string $region the block region on this page to add the block to.
620 * @param integer $weight determines the order where this block appears in the region.
621 * @param boolean $showinsubcontexts whether this block appears in subcontexts, or just the current context.
622 * @param string|null $pagetypepattern which page types this block should appear on. Defaults to just the current page type.
623 * @param string|null $subpagepattern which subpage this block should appear on. NULL = any (the default), otherwise only the specified subpage.
625 public function add_block($blockname, $region, $weight, $showinsubcontexts, $pagetypepattern = NULL, $subpagepattern = NULL) {
627 // Allow invisible blocks because this is used when adding default page blocks, which
628 // might include invisible ones if the user makes some default blocks invisible
629 $this->check_known_block_type($blockname, true);
630 $this->check_region_is_known($region);
632 if (empty($pagetypepattern)) {
633 $pagetypepattern = $this->page
->pagetype
;
636 $blockinstance = new stdClass
;
637 $blockinstance->blockname
= $blockname;
638 $blockinstance->parentcontextid
= $this->page
->context
->id
;
639 $blockinstance->showinsubcontexts
= !empty($showinsubcontexts);
640 $blockinstance->pagetypepattern
= $pagetypepattern;
641 $blockinstance->subpagepattern
= $subpagepattern;
642 $blockinstance->defaultregion
= $region;
643 $blockinstance->defaultweight
= $weight;
644 $blockinstance->configdata
= '';
645 $blockinstance->id
= $DB->insert_record('block_instances', $blockinstance);
647 // Ensure the block context is created.
648 get_context_instance(CONTEXT_BLOCK
, $blockinstance->id
);
650 // If the new instance was created, allow it to do additional setup
651 if ($block = block_instance($blockname, $blockinstance)) {
652 $block->instance_create();
656 public function add_block_at_end_of_default_region($blockname) {
657 $defaulregion = $this->get_default_region();
659 $lastcurrentblock = end($this->birecordsbyregion
[$defaulregion]);
660 if ($lastcurrentblock) {
661 $weight = $lastcurrentblock->weight +
1;
666 if ($this->page
->subpage
) {
667 $subpage = $this->page
->subpage
;
672 // Special case. Course view page type include the course format, but we
673 // want to add the block non-format-specifically.
674 $pagetypepattern = $this->page
->pagetype
;
675 if (strpos($pagetypepattern, 'course-view') === 0) {
676 $pagetypepattern = 'course-view-*';
679 $this->add_block($blockname, $defaulregion, $weight, false, $pagetypepattern, $subpage);
683 * Convenience method, calls add_block repeatedly for all the blocks in $blocks.
685 * @param array $blocks array with array keys the region names, and values an array of block names.
686 * @param string $pagetypepattern optional. Passed to @see add_block()
687 * @param string $subpagepattern optional. Passed to @see add_block()
689 public function add_blocks($blocks, $pagetypepattern = NULL, $subpagepattern = NULL, $showinsubcontexts=false, $weight=0) {
690 $this->add_regions(array_keys($blocks));
691 foreach ($blocks as $region => $regionblocks) {
693 foreach ($regionblocks as $blockname) {
694 $this->add_block($blockname, $region, $weight, $showinsubcontexts, $pagetypepattern, $subpagepattern);
701 * Move a block to a new position on this page.
703 * If this block cannot appear on any other pages, then we change defaultposition/weight
704 * in the block_instances table. Otherwise we just set the position on this page.
706 * @param $blockinstanceid the block instance id.
707 * @param $newregion the new region name.
708 * @param $newweight the new weight.
710 public function reposition_block($blockinstanceid, $newregion, $newweight) {
713 $this->check_region_is_known($newregion);
714 $inst = $this->find_instance($blockinstanceid);
716 $bi = $inst->instance
;
717 if ($bi->weight
== $bi->defaultweight
&& $bi->region
== $bi->defaultregion
&&
718 !$bi->showinsubcontexts
&& strpos($bi->pagetypepattern
, '*') === false &&
719 (!$this->page
->subpage ||
$bi->subpagepattern
)) {
721 // Set default position
722 $newbi = new stdClass
;
723 $newbi->id
= $bi->id
;
724 $newbi->defaultregion
= $newregion;
725 $newbi->defaultweight
= $newweight;
726 $DB->update_record('block_instances', $newbi);
728 if ($bi->blockpositionid
) {
730 $bp->id
= $bi->blockpositionid
;
731 $bp->region
= $newregion;
732 $bp->weight
= $newweight;
733 $DB->update_record('block_positions', $bp);
737 // Just set position on this page.
739 $bp->region
= $newregion;
740 $bp->weight
= $newweight;
742 if ($bi->blockpositionid
) {
743 $bp->id
= $bi->blockpositionid
;
744 $DB->update_record('block_positions', $bp);
747 $bp->blockinstanceid
= $bi->id
;
748 $bp->contextid
= $this->page
->context
->id
;
749 $bp->pagetype
= $this->page
->pagetype
;
750 if ($this->page
->subpage
) {
751 $bp->subpage
= $this->page
->subpage
;
755 $bp->visible
= $bi->visible
;
756 $DB->insert_record('block_positions', $bp);
762 * Find a given block by its instance id
764 * @param integer $instanceid
767 public function find_instance($instanceid) {
768 foreach ($this->regions
as $region => $notused) {
769 $this->ensure_instances_exist($region);
770 foreach($this->blockinstances
[$region] as $instance) {
771 if ($instance->instance
->id
== $instanceid) {
776 throw new block_not_on_page_exception($instanceid, $this->page
);
779 /// Inner workings =============================================================
782 * Check whether the page blocks have been loaded yet
784 * @return void Throws coding exception if already loaded
786 protected function check_not_yet_loaded() {
787 if (!is_null($this->birecordsbyregion
)) {
788 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.');
793 * Check whether the page blocks have been loaded yet
795 * Nearly identical to the above function {@link check_not_yet_loaded()} except different message
797 * @return void Throws coding exception if already loaded
799 protected function check_is_loaded() {
800 if (is_null($this->birecordsbyregion
)) {
801 throw new coding_exception('block_manager has not yet loaded the blocks, to it is too soon to request the information you asked for.');
806 * Check if a block type is known and usable
808 * @param string $blockname The block type name to search for
809 * @param bool $includeinvisible Include disabled block types in the initial pass
810 * @return void Coding Exception thrown if unknown or not enabled
812 protected function check_known_block_type($blockname, $includeinvisible = false) {
813 if (!$this->is_known_block_type($blockname, $includeinvisible)) {
814 if ($this->is_known_block_type($blockname, true)) {
815 throw new coding_exception('Unknown block type ' . $blockname);
817 throw new coding_exception('Block type ' . $blockname . ' has been disabled by the administrator.');
823 * Check if a region is known by its name
825 * @param string $region
826 * @return void Coding Exception thrown if the region is not known
828 protected function check_region_is_known($region) {
829 if (!$this->is_known_region($region)) {
830 throw new coding_exception('Trying to reference an unknown block region ' . $region);
835 * Returns an array of region names as keys and nested arrays for values
837 * @return array an array where the array keys are the region names, and the array
838 * values are empty arrays.
840 protected function prepare_per_region_arrays() {
842 foreach ($this->regions
as $region => $notused) {
843 $result[$region] = array();
849 * Create a set of new block instance from a record array
851 * @param array $birecords An array of block instance records
852 * @return array An array of instantiated block_instance objects
854 protected function create_block_instances($birecords) {
856 foreach ($birecords as $record) {
857 if ($blockobject = block_instance($record->blockname
, $record, $this->page
)) {
858 $results[] = $blockobject;
865 * Create all the block instances for all the blocks that were loaded by
866 * load_blocks. This is used, for example, to ensure that all blocks get a
867 * chance to initialise themselves via the {@link block_base::specialize()}
868 * method, before any output is done.
870 public function create_all_block_instances() {
871 foreach ($this->get_regions() as $region) {
872 $this->ensure_instances_exist($region);
877 * Return an array of content objects from a set of block instances
879 * @param array $instances An array of block instances
880 * @param renderer_base The renderer to use.
881 * @param string $region the region name.
882 * @return array An array of block_content (and possibly block_move_target) objects.
884 protected function create_block_contents($instances, $output, $region) {
889 if ($this->movingblock
) {
890 $first = reset($instances);
892 $lastweight = $first->instance
->weight
- 2;
895 $strmoveblockhere = get_string('moveblockhere', 'block');
898 foreach ($instances as $instance) {
899 $content = $instance->get_content_for_output($output);
900 if (empty($content)) {
904 if ($this->movingblock
&& $lastweight != $instance->instance
->weight
&&
905 $content->blockinstanceid
!= $this->movingblock
&& $lastblock != $this->movingblock
) {
906 $results[] = new block_move_target($strmoveblockhere, $this->get_move_target_url($region, ($lastweight +
$instance->instance
->weight
)/2));
909 if ($content->blockinstanceid
== $this->movingblock
) {
910 $content->add_class('beingmoved');
911 $content->annotation
.= get_string('movingthisblockcancel', 'block',
912 html_writer
::link($this->page
->url
, get_string('cancel')));
915 $results[] = $content;
916 $lastweight = $instance->instance
->weight
;
917 $lastblock = $instance->instance
->id
;
920 if ($this->movingblock
&& $lastblock != $this->movingblock
) {
921 $results[] = new block_move_target($strmoveblockhere, $this->get_move_target_url($region, $lastweight +
1));
927 * Ensure block instances exist for a given region
929 * @param string $region Check for bi's with the instance with this name
931 protected function ensure_instances_exist($region) {
932 $this->check_region_is_known($region);
933 if (!array_key_exists($region, $this->blockinstances
)) {
934 $this->blockinstances
[$region] =
935 $this->create_block_instances($this->birecordsbyregion
[$region]);
940 * Ensure that there is some content within the given region
942 * @param string $region The name of the region to check
944 protected function ensure_content_created($region, $output) {
945 $this->ensure_instances_exist($region);
946 if (!array_key_exists($region, $this->visibleblockcontent
)) {
948 if (array_key_exists($region, $this->extracontent
)) {
949 $contents = $this->extracontent
[$region];
951 $contents = array_merge($contents, $this->create_block_contents($this->blockinstances
[$region], $output, $region));
952 if ($region == $this->defaultregion
) {
953 $addblockui = block_add_block_ui($this->page
, $output);
955 $contents[] = $addblockui;
958 $this->visibleblockcontent
[$region] = $contents;
962 /// Process actions from the URL ===============================================
965 * Get the appropriate list of editing icons for a block. This is used
966 * to set {@link block_contents::$controls} in {@link block_base::get_contents_for_output()}.
968 * @param $output The core_renderer to use when generating the output. (Need to get icon paths.)
969 * @return an array in the format for {@link block_contents::$controls}
971 public function edit_controls($block) {
974 if (!isset($CFG->undeletableblocktypes
) ||
(!is_array($CFG->undeletableblocktypes
) && !is_string($CFG->undeletableblocktypes
))) {
975 $undeletableblocktypes = array('navigation','settings');
976 } else if (is_string($CFG->undeletableblocktypes
)) {
977 $undeletableblocktypes = explode(',', $CFG->undeletableblocktypes
);
979 $undeletableblocktypes = $CFG->undeletableblocktypes
;
983 $actionurl = $this->page
->url
->out(false, array('sesskey'=> sesskey()));
985 // Assign roles icon.
986 if (has_capability('moodle/role:assign', $block->context
)) {
987 //TODO: please note it is sloppy to pass urls through page parameters!!
988 // it is shortened because some web servers (e.g. IIS by default) give
989 // a 'security' error if you try to pass a full URL as a GET parameter in another URL.
991 $return = $this->page
->url
->out(false);
992 $return = str_replace($CFG->wwwroot
. '/', '', $return);
994 $controls[] = array('url' => $CFG->wwwroot
. '/' . $CFG->admin
.
995 '/roles/assign.php?contextid=' . $block->context
->id
. '&returnurl=' . urlencode($return),
996 'icon' => 'i/roles', 'caption' => get_string('assignroles', 'role'));
999 if ($this->page
->user_can_edit_blocks() && $block->instance_can_be_hidden()) {
1001 if ($block->instance
->visible
) {
1002 $controls[] = array('url' => $actionurl . '&bui_hideid=' . $block->instance
->id
,
1003 'icon' => 't/hide', 'caption' => get_string('hide'));
1005 $controls[] = array('url' => $actionurl . '&bui_showid=' . $block->instance
->id
,
1006 'icon' => 't/show', 'caption' => get_string('show'));
1010 if ($this->page
->user_can_edit_blocks() ||
$block->user_can_edit()) {
1011 // Edit config icon - always show - needed for positioning UI.
1012 $controls[] = array('url' => $actionurl . '&bui_editid=' . $block->instance
->id
,
1013 'icon' => 't/edit', 'caption' => get_string('configuration'));
1016 if ($this->page
->user_can_edit_blocks() && $block->user_can_edit() && $block->user_can_addto($this->page
)) {
1017 if (!in_array($block->instance
->blockname
, $undeletableblocktypes)
1018 ||
!in_array($block->instance
->pagetypepattern
, array('*', 'site-index'))
1019 ||
$block->instance
->parentcontextid
!= SITEID
) {
1021 $controls[] = array('url' => $actionurl . '&bui_deleteid=' . $block->instance
->id
,
1022 'icon' => 't/delete', 'caption' => get_string('delete'));
1026 if ($this->page
->user_can_edit_blocks()) {
1028 $controls[] = array('url' => $actionurl . '&bui_moveid=' . $block->instance
->id
,
1029 'icon' => 't/move', 'caption' => get_string('move'));
1036 * Process any block actions that were specified in the URL.
1038 * This can only be done given a valid $page object.
1040 * @param moodle_page $page the page to add blocks to.
1041 * @return boolean true if anything was done. False if not.
1043 public function process_url_actions() {
1044 if (!$this->page
->user_is_editing()) {
1047 return $this->process_url_add() ||
$this->process_url_delete() ||
1048 $this->process_url_show_hide() ||
$this->process_url_edit() ||
1049 $this->process_url_move();
1053 * Handle adding a block.
1054 * @return boolean true if anything was done. False if not.
1056 public function process_url_add() {
1057 $blocktype = optional_param('bui_addblock', null, PARAM_SAFEDIR
);
1064 if (!$this->page
->user_can_edit_blocks()) {
1065 throw new moodle_exception('nopermissions', '', $this->page
->url
->out(), get_string('addblock'));
1068 if (!array_key_exists($blocktype, $this->get_addable_blocks())) {
1069 throw new moodle_exception('cannotaddthisblocktype', '', $this->page
->url
->out(), $blocktype);
1072 $this->add_block_at_end_of_default_region($blocktype);
1074 // If the page URL was a guess, it will contain the bui_... param, so we must make sure it is not there.
1075 $this->page
->ensure_param_not_in_url('bui_addblock');
1081 * Handle deleting a block.
1082 * @return boolean true if anything was done. False if not.
1084 public function process_url_delete() {
1085 $blockid = optional_param('bui_deleteid', null, PARAM_INTEGER
);
1092 $block = $this->page
->blocks
->find_instance($blockid);
1094 if (!$block->user_can_edit() ||
!$this->page
->user_can_edit_blocks() ||
!$block->user_can_addto($this->page
)) {
1095 throw new moodle_exception('nopermissions', '', $this->page
->url
->out(), get_string('deleteablock'));
1098 blocks_delete_instance($block->instance
);
1100 // If the page URL was a guess, it will contain the bui_... param, so we must make sure it is not there.
1101 $this->page
->ensure_param_not_in_url('bui_deleteid');
1107 * Handle showing or hiding a block.
1108 * @return boolean true if anything was done. False if not.
1110 public function process_url_show_hide() {
1111 if ($blockid = optional_param('bui_hideid', null, PARAM_INTEGER
)) {
1113 } else if ($blockid = optional_param('bui_showid', null, PARAM_INTEGER
)) {
1121 $block = $this->page
->blocks
->find_instance($blockid);
1123 if (!$this->page
->user_can_edit_blocks()) {
1124 throw new moodle_exception('nopermissions', '', $this->page
->url
->out(), get_string('hideshowblocks'));
1125 } else if (!$block->instance_can_be_hidden()) {
1129 blocks_set_visibility($block->instance
, $this->page
, $newvisibility);
1131 // If the page URL was a guses, it will contain the bui_... param, so we must make sure it is not there.
1132 $this->page
->ensure_param_not_in_url('bui_hideid');
1133 $this->page
->ensure_param_not_in_url('bui_showid');
1139 * Handle showing/processing the submission from the block editing form.
1140 * @return boolean true if the form was submitted and the new config saved. Does not
1141 * return if the editing form was displayed. False otherwise.
1143 public function process_url_edit() {
1144 global $CFG, $DB, $PAGE;
1146 $blockid = optional_param('bui_editid', null, PARAM_INTEGER
);
1152 require_once($CFG->dirroot
. '/blocks/edit_form.php');
1154 $block = $this->find_instance($blockid);
1156 if (!$block->user_can_edit() && !$this->page
->user_can_edit_blocks()) {
1157 throw new moodle_exception('nopermissions', '', $this->page
->url
->out(), get_string('editblock'));
1160 $editpage = new moodle_page();
1161 $editpage->set_pagelayout('admin');
1162 $editpage->set_course($this->page
->course
);
1163 $editpage->set_context($block->context
);
1164 if ($this->page
->cm
) {
1165 $editpage->set_cm($this->page
->cm
);
1167 $editurlbase = str_replace($CFG->wwwroot
. '/', '/', $this->page
->url
->out_omit_querystring());
1168 $editurlparams = $this->page
->url
->params();
1169 $editurlparams['bui_editid'] = $blockid;
1170 $editpage->set_url($editurlbase, $editurlparams);
1171 $editpage->set_block_actions_done();
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.)
1176 $formfile = $CFG->dirroot
. '/blocks/' . $block->name() . '/edit_form.php';
1177 if (is_readable($formfile)) {
1178 require_once($formfile);
1179 $classname = 'block_' . $block->name() . '_edit_form';
1180 if (!class_exists($classname)) {
1181 $classname = 'block_edit_form';
1184 $classname = 'block_edit_form';
1187 $mform = new $classname($editpage->url
, $block, $this->page
);
1188 $mform->set_data($block->instance
);
1190 if ($mform->is_cancelled()) {
1191 redirect($this->page
->url
);
1193 } else if ($data = $mform->get_data()) {
1195 $bi->id
= $block->instance
->id
;
1196 $bi->pagetypepattern
= $data->bui_pagetypepattern
;
1197 if (empty($data->bui_subpagepattern
) ||
$data->bui_subpagepattern
== '%@NULL@%') {
1198 $bi->subpagepattern
= null;
1200 $bi->subpagepattern
= $data->bui_subpagepattern
;
1203 $parentcontext = get_context_instance_by_id($data->bui_parentcontextid
);
1204 $systemcontext = get_context_instance(CONTEXT_SYSTEM
);
1206 // Updating stickiness and contexts. See MDL-21375 for details.
1207 if (has_capability('moodle/site:manageblocks', $parentcontext)) { // Check permissions in destination
1208 // Explicitly set the context
1209 $bi->parentcontextid
= $parentcontext->id
;
1211 // If the context type is > 0 then we'll explicitly set the block as sticky, otherwise not
1212 $bi->showinsubcontexts
= (int)(!empty($data->bui_contexts
));
1214 // If the block wants to be system-wide, then explicitly set that
1215 if ($data->bui_contexts
== 2) { // Only possible on a frontpage or system page
1216 $bi->parentcontextid
= $systemcontext->id
;
1218 } else { // The block doesn't want to be system-wide, so let's ensure that
1219 if ($parentcontext->id
== $systemcontext->id
) { // We need to move it to the front page
1220 $frontpagecontext = get_context_instance(CONTEXT_COURSE
, SITEID
);
1221 $bi->parentcontextid
= $frontpagecontext->id
;
1222 $bi->pagetypepattern
= '*'; // Just in case
1227 $bi->defaultregion
= $data->bui_defaultregion
;
1228 $bi->defaultweight
= $data->bui_defaultweight
;
1229 $DB->update_record('block_instances', $bi);
1231 if (!empty($block->config
)) {
1232 $config = clone($block->config
);
1234 $config = new stdClass
;
1236 foreach ($data as $configfield => $value) {
1237 if (strpos($configfield, 'config_') !== 0) {
1240 $field = substr($configfield, 7);
1241 $config->$field = $value;
1243 $block->instance_config_save($config);
1246 $bp->visible
= $data->bui_visible
;
1247 $bp->region
= $data->bui_region
;
1248 $bp->weight
= $data->bui_weight
;
1249 $needbprecord = !$data->bui_visible ||
$data->bui_region
!= $data->bui_defaultregion ||
1250 $data->bui_weight
!= $data->bui_defaultweight
;
1252 if ($block->instance
->blockpositionid
&& !$needbprecord) {
1253 $DB->delete_records('block_positions', array('id' => $block->instance
->blockpositionid
));
1255 } else if ($block->instance
->blockpositionid
&& $needbprecord) {
1256 $bp->id
= $block->instance
->blockpositionid
;
1257 $DB->update_record('block_positions', $bp);
1259 } else if ($needbprecord) {
1260 $bp->blockinstanceid
= $block->instance
->id
;
1261 $bp->contextid
= $this->page
->context
->id
;
1262 $bp->pagetype
= $this->page
->pagetype
;
1263 if ($this->page
->subpage
) {
1264 $bp->subpage
= $this->page
->subpage
;
1268 $DB->insert_record('block_positions', $bp);
1271 redirect($this->page
->url
);
1274 $strheading = get_string('blockconfiga', 'moodle', $block->get_title());
1275 $editpage->set_title($strheading);
1276 $editpage->set_heading($strheading);
1277 $editpage->navbar
->add($strheading);
1278 $output = $editpage->get_renderer('core');
1279 echo $output->header();
1280 echo $output->heading($strheading, 2);
1282 echo $output->footer();
1288 * Handle showing/processing the submission from the block editing form.
1289 * @return boolean true if the form was submitted and the new config saved. Does not
1290 * return if the editing form was displayed. False otherwise.
1292 public function process_url_move() {
1293 global $CFG, $DB, $PAGE;
1295 $blockid = optional_param('bui_moveid', null, PARAM_INTEGER
);
1302 $block = $this->find_instance($blockid);
1304 if (!$this->page
->user_can_edit_blocks()) {
1305 throw new moodle_exception('nopermissions', '', $this->page
->url
->out(), get_string('editblock'));
1308 $newregion = optional_param('bui_newregion', '', PARAM_ALPHANUMEXT
);
1309 $newweight = optional_param('bui_newweight', null, PARAM_FLOAT
);
1310 if (!$newregion ||
is_null($newweight)) {
1311 // Don't have a valid target position yet, must be just starting the move.
1312 $this->movingblock
= $blockid;
1313 $this->page
->ensure_param_not_in_url('bui_moveid');
1317 if (!$this->is_known_region($newregion)) {
1318 throw new moodle_exception('unknownblockregion', '', $this->page
->url
, $newregion);
1321 // Move this block. This may involve moving other nearby blocks.
1322 $blocks = $this->birecordsbyregion
[$newregion];
1324 $maxweight = self
::MAX_WEIGHT
;
1325 $minweight = -self
::MAX_WEIGHT
;
1327 // Initialise the used weights and spareweights array with the default values
1328 $spareweights = array();
1329 $usedweights = array();
1330 for ($i = $minweight; $i <= $maxweight; $i++
) {
1331 $spareweights[$i] = $i;
1332 $usedweights[$i] = array();
1335 // Check each block and sort out where we have used weights
1336 foreach ($blocks as $bi) {
1337 if ($bi->weight
> $maxweight) {
1338 // If this statement is true then the blocks weight is more than the
1339 // current maximum. To ensure that we can get the best block position
1340 // we will initialise elements within the usedweights and spareweights
1341 // arrays between the blocks weight (which will then be the new max) and
1343 $parseweight = $bi->weight
;
1344 while (!array_key_exists($parseweight, $usedweights)) {
1345 $usedweights[$parseweight] = array();
1346 $spareweights[$parseweight] = $parseweight;
1349 $maxweight = $bi->weight
;
1350 } else if ($bi->weight
< $minweight) {
1351 // As above except this time the blocks weight is LESS than the
1352 // the current minimum, so we will initialise the array from the
1353 // blocks weight (new minimum) to the current minimum
1354 $parseweight = $bi->weight
;
1355 while (!array_key_exists($parseweight, $usedweights)) {
1356 $usedweights[$parseweight] = array();
1357 $spareweights[$parseweight] = $parseweight;
1360 $minweight = $bi->weight
;
1362 if ($bi->id
!= $block->instance
->id
) {
1363 unset($spareweights[$bi->weight
]);
1364 $usedweights[$bi->weight
][] = $bi->id
;
1368 // First we find the nearest gap in the list of weights.
1369 $bestdistance = max(abs($newweight - self
::MAX_WEIGHT
), abs($newweight + self
::MAX_WEIGHT
)) +
1;
1371 foreach ($spareweights as $spareweight) {
1372 if (abs($newweight - $spareweight) < $bestdistance) {
1373 $bestdistance = abs($newweight - $spareweight);
1374 $bestgap = $spareweight;
1378 // If there is no gap, we have to go outside -self::MAX_WEIGHT .. self::MAX_WEIGHT.
1379 if (is_null($bestgap)) {
1380 $bestgap = self
::MAX_WEIGHT +
1;
1381 while (!empty($usedweights[$bestgap])) {
1386 // Now we know the gap we are aiming for, so move all the blocks along.
1387 if ($bestgap < $newweight) {
1388 $newweight = floor($newweight);
1389 for ($weight = $bestgap +
1; $weight <= $newweight; $weight++
) {
1390 foreach ($usedweights[$weight] as $biid) {
1391 $this->reposition_block($biid, $newregion, $weight - 1);
1394 $this->reposition_block($block->instance
->id
, $newregion, $newweight);
1396 $newweight = ceil($newweight);
1397 for ($weight = $bestgap - 1; $weight >= $newweight; $weight--) {
1398 foreach ($usedweights[$weight] as $biid) {
1399 $this->reposition_block($biid, $newregion, $weight +
1);
1402 $this->reposition_block($block->instance
->id
, $newregion, $newweight);
1405 $this->page
->ensure_param_not_in_url('bui_moveid');
1406 $this->page
->ensure_param_not_in_url('bui_newregion');
1407 $this->page
->ensure_param_not_in_url('bui_newweight');
1412 * Turns the display of normal blocks either on or off.
1414 * @param bool $setting
1416 public function show_only_fake_blocks($setting = true) {
1417 $this->fakeblocksonly
= $setting;
1421 /// Helper functions for working with block classes ============================
1424 * Call a class method (one that does not require a block instance) on a block class.
1426 * @param string $blockname the name of the block.
1427 * @param string $method the method name.
1428 * @param array $param parameters to pass to the method.
1429 * @return mixed whatever the method returns.
1431 function block_method_result($blockname, $method, $param = NULL) {
1432 if(!block_load_class($blockname)) {
1435 return call_user_func(array('block_'.$blockname, $method), $param);
1439 * Creates a new instance of the specified block class.
1441 * @param string $blockname the name of the block.
1442 * @param $instance block_instances DB table row (optional).
1443 * @param moodle_page $page the page this block is appearing on.
1444 * @return block_base the requested block instance.
1446 function block_instance($blockname, $instance = NULL, $page = NULL) {
1447 if(!block_load_class($blockname)) {
1450 $classname = 'block_'.$blockname;
1451 $retval = new $classname;
1452 if($instance !== NULL) {
1453 if (is_null($page)) {
1457 $retval->_load_instance($instance, $page);
1463 * Load the block class for a particular type of block.
1465 * @param string $blockname the name of the block.
1466 * @return boolean success or failure.
1468 function block_load_class($blockname) {
1471 if(empty($blockname)) {
1475 $classname = 'block_'.$blockname;
1477 if(class_exists($classname)) {
1481 $blockpath = $CFG->dirroot
.'/blocks/'.$blockname.'/block_'.$blockname.'.php';
1483 if (file_exists($blockpath)) {
1484 require_once($CFG->dirroot
.'/blocks/moodleblock.class.php');
1485 include_once($blockpath);
1487 //debugging("$blockname code does not exist in $blockpath", DEBUG_DEVELOPER);
1491 return class_exists($classname);
1495 * Given a specific page type, return all the page type patterns that might
1498 * @param string $pagetype for example 'course-view-weeks' or 'mod-quiz-view'.
1499 * @return array an array of all the page type patterns that might match this page type.
1501 function matching_page_type_patterns($pagetype) {
1502 $patterns = array($pagetype);
1503 $bits = explode('-', $pagetype);
1504 if (count($bits) == 3 && $bits[0] == 'mod') {
1505 if ($bits[2] == 'view') {
1506 $patterns[] = 'mod-*-view';
1507 } else if ($bits[2] == 'index') {
1508 $patterns[] = 'mod-*-index';
1511 while (count($bits) > 0) {
1512 $patterns[] = implode('-', $bits) . '-*';
1519 /// Functions update the blocks if required by the request parameters ==========
1522 * Return a {@link block_contents} representing the add a new block UI, if
1523 * this user is allowed to see it.
1525 * @return block_contents an appropriate block_contents, or null if the user
1526 * cannot add any blocks here.
1528 function block_add_block_ui($page, $output) {
1529 global $CFG, $OUTPUT;
1530 if (!$page->user_is_editing() ||
!$page->user_can_edit_blocks()) {
1534 $bc = new block_contents();
1535 $bc->title
= get_string('addblock');
1536 $bc->add_class('block_adminblock');
1538 $missingblocks = $page->blocks
->get_addable_blocks();
1539 if (empty($missingblocks)) {
1540 $bc->content
= get_string('noblockstoaddhere');
1545 foreach ($missingblocks as $block) {
1546 $blockobject = block_instance($block->name
);
1547 if ($blockobject !== false && $blockobject->user_can_addto($page)) {
1548 $menu[$block->name
] = $blockobject->get_title();
1551 textlib_get_instance()->asort($menu);
1553 $actionurl = new moodle_url($page->url
, array('sesskey'=>sesskey()));
1554 $select = new single_select($actionurl, 'bui_addblock', $menu, null, array(''=>get_string('adddots')), 'add_block');
1555 $bc->content
= $OUTPUT->render($select);
1559 // Functions that have been deprecated by block_manager =======================
1562 * @deprecated since Moodle 2.0 - use $page->blocks->get_addable_blocks();
1564 * This function returns an array with the IDs of any blocks that you can add to your page.
1565 * Parameters are passed by reference for speed; they are not modified at all.
1567 * @param $page the page object.
1568 * @param $blockmanager Not used.
1569 * @return array of block type ids.
1571 function blocks_get_missing(&$page, &$blockmanager) {
1572 debugging('blocks_get_missing is deprecated. Please use $page->blocks->get_addable_blocks() instead.', DEBUG_DEVELOPER
);
1573 $blocks = $page->blocks
->get_addable_blocks();
1575 foreach ($blocks as $block) {
1576 $ids[] = $block->id
;
1582 * Actually delete from the database any blocks that are currently on this page,
1583 * but which should not be there according to blocks_name_allowed_in_format.
1585 * @todo Write/Fix this function. Currently returns immediately
1588 function blocks_remove_inappropriate($course) {
1592 $blockmanager = blocks_get_by_page($page);
1594 if (empty($blockmanager)) {
1598 if (($pageformat = $page->pagetype) == NULL) {
1602 foreach($blockmanager as $region) {
1603 foreach($region as $instance) {
1604 $block = blocks_get_record($instance->blockid);
1605 if(!blocks_name_allowed_in_format($block->name, $pageformat)) {
1606 blocks_delete_instance($instance->instance);
1613 * Check that a given name is in a permittable format
1615 * @param string $name
1616 * @param string $pageformat
1619 function blocks_name_allowed_in_format($name, $pageformat) {
1622 $formats = block_method_result($name, 'applicable_formats');
1626 foreach ($formats as $format => $allowed) {
1627 $formatregex = '/^'.str_replace('*', '[^-]*', $format).'.*$/';
1628 $depth = substr_count($format, '-');
1629 if (preg_match($formatregex, $pageformat) && $depth > $maxdepth) {
1634 if ($accept === NULL) {
1635 $accept = !empty($formats['all']);
1641 * Delete a block, and associated data.
1643 * @param object $instance a row from the block_instances table
1644 * @param bool $nolongerused legacy parameter. Not used, but kept for backwards compatibility.
1645 * @param bool $skipblockstables for internal use only. Makes @see blocks_delete_all_for_context() more efficient.
1647 function blocks_delete_instance($instance, $nolongerused = false, $skipblockstables = false) {
1650 if ($block = block_instance($instance->blockname
, $instance)) {
1651 $block->instance_delete();
1653 delete_context(CONTEXT_BLOCK
, $instance->id
);
1655 if (!$skipblockstables) {
1656 $DB->delete_records('block_positions', array('blockinstanceid' => $instance->id
));
1657 $DB->delete_records('block_instances', array('id' => $instance->id
));
1658 $DB->delete_records_list('user_preferences', 'name', array('block'.$instance->id
.'hidden','docked_block_instance_'.$instance->id
));
1663 * Delete all the blocks that belong to a particular context.
1665 * @param int $contextid the context id.
1667 function blocks_delete_all_for_context($contextid) {
1669 $instances = $DB->get_recordset('block_instances', array('parentcontextid' => $contextid));
1670 foreach ($instances as $instance) {
1671 blocks_delete_instance($instance, true);
1673 $instances->close();
1674 $DB->delete_records('block_instances', array('parentcontextid' => $contextid));
1675 $DB->delete_records('block_positions', array('contextid' => $contextid));
1679 * Set a block to be visible or hidden on a particular page.
1681 * @param object $instance a row from the block_instances, preferably LEFT JOINed with the
1682 * block_positions table as return by block_manager.
1683 * @param moodle_page $page the back to set the visibility with respect to.
1684 * @param integer $newvisibility 1 for visible, 0 for hidden.
1686 function blocks_set_visibility($instance, $page, $newvisibility) {
1688 if (!empty($instance->blockpositionid
)) {
1689 // Already have local information on this page.
1690 $DB->set_field('block_positions', 'visible', $newvisibility, array('id' => $instance->blockpositionid
));
1694 // Create a new block_positions record.
1696 $bp->blockinstanceid
= $instance->id
;
1697 $bp->contextid
= $page->context
->id
;
1698 $bp->pagetype
= $page->pagetype
;
1699 if ($page->subpage
) {
1700 $bp->subpage
= $page->subpage
;
1702 $bp->visible
= $newvisibility;
1703 $bp->region
= $instance->defaultregion
;
1704 $bp->weight
= $instance->defaultweight
;
1705 $DB->insert_record('block_positions', $bp);
1709 * @deprecated since 2.0
1710 * Delete all the blocks from a particular page.
1712 * @param string $pagetype the page type.
1713 * @param integer $pageid the page id.
1714 * @return bool success or failure.
1716 function blocks_delete_all_on_page($pagetype, $pageid) {
1719 debugging('Call to deprecated function blocks_delete_all_on_page. ' .
1720 'This function cannot work any more. Doing nothing. ' .
1721 'Please update your code to use a block_manager method $PAGE->blocks->....', DEBUG_DEVELOPER
);
1726 * Dispite what this function is called, it seems to be mostly used to populate
1727 * the default blocks when a new course (or whatever) is created.
1729 * @deprecated since 2.0
1731 * @param object $page the page to add default blocks to.
1732 * @return boolean success or failure.
1734 function blocks_repopulate_page($page) {
1737 debugging('Call to deprecated function blocks_repopulate_page. ' .
1738 'Use a more specific method like blocks_add_default_course_blocks, ' .
1739 'or just call $PAGE->blocks->add_blocks()', DEBUG_DEVELOPER
);
1741 /// If the site override has been defined, it is the only valid one.
1742 if (!empty($CFG->defaultblocks_override
)) {
1743 $blocknames = $CFG->defaultblocks_override
;
1745 $blocknames = $page->blocks_get_default();
1748 $blocks = blocks_parse_default_blocks_list($blocknames);
1749 $page->blocks
->add_blocks($blocks);
1755 * Get the block record for a particular blockid - that is, a particular type os block.
1757 * @param $int blockid block type id. If null, an array of all block types is returned.
1758 * @param bool $notusedanymore No longer used.
1759 * @return array|object row from block table, or all rows.
1761 function blocks_get_record($blockid = NULL, $notusedanymore = false) {
1763 $blocks = $PAGE->blocks
->get_installed_blocks();
1764 if ($blockid === NULL) {
1766 } else if (isset($blocks[$blockid])) {
1767 return $blocks[$blockid];
1774 * Find a given block by its blockid within a provide array
1776 * @param int $blockid
1777 * @param array $blocksarray
1778 * @return bool|object Instance if found else false
1780 function blocks_find_block($blockid, $blocksarray) {
1781 if (empty($blocksarray)) {
1784 foreach($blocksarray as $blockgroup) {
1785 if (empty($blockgroup)) {
1788 foreach($blockgroup as $instance) {
1789 if($instance->blockid
== $blockid) {
1797 // Functions for programatically adding default blocks to pages ================
1800 * Parse a list of default blocks. See config-dist for a description of the format.
1802 * @param string $blocksstr
1805 function blocks_parse_default_blocks_list($blocksstr) {
1807 $bits = explode(':', $blocksstr);
1808 if (!empty($bits)) {
1809 $leftbits = trim(array_shift($bits));
1810 if ($leftbits != '') {
1811 $blocks[BLOCK_POS_LEFT
] = explode(',', $leftbits);
1814 if (!empty($bits)) {
1815 $rightbits =trim(array_shift($bits));
1816 if ($rightbits != '') {
1817 $blocks[BLOCK_POS_RIGHT
] = explode(',', $rightbits);
1824 * @return array the blocks that should be added to the site course by default.
1826 function blocks_get_default_site_course_blocks() {
1829 if (!empty($CFG->defaultblocks_site
)) {
1830 return blocks_parse_default_blocks_list($CFG->defaultblocks_site
);
1833 BLOCK_POS_LEFT
=> array('site_main_menu'),
1834 BLOCK_POS_RIGHT
=> array('course_summary', 'calendar_month')
1840 * Add the default blocks to a course.
1842 * @param object $course a course object.
1844 function blocks_add_default_course_blocks($course) {
1847 if (!empty($CFG->defaultblocks_override
)) {
1848 $blocknames = blocks_parse_default_blocks_list($CFG->defaultblocks_override
);
1850 } else if ($course->id
== SITEID
) {
1851 $blocknames = blocks_get_default_site_course_blocks();
1854 $defaultblocks = 'defaultblocks_' . $course->format
;
1855 if (!empty($CFG->$defaultblocks)) {
1856 $blocknames = blocks_parse_default_blocks_list($CFG->$defaultblocks);
1859 $formatconfig = $CFG->dirroot
.'/course/format/'.$course->format
.'/config.php';
1860 $format = array(); // initialize array in external file
1861 if (is_readable($formatconfig)) {
1862 include($formatconfig);
1864 if (!empty($format['defaultblocks'])) {
1865 $blocknames = blocks_parse_default_blocks_list($format['defaultblocks']);
1867 } else if (!empty($CFG->defaultblocks
)){
1868 $blocknames = blocks_parse_default_blocks_list($CFG->defaultblocks
);
1871 $blocknames = array(
1872 BLOCK_POS_LEFT
=> array(),
1873 BLOCK_POS_RIGHT
=> array('search_forums', 'news_items', 'calendar_upcoming', 'recent_activity')
1879 if ($course->id
== SITEID
) {
1880 $pagetypepattern = 'site-index';
1882 $pagetypepattern = 'course-view-*';
1884 $page = new moodle_page();
1885 $page->set_course($course);
1886 $page->blocks
->add_blocks($blocknames, $pagetypepattern);
1890 * Add the default system-context blocks. E.g. the admin tree.
1892 function blocks_add_default_system_blocks() {
1895 $page = new moodle_page();
1896 $page->set_context(get_context_instance(CONTEXT_SYSTEM
));
1897 $page->blocks
->add_blocks(array(BLOCK_POS_LEFT
=> array('navigation', 'settings')), '*', null, true);
1898 $page->blocks
->add_blocks(array(BLOCK_POS_LEFT
=> array('admin_bookmarks')), 'admin-*', null, null, 2);
1900 if ($defaultmypage = $DB->get_record('my_pages', array('userid'=>null, 'name'=>'__default', 'private'=>1))) {
1901 $subpagepattern = $defaultmypage->id
;
1903 $subpagepattern = null;
1906 $page->blocks
->add_blocks(array(BLOCK_POS_RIGHT
=> array('private_files', 'online_users'), 'content' => array('course_overview')), 'my-index', $subpagepattern, false);