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