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 =========================================================
96 /** @var moodle_page the moodle_page we are managing blocks for. */
99 /** @var array region name => 1.*/
100 protected $regions = array();
102 /** @var string the region where new blocks are added.*/
103 protected $defaultregion = null;
105 /** @var array will be $DB->get_records('blocks') */
106 protected $allblocks = null;
109 * @var array blocks that this user can add to this page. Will be a subset
110 * of $allblocks, but with array keys block->name. Access this via the
111 * {@link get_addable_blocks()} method to ensure it is lazy-loaded.
113 protected $addableblocks = null;
116 * Will be an array region-name => array(db rows loaded in load_blocks);
119 protected $birecordsbyregion = null;
122 * array region-name => array(block objects); populated as necessary by
123 * the ensure_instances_exist method.
126 protected $blockinstances = array();
129 * array region-name => array(block_contents objects) what actually needs to
130 * be displayed in each region.
133 protected $visibleblockcontent = array();
136 * array region-name => array(block_contents objects) extra block-like things
137 * to be displayed in each region, before the real blocks.
140 protected $extracontent = array();
143 * Used by the block move id, to track whether a block is currently being moved.
145 * When you click on the move icon of a block, first the page needs to reload with
146 * extra UI for choosing a new position for a particular block. In that situation
147 * this field holds the id of the block being moved.
151 protected $movingblock = null;
154 * Show only fake blocks
156 protected $fakeblocksonly = false;
158 /// Constructor ================================================================
162 * @param object $page the moodle_page object object we are managing the blocks for,
163 * or a reasonable faxilimily. (See the comment at the top of this class
164 * and {@link http://en.wikipedia.org/wiki/Duck_typing})
166 public function __construct($page) {
170 /// Getter methods =============================================================
173 * Get an array of all region names on this page where a block may appear
175 * @return array the internal names of the regions on this page where block may appear.
177 public function get_regions() {
178 if (is_null($this->defaultregion)) {
179 $this->page->initialise_theme_and_output();
181 return array_keys($this->regions);
185 * Get the region name of the region blocks are added to by default
187 * @return string the internal names of the region where new blocks are added
188 * by default, and where any blocks from an unrecognised region are shown.
189 * (Imagine that blocks were added with one theme selected, then you switched
190 * to a theme with different block positions.)
192 public function get_default_region() {
193 $this->page->initialise_theme_and_output();
194 return $this->defaultregion;
198 * The list of block types that may be added to this page.
200 * @return array block name => record from block table.
202 public function get_addable_blocks() {
203 $this->check_is_loaded();
205 if (!is_null($this->addableblocks)) {
206 return $this->addableblocks;
210 $this->addableblocks = array();
212 $allblocks = blocks_get_record();
213 if (empty($allblocks)) {
214 return $this->addableblocks;
217 $pageformat = $this->page->pagetype;
218 foreach($allblocks as $block) {
219 if ($block->visible &&
220 (block_method_result($block->name, 'instance_allow_multiple') || !$this->is_block_present($block->name)) &&
221 blocks_name_allowed_in_format($block->name, $pageformat) &&
222 block_method_result($block->name, 'user_can_addto', $this->page)) {
223 $this->addableblocks[$block->name] = $block;
227 return $this->addableblocks;
231 * Given a block name, find out of any of them are currently present in the page
233 * @param string $blockname - the basic name of a block (eg "navigation")
234 * @return boolean - is there one of these blocks in the current page?
236 public function is_block_present($blockname) {
237 if (empty($this->blockinstances)) {
241 foreach ($this->blockinstances as $region) {
242 foreach ($region as $instance) {
243 if (empty($instance->instance->blockname)) {
246 if ($instance->instance->blockname == $blockname) {
255 * Find out if a block type is known by the system
257 * @param string $blockname the name of the type of block.
258 * @param boolean $includeinvisible if false (default) only check 'visible' blocks, that is, blocks enabled by the admin.
259 * @return boolean true if this block in installed.
261 public function is_known_block_type($blockname, $includeinvisible = false) {
262 $blocks = $this->get_installed_blocks();
263 foreach ($blocks as $block) {
264 if ($block->name == $blockname && ($includeinvisible || $block->visible)) {
272 * Find out if a region exists on a page
274 * @param string $region a region name
275 * @return boolean true if this region exists on this page.
277 public function is_known_region($region) {
278 return array_key_exists($region, $this->regions);
282 * Get an array of all blocks within a given region
284 * @param string $region a block region that exists on this page.
285 * @return array of block instances.
287 public function get_blocks_for_region($region) {
288 $this->check_is_loaded();
289 $this->ensure_instances_exist($region);
290 return $this->blockinstances[$region];
294 * Returns an array of block content objects that exist in a region
296 * @param string $region a block region that exists on this page.
297 * @return array of block block_contents objects for all the blocks in a region.
299 public function get_content_for_region($region, $output) {
300 $this->check_is_loaded();
301 $this->ensure_content_created($region, $output);
302 return $this->visibleblockcontent[$region];
306 * Helper method used by get_content_for_region.
307 * @param string $region region name
308 * @param float $weight weight. May be fractional, since you may want to move a block
309 * between ones with weight 2 and 3, say ($weight would be 2.5).
310 * @return string URL for moving block $this->movingblock to this position.
312 protected function get_move_target_url($region, $weight) {
313 return new moodle_url($this->page->url, array('bui_moveid' => $this->movingblock,
314 'bui_newregion' => $region, 'bui_newweight' => $weight, 'sesskey' => sesskey()));
318 * Determine whether a region contains anything. (Either any real blocks, or
319 * the add new block UI.)
321 * (You may wonder why the $output parameter is required. Unfortunately,
322 * because of the way that blocks work, the only reliable way to find out
323 * if a block will be visible is to get the content for output, and to
324 * get the content, you need a renderer. Fortunately, this is not a
325 * performance problem, because we cache the output that is generated, and
326 * in almost every case where we call region_has_content, we are about to
327 * output the blocks anyway, so we are not doing wasted effort.)
329 * @param string $region a block region that exists on this page.
330 * @param object $output a core_renderer. normally the global $OUTPUT.
331 * @return boolean Whether there is anything in this region.
333 public function region_has_content($region, $output) {
335 if (!$this->is_known_region($region)) {
338 $this->check_is_loaded();
339 $this->ensure_content_created($region, $output);
340 if ($this->page->user_is_editing() && $this->page->user_can_edit_blocks()) {
341 // If editing is on, we need all the block regions visible, for the
345 return !empty($this->visibleblockcontent[$region]) || !empty($this->extracontent[$region]);
349 * Get an array of all of the installed blocks.
351 * @return array contents of the block table.
353 public function get_installed_blocks() {
355 if (is_null($this->allblocks)) {
356 $this->allblocks = $DB->get_records('block');
358 return $this->allblocks;
361 /// Setter methods =============================================================
364 * Add a region to a page
366 * @param string $region add a named region where blocks may appear on the
367 * current page. This is an internal name, like 'side-pre', not a string to
370 public function add_region($region) {
371 $this->check_not_yet_loaded();
372 $this->regions[$region] = 1;
376 * Add an array of regions
379 * @param array $regions this utility method calls add_region for each array element.
381 public function add_regions($regions) {
382 foreach ($regions as $region) {
383 $this->add_region($region);
388 * Set the default region for new blocks on the page
390 * @param string $defaultregion the internal names of the region where new
391 * blocks should be added by default, and where any blocks from an
392 * unrecognised region are shown.
394 public function set_default_region($defaultregion) {
395 $this->check_not_yet_loaded();
396 if ($defaultregion) {
397 $this->check_region_is_known($defaultregion);
399 $this->defaultregion = $defaultregion;
403 * Add something that looks like a block, but which isn't an actual block_instance,
406 * @param block_contents $bc the content of the block like thing.
407 * @param string $region a block region that exists on this page.
409 public function add_pretend_block($bc, $region) {
410 $this->page->initialise_theme_and_output();
411 $this->check_region_is_known($region);
412 if (array_key_exists($region, $this->visibleblockcontent)) {
413 throw new coding_exception('block_manager has already prepared the blocks in region ' .
414 $region . 'for output. It is too late to add a pretend block.');
416 $this->extracontent[$region][] = $bc;
420 * Checks to see whether all of the blocks within the given region are docked
422 * @see region_uses_dock
423 * @param string $region
424 * @return bool True if all of the blocks within that region are docked
426 public function region_completely_docked($region, $output) {
427 if (!$this->page->theme->enable_dock) {
430 $this->check_is_loaded();
431 $this->ensure_content_created($region, $output);
432 foreach($this->visibleblockcontent[$region] as $instance) {
433 if (!empty($instance->content) && !get_user_preferences('docked_block_instance_'.$instance->blockinstanceid, 0)) {
441 * Checks to see whether any of the blocks within the given regions are docked
443 * @see region_completely_docked
444 * @param array|string $regions array of regions (or single region)
445 * @return bool True if any of the blocks within that region are docked
447 public function region_uses_dock($regions, $output) {
448 if (!$this->page->theme->enable_dock) {
451 $this->check_is_loaded();
452 foreach((array)$regions as $region) {
453 $this->ensure_content_created($region, $output);
454 foreach($this->visibleblockcontent[$region] as $instance) {
455 if(!empty($instance->content) && get_user_preferences('docked_block_instance_'.$instance->blockinstanceid, 0)) {
463 /// Actions ====================================================================
466 * This method actually loads the blocks for our page from the database.
468 * @param boolean|null $includeinvisible
469 * null (default) - load hidden blocks if $this->page->user_is_editing();
470 * true - load hidden blocks.
471 * false - don't load hidden blocks.
473 public function load_blocks($includeinvisible = null) {
476 if (!is_null($this->birecordsbyregion)) {
481 if ($CFG->version < 2009050619) {
482 // Upgrade/install not complete. Don't try too show any blocks.
483 $this->birecordsbyregion = array();
487 // Ensure we have been initialised.
488 if (is_null($this->defaultregion)) {
489 $this->page->initialise_theme_and_output();
490 // If there are still no block regions, then there are no blocks on this page.
491 if (empty($this->regions)) {
492 $this->birecordsbyregion = array();
497 // Check if we need to load normal blocks
498 if ($this->fakeblocksonly) {
499 $this->birecordsbyregion = $this->prepare_per_region_arrays();
503 if (is_null($includeinvisible)) {
504 $includeinvisible = $this->page->user_is_editing();
506 if ($includeinvisible) {
509 $visiblecheck = 'AND (bp.visible = 1 OR bp.visible IS NULL)';
512 $context = $this->page->context;
513 $contexttest = 'bi.parentcontextid = :contextid2';
514 $parentcontextparams = array();
515 $parentcontextids = get_parent_contexts($context);
516 if ($parentcontextids) {
517 list($parentcontexttest, $parentcontextparams) =
518 $DB->get_in_or_equal($parentcontextids, SQL_PARAMS_NAMED, 'parentcontext0000');
519 $contexttest = "($contexttest OR (bi.showinsubcontexts = 1 AND bi.parentcontextid $parentcontexttest))";
522 $pagetypepatterns = matching_page_type_patterns($this->page->pagetype);
523 list($pagetypepatterntest, $pagetypepatternparams) =
524 $DB->get_in_or_equal($pagetypepatterns, SQL_PARAMS_NAMED, 'pagetypepatterntest0000');
526 list($ccselect, $ccjoin) = context_instance_preload_sql('b.id', CONTEXT_BLOCK, 'ctx');
529 'subpage1' => $this->page->subpage,
530 'subpage2' => $this->page->subpage,
531 'contextid1' => $context->id,
532 'contextid2' => $context->id,
533 'pagetype' => $this->page->pagetype,
537 bp.id AS blockpositionid,
540 bi.showinsubcontexts,
545 COALESCE(bp.visible, 1) AS visible,
546 COALESCE(bp.region, bi.defaultregion) AS region,
547 COALESCE(bp.weight, bi.defaultweight) AS weight,
551 FROM {block_instances} bi
552 JOIN {block} b ON bi.blockname = b.name
553 LEFT JOIN {block_positions} bp ON bp.blockinstanceid = bi.id
554 AND bp.contextid = :contextid1
555 AND bp.pagetype = :pagetype
556 AND bp.subpage = :subpage1
561 AND bi.pagetypepattern $pagetypepatterntest
562 AND (bi.subpagepattern IS NULL OR bi.subpagepattern = :subpage2)
567 COALESCE(bp.region, bi.defaultregion),
568 COALESCE(bp.weight, bi.defaultweight),
570 $blockinstances = $DB->get_recordset_sql($sql, $params + $parentcontextparams + $pagetypepatternparams);
572 $this->birecordsbyregion = $this->prepare_per_region_arrays();
574 foreach ($blockinstances as $bi) {
575 context_instance_preload($bi);
576 if ($this->is_known_region($bi->region)) {
577 $this->birecordsbyregion[$bi->region][] = $bi;
583 // Pages don't necessarily have a defaultregion. The one time this can
584 // happen is when there are no theme block regions, but the script itself
585 // has a block region in the main content area.
586 if (!empty($this->defaultregion)) {
587 $this->birecordsbyregion[$this->defaultregion] =
588 array_merge($this->birecordsbyregion[$this->defaultregion], $unknown);
593 * Add a block to the current page, or related pages. The block is added to
594 * context $this->page->contextid. If $pagetypepattern $subpagepattern
596 * @param string $blockname The type of block to add.
597 * @param string $region the block region on this page to add the block to.
598 * @param integer $weight determines the order where this block appears in the region.
599 * @param boolean $showinsubcontexts whether this block appears in subcontexts, or just the current context.
600 * @param string|null $pagetypepattern which page types this block should appear on. Defaults to just the current page type.
601 * @param string|null $subpagepattern which subpage this block should appear on. NULL = any (the default), otherwise only the specified subpage.
603 public function add_block($blockname, $region, $weight, $showinsubcontexts, $pagetypepattern = NULL, $subpagepattern = NULL) {
605 // Allow invisible blocks because this is used when adding default page blocks, which
606 // might include invisible ones if the user makes some default blocks invisible
607 $this->check_known_block_type($blockname, true);
608 $this->check_region_is_known($region);
610 if (empty($pagetypepattern)) {
611 $pagetypepattern = $this->page->pagetype;
614 $blockinstance = new stdClass;
615 $blockinstance->blockname = $blockname;
616 $blockinstance->parentcontextid = $this->page->context->id;
617 $blockinstance->showinsubcontexts = !empty($showinsubcontexts);
618 $blockinstance->pagetypepattern = $pagetypepattern;
619 $blockinstance->subpagepattern = $subpagepattern;
620 $blockinstance->defaultregion = $region;
621 $blockinstance->defaultweight = $weight;
622 $blockinstance->configdata = '';
623 $blockinstance->id = $DB->insert_record('block_instances', $blockinstance);
625 // Ensure the block context is created.
626 get_context_instance(CONTEXT_BLOCK, $blockinstance->id);
628 // If the new instance was created, allow it to do additional setup
629 if ($block = block_instance($blockname, $blockinstance)) {
630 $block->instance_create();
634 public function add_block_at_end_of_default_region($blockname) {
635 $defaulregion = $this->get_default_region();
637 $lastcurrentblock = end($this->birecordsbyregion[$defaulregion]);
638 if ($lastcurrentblock) {
639 $weight = $lastcurrentblock->weight + 1;
644 if ($this->page->subpage) {
645 $subpage = $this->page->subpage;
650 // Special case. Course view page type include the course format, but we
651 // want to add the block non-format-specifically.
652 $pagetypepattern = $this->page->pagetype;
653 if (strpos($pagetypepattern, 'course-view') === 0) {
654 $pagetypepattern = 'course-view-*';
657 $this->add_block($blockname, $defaulregion, $weight, false, $pagetypepattern, $subpage);
661 * Convenience method, calls add_block repeatedly for all the blocks in $blocks.
663 * @param array $blocks array with array keys the region names, and values an array of block names.
664 * @param string $pagetypepattern optional. Passed to @see add_block()
665 * @param string $subpagepattern optional. Passed to @see add_block()
667 public function add_blocks($blocks, $pagetypepattern = NULL, $subpagepattern = NULL, $showinsubcontexts=false, $weight=0) {
668 $this->add_regions(array_keys($blocks));
669 foreach ($blocks as $region => $regionblocks) {
671 foreach ($regionblocks as $blockname) {
672 $this->add_block($blockname, $region, $weight, $showinsubcontexts, $pagetypepattern, $subpagepattern);
679 * Move a block to a new position on this page.
681 * If this block cannot appear on any other pages, then we change defaultposition/weight
682 * in the block_instances table. Otherwise we just set the position on this page.
684 * @param $blockinstanceid the block instance id.
685 * @param $newregion the new region name.
686 * @param $newweight the new weight.
688 public function reposition_block($blockinstanceid, $newregion, $newweight) {
691 $this->check_region_is_known($newregion);
692 $inst = $this->find_instance($blockinstanceid);
694 $bi = $inst->instance;
695 if ($bi->weight == $bi->defaultweight && $bi->region == $bi->defaultregion &&
696 !$bi->showinsubcontexts && strpos($bi->pagetypepattern, '*') === false &&
697 (!$this->page->subpage || $bi->subpagepattern)) {
699 // Set default position
700 $newbi = new stdClass;
701 $newbi->id = $bi->id;
702 $newbi->defaultregion = $newregion;
703 $newbi->defaultweight = $newweight;
704 $DB->update_record('block_instances', $newbi);
706 if ($bi->blockpositionid) {
708 $bp->id = $bi->blockpositionid;
709 $bp->region = $newregion;
710 $bp->weight = $newweight;
711 $DB->update_record('block_positions', $bp);
715 // Just set position on this page.
717 $bp->region = $newregion;
718 $bp->weight = $newweight;
720 if ($bi->blockpositionid) {
721 $bp->id = $bi->blockpositionid;
722 $DB->update_record('block_positions', $bp);
725 $bp->blockinstanceid = $bi->id;
726 $bp->contextid = $this->page->context->id;
727 $bp->pagetype = $this->page->pagetype;
728 if ($this->page->subpage) {
729 $bp->subpage = $this->page->subpage;
733 $bp->visible = $bi->visible;
734 $DB->insert_record('block_positions', $bp);
740 * Find a given block by its instance id
742 * @param integer $instanceid
745 public function find_instance($instanceid) {
746 foreach ($this->regions as $region => $notused) {
747 $this->ensure_instances_exist($region);
748 foreach($this->blockinstances[$region] as $instance) {
749 if ($instance->instance->id == $instanceid) {
754 throw new block_not_on_page_exception($instanceid, $this->page);
757 /// Inner workings =============================================================
760 * Check whether the page blocks have been loaded yet
762 * @return void Throws coding exception if already loaded
764 protected function check_not_yet_loaded() {
765 if (!is_null($this->birecordsbyregion)) {
766 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.');
771 * Check whether the page blocks have been loaded yet
773 * Nearly identical to the above function {@link check_not_yet_loaded()} except different message
775 * @return void Throws coding exception if already loaded
777 protected function check_is_loaded() {
778 if (is_null($this->birecordsbyregion)) {
779 throw new coding_exception('block_manager has not yet loaded the blocks, to it is too soon to request the information you asked for.');
784 * Check if a block type is known and usable
786 * @param string $blockname The block type name to search for
787 * @param bool $includeinvisible Include disabled block types in the initial pass
788 * @return void Coding Exception thrown if unknown or not enabled
790 protected function check_known_block_type($blockname, $includeinvisible = false) {
791 if (!$this->is_known_block_type($blockname, $includeinvisible)) {
792 if ($this->is_known_block_type($blockname, true)) {
793 throw new coding_exception('Unknown block type ' . $blockname);
795 throw new coding_exception('Block type ' . $blockname . ' has been disabled by the administrator.');
801 * Check if a region is known by its name
803 * @param string $region
804 * @return void Coding Exception thrown if the region is not known
806 protected function check_region_is_known($region) {
807 if (!$this->is_known_region($region)) {
808 throw new coding_exception('Trying to reference an unknown block region ' . $region);
813 * Returns an array of region names as keys and nested arrays for values
815 * @return array an array where the array keys are the region names, and the array
816 * values are empty arrays.
818 protected function prepare_per_region_arrays() {
820 foreach ($this->regions as $region => $notused) {
821 $result[$region] = array();
827 * Create a set of new block instance from a record array
829 * @param array $birecords An array of block instance records
830 * @return array An array of instantiated block_instance objects
832 protected function create_block_instances($birecords) {
834 foreach ($birecords as $record) {
835 if ($blockobject = block_instance($record->blockname, $record, $this->page)) {
836 $results[] = $blockobject;
843 * Create all the block instances for all the blocks that were loaded by
844 * load_blocks. This is used, for example, to ensure that all blocks get a
845 * chance to initialise themselves via the {@link block_base::specialize()}
846 * method, before any output is done.
848 public function create_all_block_instances() {
849 foreach ($this->get_regions() as $region) {
850 $this->ensure_instances_exist($region);
855 * Return an array of content objects from a set of block instances
857 * @param array $instances An array of block instances
858 * @param renderer_base The renderer to use.
859 * @param string $region the region name.
860 * @return array An array of block_content (and possibly block_move_target) objects.
862 protected function create_block_contents($instances, $output, $region) {
867 if ($this->movingblock) {
868 $first = reset($instances);
870 $lastweight = $first->instance->weight - 2;
873 $strmoveblockhere = get_string('moveblockhere', 'block');
876 foreach ($instances as $instance) {
877 $content = $instance->get_content_for_output($output);
878 if (empty($content)) {
882 if ($this->movingblock && $lastweight != $instance->instance->weight &&
883 $content->blockinstanceid != $this->movingblock && $lastblock != $this->movingblock) {
884 $results[] = new block_move_target($strmoveblockhere, $this->get_move_target_url($region, ($lastweight + $instance->instance->weight)/2));
887 if ($content->blockinstanceid == $this->movingblock) {
888 $content->add_class('beingmoved');
889 $content->annotation .= get_string('movingthisblockcancel', 'block',
890 html_writer::link($this->page->url, get_string('cancel')));
893 $results[] = $content;
894 $lastweight = $instance->instance->weight;
895 $lastblock = $instance->instance->id;
898 if ($this->movingblock && $lastblock != $this->movingblock) {
899 $results[] = new block_move_target($strmoveblockhere, $this->get_move_target_url($region, $lastweight + 1));
905 * Ensure block instances exist for a given region
907 * @param string $region Check for bi's with the instance with this name
909 protected function ensure_instances_exist($region) {
910 $this->check_region_is_known($region);
911 if (!array_key_exists($region, $this->blockinstances)) {
912 $this->blockinstances[$region] =
913 $this->create_block_instances($this->birecordsbyregion[$region]);
918 * Ensure that there is some content within the given region
920 * @param string $region The name of the region to check
922 protected function ensure_content_created($region, $output) {
923 $this->ensure_instances_exist($region);
924 if (!array_key_exists($region, $this->visibleblockcontent)) {
926 if (array_key_exists($region, $this->extracontent)) {
927 $contents = $this->extracontent[$region];
929 $contents = array_merge($contents, $this->create_block_contents($this->blockinstances[$region], $output, $region));
930 if ($region == $this->defaultregion) {
931 $addblockui = block_add_block_ui($this->page, $output);
933 $contents[] = $addblockui;
936 $this->visibleblockcontent[$region] = $contents;
940 /// Process actions from the URL ===============================================
943 * Get the appropriate list of editing icons for a block. This is used
944 * to set {@link block_contents::$controls} in {@link block_base::get_contents_for_output()}.
946 * @param $output The core_renderer to use when generating the output. (Need to get icon paths.)
947 * @return an array in the format for {@link block_contents::$controls}
949 public function edit_controls($block) {
952 if (!isset($CFG->undeletableblocktypes) || (!is_array($CFG->undeletableblocktypes) && !is_string($CFG->undeletableblocktypes))) {
953 $CFG->undeletableblocktypes = array('navigation','settings');
954 } else if (is_string($CFG->undeletableblocktypes)) {
955 $CFG->undeletableblocktypes = explode(',', $CFG->undeletableblocktypes);
959 $actionurl = $this->page->url->out(false, array('sesskey'=> sesskey()));
961 // Assign roles icon.
962 if (has_capability('moodle/role:assign', $block->context)) {
963 //TODO: please note it is sloppy to pass urls through page parameters!!
964 // it is shortened because some web servers (e.g. IIS by default) give
965 // a 'security' error if you try to pass a full URL as a GET parameter in another URL.
967 $return = $this->page->url->out(false);
968 $return = str_replace($CFG->wwwroot . '/', '', $return);
970 $controls[] = array('url' => $CFG->wwwroot . '/' . $CFG->admin .
971 '/roles/assign.php?contextid=' . $block->context->id . '&returnurl=' . urlencode($return),
972 'icon' => 'i/roles', 'caption' => get_string('assignroles', 'role'));
975 if ($this->page->user_can_edit_blocks()) {
977 if ($block->instance->visible) {
978 $controls[] = array('url' => $actionurl . '&bui_hideid=' . $block->instance->id,
979 'icon' => 't/hide', 'caption' => get_string('hide'));
981 $controls[] = array('url' => $actionurl . '&bui_showid=' . $block->instance->id,
982 'icon' => 't/show', 'caption' => get_string('show'));
986 if ($this->page->user_can_edit_blocks() || $block->user_can_edit()) {
987 // Edit config icon - always show - needed for positioning UI.
988 $controls[] = array('url' => $actionurl . '&bui_editid=' . $block->instance->id,
989 'icon' => 't/edit', 'caption' => get_string('configuration'));
992 if ($this->page->user_can_edit_blocks() && $block->user_can_edit() && $block->user_can_addto($this->page)) {
993 if (!in_array($block->instance->blockname, $CFG->undeletableblocktypes)) {
995 $controls[] = array('url' => $actionurl . '&bui_deleteid=' . $block->instance->id,
996 'icon' => 't/delete', 'caption' => get_string('delete'));
1000 if ($this->page->user_can_edit_blocks()) {
1002 $controls[] = array('url' => $actionurl . '&bui_moveid=' . $block->instance->id,
1003 'icon' => 't/move', 'caption' => get_string('move'));
1010 * Process any block actions that were specified in the URL.
1012 * This can only be done given a valid $page object.
1014 * @param moodle_page $page the page to add blocks to.
1015 * @return boolean true if anything was done. False if not.
1017 public function process_url_actions() {
1018 if (!$this->page->user_is_editing()) {
1021 return $this->process_url_add() || $this->process_url_delete() ||
1022 $this->process_url_show_hide() || $this->process_url_edit() ||
1023 $this->process_url_move();
1027 * Handle adding a block.
1028 * @return boolean true if anything was done. False if not.
1030 public function process_url_add() {
1031 $blocktype = optional_param('bui_addblock', null, PARAM_SAFEDIR);
1038 if (!$this->page->user_can_edit_blocks()) {
1039 throw new moodle_exception('nopermissions', '', $this->page->url->out(), get_string('addblock'));
1042 if (!array_key_exists($blocktype, $this->get_addable_blocks())) {
1043 throw new moodle_exception('cannotaddthisblocktype', '', $this->page->url->out(), $blocktype);
1046 $this->add_block_at_end_of_default_region($blocktype);
1048 // If the page URL was a guess, it will contain the bui_... param, so we must make sure it is not there.
1049 $this->page->ensure_param_not_in_url('bui_addblock');
1055 * Handle deleting a block.
1056 * @return boolean true if anything was done. False if not.
1058 public function process_url_delete() {
1059 $blockid = optional_param('bui_deleteid', null, PARAM_INTEGER);
1066 $block = $this->page->blocks->find_instance($blockid);
1068 if (!$block->user_can_edit() || !$this->page->user_can_edit_blocks() || !$block->user_can_addto($this->page)) {
1069 throw new moodle_exception('nopermissions', '', $this->page->url->out(), get_string('deleteablock'));
1072 blocks_delete_instance($block->instance);
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_deleteid');
1081 * Handle showing or hiding a block.
1082 * @return boolean true if anything was done. False if not.
1084 public function process_url_show_hide() {
1085 if ($blockid = optional_param('bui_hideid', null, PARAM_INTEGER)) {
1087 } else if ($blockid = optional_param('bui_showid', null, PARAM_INTEGER)) {
1095 $block = $this->page->blocks->find_instance($blockid);
1097 if (!$this->page->user_can_edit_blocks()) {
1098 throw new moodle_exception('nopermissions', '', $this->page->url->out(), get_string('hideshowblocks'));
1101 blocks_set_visibility($block->instance, $this->page, $newvisibility);
1103 // If the page URL was a guses, it will contain the bui_... param, so we must make sure it is not there.
1104 $this->page->ensure_param_not_in_url('bui_hideid');
1105 $this->page->ensure_param_not_in_url('bui_showid');
1111 * Handle showing/processing the submission from the block editing form.
1112 * @return boolean true if the form was submitted and the new config saved. Does not
1113 * return if the editing form was displayed. False otherwise.
1115 public function process_url_edit() {
1116 global $CFG, $DB, $PAGE;
1118 $blockid = optional_param('bui_editid', null, PARAM_INTEGER);
1124 require_once($CFG->dirroot . '/blocks/edit_form.php');
1126 $block = $this->find_instance($blockid);
1128 if (!$block->user_can_edit() && !$this->page->user_can_edit_blocks()) {
1129 throw new moodle_exception('nopermissions', '', $this->page->url->out(), get_string('editblock'));
1132 $editpage = new moodle_page();
1133 $editpage->set_pagelayout('admin');
1134 $editpage->set_course($this->page->course);
1135 $editpage->set_context($block->context);
1136 if ($this->page->cm) {
1137 $editpage->set_cm($this->page->cm);
1139 $editurlbase = str_replace($CFG->wwwroot . '/', '/', $this->page->url->out_omit_querystring());
1140 $editurlparams = $this->page->url->params();
1141 $editurlparams['bui_editid'] = $blockid;
1142 $editpage->set_url($editurlbase, $editurlparams);
1143 $editpage->set_block_actions_done();
1144 // At this point we are either going to redirect, or display the form, so
1145 // overwrite global $PAGE ready for this. (Formslib refers to it.)
1148 $formfile = $CFG->dirroot . '/blocks/' . $block->name() . '/edit_form.php';
1149 if (is_readable($formfile)) {
1150 require_once($formfile);
1151 $classname = 'block_' . $block->name() . '_edit_form';
1152 if (!class_exists($classname)) {
1153 $classname = 'block_edit_form';
1156 $classname = 'block_edit_form';
1159 $mform = new $classname($editpage->url, $block, $this->page);
1160 $mform->set_data($block->instance);
1162 if ($mform->is_cancelled()) {
1163 redirect($this->page->url);
1165 } else if ($data = $mform->get_data()) {
1167 $bi->id = $block->instance->id;
1168 $bi->pagetypepattern = $data->bui_pagetypepattern;
1169 if (empty($data->bui_subpagepattern) || $data->bui_subpagepattern == '%@NULL@%') {
1170 $bi->subpagepattern = null;
1172 $bi->subpagepattern = $data->bui_subpagepattern;
1175 $parentcontext = get_context_instance_by_id($data->bui_parentcontextid);
1176 $systemcontext = get_context_instance(CONTEXT_SYSTEM);
1178 // Updating stickiness and contexts. See MDL-21375 for details.
1179 if (has_capability('moodle/site:manageblocks', $parentcontext)) { // Check permissions in destination
1180 // Explicitly set the context
1181 $bi->parentcontextid = $parentcontext->id;
1183 // If the context type is > 0 then we'll explicitly set the block as sticky, otherwise not
1184 $bi->showinsubcontexts = (int)(!empty($data->bui_contexts));
1186 // If the block wants to be system-wide, then explicitly set that
1187 if ($data->bui_contexts == 2) { // Only possible on a frontpage or system page
1188 $bi->parentcontextid = $systemcontext->id;
1190 } else { // The block doesn't want to be system-wide, so let's ensure that
1191 if ($parentcontext->id == $systemcontext->id) { // We need to move it to the front page
1192 $frontpagecontext = get_context_instance(CONTEXT_COURSE, SITEID);
1193 $bi->parentcontextid = $frontpagecontext->id;
1194 $bi->pagetypepattern = '*'; // Just in case
1199 $bi->defaultregion = $data->bui_defaultregion;
1200 $bi->defaultweight = $data->bui_defaultweight;
1201 $DB->update_record('block_instances', $bi);
1203 if (!empty($block->config)) {
1204 $config = clone($block->config);
1206 $config = new stdClass;
1208 foreach ($data as $configfield => $value) {
1209 if (strpos($configfield, 'config_') !== 0) {
1212 $field = substr($configfield, 7);
1213 $config->$field = $value;
1215 $block->instance_config_save($config);
1218 $bp->visible = $data->bui_visible;
1219 $bp->region = $data->bui_region;
1220 $bp->weight = $data->bui_weight;
1221 $needbprecord = !$data->bui_visible || $data->bui_region != $data->bui_defaultregion ||
1222 $data->bui_weight != $data->bui_defaultweight;
1224 if ($block->instance->blockpositionid && !$needbprecord) {
1225 $DB->delete_records('block_positions', array('id' => $block->instance->blockpositionid));
1227 } else if ($block->instance->blockpositionid && $needbprecord) {
1228 $bp->id = $block->instance->blockpositionid;
1229 $DB->update_record('block_positions', $bp);
1231 } else if ($needbprecord) {
1232 $bp->blockinstanceid = $block->instance->id;
1233 $bp->contextid = $this->page->context->id;
1234 $bp->pagetype = $this->page->pagetype;
1235 if ($this->page->subpage) {
1236 $bp->subpage = $this->page->subpage;
1240 $DB->insert_record('block_positions', $bp);
1243 redirect($this->page->url);
1246 $strheading = get_string('blockconfiga', 'moodle', $block->get_title());
1247 $editpage->set_title($strheading);
1248 $editpage->set_heading($strheading);
1249 $editpage->navbar->add($strheading);
1250 $output = $editpage->get_renderer('core');
1251 echo $output->header();
1252 echo $output->heading($strheading, 2);
1254 echo $output->footer();
1260 * Handle showing/processing the submission from the block editing form.
1261 * @return boolean true if the form was submitted and the new config saved. Does not
1262 * return if the editing form was displayed. False otherwise.
1264 public function process_url_move() {
1265 global $CFG, $DB, $PAGE;
1267 $blockid = optional_param('bui_moveid', null, PARAM_INTEGER);
1274 $block = $this->find_instance($blockid);
1276 if (!$this->page->user_can_edit_blocks()) {
1277 throw new moodle_exception('nopermissions', '', $this->page->url->out(), get_string('editblock'));
1280 $newregion = optional_param('bui_newregion', '', PARAM_ALPHANUMEXT);
1281 $newweight = optional_param('bui_newweight', null, PARAM_FLOAT);
1282 if (!$newregion || is_null($newweight)) {
1283 // Don't have a valid target position yet, must be just starting the move.
1284 $this->movingblock = $blockid;
1285 $this->page->ensure_param_not_in_url('bui_moveid');
1289 if (!$this->is_known_region($newregion)) {
1290 throw new moodle_exception('unknownblockregion', '', $this->page->url, $newregion);
1293 // Move this block. This may involve moving other nearby blocks.
1294 $blocks = $this->birecordsbyregion[$newregion];
1296 $maxweight = self::MAX_WEIGHT;
1297 $minweight = -self::MAX_WEIGHT;
1299 // Initialise the used weights and spareweights array with the default values
1300 $spareweights = array();
1301 $usedweights = array();
1302 for ($i = $minweight; $i <= $maxweight; $i++) {
1303 $spareweights[$i] = $i;
1304 $usedweights[$i] = array();
1307 // Check each block and sort out where we have used weights
1308 foreach ($blocks as $bi) {
1309 if ($bi->weight > $maxweight) {
1310 // If this statement is true then the blocks weight is more than the
1311 // current maximum. To ensure that we can get the best block position
1312 // we will initialise elements within the usedweights and spareweights
1313 // arrays between the blocks weight (which will then be the new max) and
1315 $parseweight = $bi->weight;
1316 while (!array_key_exists($parseweight, $usedweights)) {
1317 $usedweights[$parseweight] = array();
1318 $spareweights[$parseweight] = $parseweight;
1321 $maxweight = $bi->weight;
1322 } else if ($bi->weight < $minweight) {
1323 // As above except this time the blocks weight is LESS than the
1324 // the current minimum, so we will initialise the array from the
1325 // blocks weight (new minimum) to the current minimum
1326 $parseweight = $bi->weight;
1327 while (!array_key_exists($parseweight, $usedweights)) {
1328 $usedweights[$parseweight] = array();
1329 $spareweights[$parseweight] = $parseweight;
1332 $minweight = $bi->weight;
1334 if ($bi->id != $block->instance->id) {
1335 unset($spareweights[$bi->weight]);
1336 $usedweights[$bi->weight][] = $bi->id;
1340 // First we find the nearest gap in the list of weights.
1341 $bestdistance = max(abs($newweight - self::MAX_WEIGHT), abs($newweight + self::MAX_WEIGHT)) + 1;
1343 foreach ($spareweights as $spareweight) {
1344 if (abs($newweight - $spareweight) < $bestdistance) {
1345 $bestdistance = abs($newweight - $spareweight);
1346 $bestgap = $spareweight;
1350 // If there is no gap, we have to go outside -self::MAX_WEIGHT .. self::MAX_WEIGHT.
1351 if (is_null($bestgap)) {
1352 $bestgap = self::MAX_WEIGHT + 1;
1353 while (!empty($usedweights[$bestgap])) {
1358 // Now we know the gap we are aiming for, so move all the blocks along.
1359 if ($bestgap < $newweight) {
1360 $newweight = floor($newweight);
1361 for ($weight = $bestgap + 1; $weight <= $newweight; $weight++) {
1362 foreach ($usedweights[$weight] as $biid) {
1363 $this->reposition_block($biid, $newregion, $weight - 1);
1366 $this->reposition_block($block->instance->id, $newregion, $newweight);
1368 $newweight = ceil($newweight);
1369 for ($weight = $bestgap - 1; $weight >= $newweight; $weight--) {
1370 foreach ($usedweights[$weight] as $biid) {
1371 $this->reposition_block($biid, $newregion, $weight + 1);
1374 $this->reposition_block($block->instance->id, $newregion, $newweight);
1377 $this->page->ensure_param_not_in_url('bui_moveid');
1378 $this->page->ensure_param_not_in_url('bui_newregion');
1379 $this->page->ensure_param_not_in_url('bui_newweight');
1384 * Turns the display of normal blocks either on or off.
1386 * @param bool $setting
1388 public function show_only_fake_blocks($setting = true) {
1389 $this->fakeblocksonly = $setting;
1393 /// Helper functions for working with block classes ============================
1396 * Call a class method (one that does not require a block instance) on a block class.
1398 * @param string $blockname the name of the block.
1399 * @param string $method the method name.
1400 * @param array $param parameters to pass to the method.
1401 * @return mixed whatever the method returns.
1403 function block_method_result($blockname, $method, $param = NULL) {
1404 if(!block_load_class($blockname)) {
1407 return call_user_func(array('block_'.$blockname, $method), $param);
1411 * Creates a new object of the specified block class.
1413 * @param string $blockname the name of the block.
1414 * @param $instance block_instances DB table row (optional).
1415 * @param moodle_page $page the page this block is appearing on.
1416 * @return block_base the requested block instance.
1418 function block_instance($blockname, $instance = NULL, $page = NULL) {
1419 if(!block_load_class($blockname)) {
1422 $classname = 'block_'.$blockname;
1423 $retval = new $classname;
1424 if($instance !== NULL) {
1425 if (is_null($page)) {
1429 $retval->_load_instance($instance, $page);
1435 * Load the block class for a particular type of block.
1437 * @param string $blockname the name of the block.
1438 * @return boolean success or failure.
1440 function block_load_class($blockname) {
1443 if(empty($blockname)) {
1447 $classname = 'block_'.$blockname;
1449 if(class_exists($classname)) {
1453 $blockpath = $CFG->dirroot.'/blocks/'.$blockname.'/block_'.$blockname.'.php';
1455 if (file_exists($blockpath)) {
1456 require_once($CFG->dirroot.'/blocks/moodleblock.class.php');
1457 include_once($blockpath);
1459 //debugging("$blockname code does not exist in $blockpath", DEBUG_DEVELOPER);
1463 return class_exists($classname);
1467 * Given a specific page type, return all the page type patterns that might
1470 * @param string $pagetype for example 'course-view-weeks' or 'mod-quiz-view'.
1471 * @return array an array of all the page type patterns that might match this page type.
1473 function matching_page_type_patterns($pagetype) {
1474 $patterns = array($pagetype);
1475 $bits = explode('-', $pagetype);
1476 if (count($bits) == 3 && $bits[0] == 'mod') {
1477 if ($bits[2] == 'view') {
1478 $patterns[] = 'mod-*-view';
1479 } else if ($bits[2] == 'index') {
1480 $patterns[] = 'mod-*-index';
1483 while (count($bits) > 0) {
1484 $patterns[] = implode('-', $bits) . '-*';
1491 /// Functions update the blocks if required by the request parameters ==========
1494 * Return a {@link block_contents} representing the add a new block UI, if
1495 * this user is allowed to see it.
1497 * @return block_contents an appropriate block_contents, or null if the user
1498 * cannot add any blocks here.
1500 function block_add_block_ui($page, $output) {
1501 global $CFG, $OUTPUT;
1502 if (!$page->user_is_editing() || !$page->user_can_edit_blocks()) {
1506 $bc = new block_contents();
1507 $bc->title = get_string('addblock');
1508 $bc->add_class('block_adminblock');
1510 $missingblocks = $page->blocks->get_addable_blocks();
1511 if (empty($missingblocks)) {
1512 $bc->content = get_string('noblockstoaddhere');
1517 foreach ($missingblocks as $block) {
1518 $blockobject = block_instance($block->name);
1519 if ($blockobject !== false && $blockobject->user_can_addto($page)) {
1520 $menu[$block->name] = $blockobject->get_title();
1523 textlib_get_instance()->asort($menu);
1525 $actionurl = new moodle_url($page->url, array('sesskey'=>sesskey()));
1526 $select = new single_select($actionurl, 'bui_addblock', $menu, null, array(''=>get_string('adddots')), 'add_block');
1527 $bc->content = $OUTPUT->render($select);
1531 // Functions that have been deprecated by block_manager =======================
1534 * @deprecated since Moodle 2.0 - use $page->blocks->get_addable_blocks();
1536 * This function returns an array with the IDs of any blocks that you can add to your page.
1537 * Parameters are passed by reference for speed; they are not modified at all.
1539 * @param $page the page object.
1540 * @param $blockmanager Not used.
1541 * @return array of block type ids.
1543 function blocks_get_missing(&$page, &$blockmanager) {
1544 debugging('blocks_get_missing is deprecated. Please use $page->blocks->get_addable_blocks() instead.', DEBUG_DEVELOPER);
1545 $blocks = $page->blocks->get_addable_blocks();
1547 foreach ($blocks as $block) {
1548 $ids[] = $block->id;
1554 * Actually delete from the database any blocks that are currently on this page,
1555 * but which should not be there according to blocks_name_allowed_in_format.
1557 * @todo Write/Fix this function. Currently returns immediately
1560 function blocks_remove_inappropriate($course) {
1563 $blockmanager = blocks_get_by_page($page);
1565 if (empty($blockmanager)) {
1569 if (($pageformat = $page->pagetype) == NULL) {
1573 foreach($blockmanager as $region) {
1574 foreach($region as $instance) {
1575 $block = blocks_get_record($instance->blockid);
1576 if(!blocks_name_allowed_in_format($block->name, $pageformat)) {
1577 blocks_delete_instance($instance->instance);
1584 * Check that a given name is in a permittable format
1586 * @param string $name
1587 * @param string $pageformat
1590 function blocks_name_allowed_in_format($name, $pageformat) {
1593 $formats = block_method_result($name, 'applicable_formats');
1597 foreach ($formats as $format => $allowed) {
1598 $formatregex = '/^'.str_replace('*', '[^-]*', $format).'.*$/';
1599 $depth = substr_count($format, '-');
1600 if (preg_match($formatregex, $pageformat) && $depth > $maxdepth) {
1605 if ($accept === NULL) {
1606 $accept = !empty($formats['all']);
1612 * Delete a block, and associated data.
1614 * @param object $instance a row from the block_instances table
1615 * @param bool $nolongerused legacy parameter. Not used, but kept for backwards compatibility.
1616 * @param bool $skipblockstables for internal use only. Makes @see blocks_delete_all_for_context() more efficient.
1618 function blocks_delete_instance($instance, $nolongerused = false, $skipblockstables = false) {
1621 if ($block = block_instance($instance->blockname, $instance)) {
1622 $block->instance_delete();
1624 delete_context(CONTEXT_BLOCK, $instance->id);
1626 if (!$skipblockstables) {
1627 $DB->delete_records('block_positions', array('blockinstanceid' => $instance->id));
1628 $DB->delete_records('block_instances', array('id' => $instance->id));
1633 * Delete all the blocks that belong to a particular context.
1635 * @param int $contextid the context id.
1637 function blocks_delete_all_for_context($contextid) {
1639 $instances = $DB->get_recordset('block_instances', array('parentcontextid' => $contextid));
1640 foreach ($instances as $instance) {
1641 blocks_delete_instance($instance, true);
1643 $instances->close();
1644 $DB->delete_records('block_instances', array('parentcontextid' => $contextid));
1645 $DB->delete_records('block_positions', array('contextid' => $contextid));
1649 * Set a block to be visible or hidden on a particular page.
1651 * @param object $instance a row from the block_instances, preferably LEFT JOINed with the
1652 * block_positions table as return by block_manager.
1653 * @param moodle_page $page the back to set the visibility with respect to.
1654 * @param integer $newvisibility 1 for visible, 0 for hidden.
1656 function blocks_set_visibility($instance, $page, $newvisibility) {
1658 if (!empty($instance->blockpositionid)) {
1659 // Already have local information on this page.
1660 $DB->set_field('block_positions', 'visible', $newvisibility, array('id' => $instance->blockpositionid));
1664 // Create a new block_positions record.
1666 $bp->blockinstanceid = $instance->id;
1667 $bp->contextid = $page->context->id;
1668 $bp->pagetype = $page->pagetype;
1669 if ($page->subpage) {
1670 $bp->subpage = $page->subpage;
1672 $bp->visible = $newvisibility;
1673 $bp->region = $instance->defaultregion;
1674 $bp->weight = $instance->defaultweight;
1675 $DB->insert_record('block_positions', $bp);
1679 * @deprecated since 2.0
1680 * Delete all the blocks from a particular page.
1682 * @param string $pagetype the page type.
1683 * @param integer $pageid the page id.
1684 * @return bool success or failure.
1686 function blocks_delete_all_on_page($pagetype, $pageid) {
1689 debugging('Call to deprecated function blocks_delete_all_on_page. ' .
1690 'This function cannot work any more. Doing nothing. ' .
1691 'Please update your code to use a block_manager method $PAGE->blocks->....', DEBUG_DEVELOPER);
1696 * Dispite what this function is called, it seems to be mostly used to populate
1697 * the default blocks when a new course (or whatever) is created.
1699 * @deprecated since 2.0
1701 * @param object $page the page to add default blocks to.
1702 * @return boolean success or failure.
1704 function blocks_repopulate_page($page) {
1707 debugging('Call to deprecated function blocks_repopulate_page. ' .
1708 'Use a more specific method like blocks_add_default_course_blocks, ' .
1709 'or just call $PAGE->blocks->add_blocks()', DEBUG_DEVELOPER);
1711 /// If the site override has been defined, it is the only valid one.
1712 if (!empty($CFG->defaultblocks_override)) {
1713 $blocknames = $CFG->defaultblocks_override;
1715 $blocknames = $page->blocks_get_default();
1718 $blocks = blocks_parse_default_blocks_list($blocknames);
1719 $page->blocks->add_blocks($blocks);
1725 * Get the block record for a particular blockid - that is, a particular type os block.
1727 * @param $int blockid block type id. If null, an array of all block types is returned.
1728 * @param bool $notusedanymore No longer used.
1729 * @return array|object row from block table, or all rows.
1731 function blocks_get_record($blockid = NULL, $notusedanymore = false) {
1733 $blocks = $PAGE->blocks->get_installed_blocks();
1734 if ($blockid === NULL) {
1736 } else if (isset($blocks[$blockid])) {
1737 return $blocks[$blockid];
1744 * Find a given block by its blockid within a provide array
1746 * @param int $blockid
1747 * @param array $blocksarray
1748 * @return bool|object Instance if found else false
1750 function blocks_find_block($blockid, $blocksarray) {
1751 if (empty($blocksarray)) {
1754 foreach($blocksarray as $blockgroup) {
1755 if (empty($blockgroup)) {
1758 foreach($blockgroup as $instance) {
1759 if($instance->blockid == $blockid) {
1767 // Functions for programatically adding default blocks to pages ================
1770 * Parse a list of default blocks. See config-dist for a description of the format.
1772 * @param string $blocksstr
1775 function blocks_parse_default_blocks_list($blocksstr) {
1777 $bits = explode(':', $blocksstr);
1778 if (!empty($bits)) {
1779 $leftbits = trim(array_shift($bits));
1780 if ($leftbits != '') {
1781 $blocks[BLOCK_POS_LEFT] = explode(',', $leftbits);
1784 if (!empty($bits)) {
1785 $rightbits =trim(array_shift($bits));
1786 if ($rightbits != '') {
1787 $blocks[BLOCK_POS_RIGHT] = explode(',', $rightbits);
1794 * @return array the blocks that should be added to the site course by default.
1796 function blocks_get_default_site_course_blocks() {
1799 if (!empty($CFG->defaultblocks_site)) {
1800 return blocks_parse_default_blocks_list($CFG->defaultblocks_site);
1803 BLOCK_POS_LEFT => array('site_main_menu'),
1804 BLOCK_POS_RIGHT => array('course_summary', 'calendar_month')
1810 * Add the default blocks to a course.
1812 * @param object $course a course object.
1814 function blocks_add_default_course_blocks($course) {
1817 if (!empty($CFG->defaultblocks_override)) {
1818 $blocknames = blocks_parse_default_blocks_list($CFG->defaultblocks_override);
1820 } else if ($course->id == SITEID) {
1821 $blocknames = blocks_get_default_site_course_blocks();
1824 $defaultblocks = 'defaultblocks_' . $course->format;
1825 if (!empty($CFG->$defaultblocks)) {
1826 $blocknames = blocks_parse_default_blocks_list($CFG->$defaultblocks);
1829 $formatconfig = $CFG->dirroot.'/course/format/'.$course->format.'/config.php';
1830 if (is_readable($formatconfig)) {
1831 require($formatconfig);
1833 if (!empty($format['defaultblocks'])) {
1834 $blocknames = blocks_parse_default_blocks_list($format['defaultblocks']);
1836 } else if (!empty($CFG->defaultblocks)){
1837 $blocknames = blocks_parse_default_blocks_list($CFG->defaultblocks);
1840 $blocknames = array(
1841 BLOCK_POS_LEFT => array(),
1842 BLOCK_POS_RIGHT => array('search_forums', 'news_items', 'calendar_upcoming', 'recent_activity')
1848 if ($course->id == SITEID) {
1849 $pagetypepattern = 'site-index';
1851 $pagetypepattern = 'course-view-*';
1853 $page = new moodle_page();
1854 $page->set_course($course);
1855 $page->blocks->add_blocks($blocknames, $pagetypepattern);
1859 * Add the default system-context blocks. E.g. the admin tree.
1861 function blocks_add_default_system_blocks() {
1864 $page = new moodle_page();
1865 $page->set_context(get_context_instance(CONTEXT_SYSTEM));
1866 $page->blocks->add_blocks(array(BLOCK_POS_LEFT => array('navigation', 'settings')), '*', null, true);
1867 $page->blocks->add_blocks(array(BLOCK_POS_LEFT => array('admin_bookmarks')), 'admin-*', null, null, 2);
1869 if ($defaultmypage = $DB->get_record('my_pages', array('userid'=>null, 'name'=>'__default', 'private'=>1))) {
1870 $subpagepattern = $defaultmypage->id;
1872 $subpagepattern = null;
1875 $page->blocks->add_blocks(array(BLOCK_POS_RIGHT => array('private_files', 'online_users'), 'content' => array('course_overview')), 'my-index', $subpagepattern, false);