blocklib: MDL-19010 add block and get_blocks methods
[moodle.git] / lib / blocklib.php
CommitLineData
dcb1bd3c 1<?php //$Id$
0f3fe4b6 2
e8c8cee9 3///////////////////////////////////////////////////////////////////////////
4// //
5// NOTICE OF COPYRIGHT //
6// //
7// Moodle - Modular Object-Oriented Dynamic Learning Environment //
8// http://moodle.org //
9// //
10// Copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com //
11// //
12// This program is free software; you can redistribute it and/or modify //
13// it under the terms of the GNU General Public License as published by //
14// the Free Software Foundation; either version 2 of the License, or //
15// (at your option) any later version. //
16// //
17// This program is distributed in the hope that it will be useful, //
18// but WITHOUT ANY WARRANTY; without even the implied warranty of //
19// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
20// GNU General Public License for more details: //
21// //
22// http://www.gnu.org/copyleft/gpl.html //
23// //
24///////////////////////////////////////////////////////////////////////////
25
26/**
27 * This library includes all the necessary stuff to use blocks on pages in Moodle.
28 *
29 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
30 * @package pages
31 */
0f3fe4b6 32
0f3fe4b6 33define('BLOCK_MOVE_LEFT', 0x01);
34define('BLOCK_MOVE_RIGHT', 0x02);
35define('BLOCK_MOVE_UP', 0x04);
36define('BLOCK_MOVE_DOWN', 0x08);
9b4b78fd 37define('BLOCK_CONFIGURE', 0x10);
0f3fe4b6 38
9b4b78fd 39define('BLOCK_POS_LEFT', 'l');
40define('BLOCK_POS_RIGHT', 'r');
0e9af917 41
ee6055eb 42define('BLOCKS_PINNED_TRUE',0);
43define('BLOCKS_PINNED_FALSE',1);
44define('BLOCKS_PINNED_BOTH',2);
45
f032aa7a 46require_once($CFG->libdir.'/pagelib.php');
61abc65b 47
86b5ea0f 48/**
49 * This class keeps track of the block that should appear on a moodle_page.
50 * The page to work with as passed to the constructor.
51 * The only fields of moodle_page that is uses are ->context, ->pagetype and
52 * ->subpage, so instead of passing a full moodle_page object, you may also
53 * pass a stdClass object with those three fields. These field values are read
54 * only at the point that the load_blocks() method is called. It is the caller's
55 * responsibility to ensure that those fields do not subsequently change.
56 */
57class block_manager {
86b5ea0f 58
59/// Field declarations =========================================================
60
86b5ea0f 61 protected $page;
62
63 protected $regions = array();
64
65 protected $defaultregion;
66
08eab897 67 protected $allblocks = null; // Will be get_records('blocks');
68
69 protected $addableblocks = null; // Will be a subset of $allblocks.
70
71 protected $blocksbyregion = null; // Will be an array region-name => array(block_instances);
72
86b5ea0f 73/// Constructor ================================================================
74
75 /**
76 * Constructor.
77 * @param object $page the moodle_page object object we are managing the blocks for,
78 * or a reasonable faxilimily. (See the comment at the top of this classe
79 * and http://en.wikipedia.org/wiki/Duck_typing)
80 */
81 public function __construct($page) {
82 $this->page = $page;
83 }
84
85/// Getter methods =============================================================
86
87 /**
88 * @return array the internal names of the regions on this page where block may appear.
89 */
90 public function get_regions() {
91 return array_keys($this->regions);
92 }
93
94 /**
95 * @return string the internal names of the region where new blocks are added
96 * by default, and where any blocks from an unrecognised region are shown.
97 * (Imagine that blocks were added with one theme selected, then you switched
98 * to a theme with different block positions.)
99 */
100 public function get_default_region() {
101 return $this->defaultregion;
102 }
103
08eab897 104 /**
105 * The list of block types that may be added to this page.
106 * @return array block id => record from block table.
107 */
108 public function get_addable_blocks() {
109 $this->check_is_loaded();
110
111 if (!is_null($this->addableblocks)) {
112 return $this->addableblocks;
113 }
114
115 // Lazy load.
116 $this->addableblocks = array();
117
118 $allblocks = blocks_get_record();
119 if (empty($allblocks)) {
120 return $this->addableblocks;
121 }
122
123 $pageformat = $page->pagetype;
124 foreach($allblocks as $block) {
125 if ($block->visible &&
126 ($block->multiple || !$this->is_block_present($block->id)) &&
127 blocks_name_allowed_in_format($block->name, $pageformat)) {
128 $this->addableblocks[$block->id] = $block;
129 }
130 }
131
132 return $this->addableblocks;
133 }
134
135 public function is_block_present($blocktypeid) {
136 // TODO
137 }
138
139 /**
140 * @param string $blockname the name of ta type of block.
141 * @param boolean $includeinvisible if false (default) only check 'visible' blocks, that is, blocks enabled by the admin.
142 * @return boolean true if this block in installed.
143 */
144 public function is_known_block_type($blockname, $includeinvisible = false) {
145 $blocks = $this->get_installed_blocks();
146 foreach ($blocks as $block) {
147 if ($block->name == $blockname && ($includeinvisible || $block->visible)) {
148 return true;
149 }
150 }
151 return false;
152 }
153
154 /**
155 * @param string $region a region name
156 * @return boolean true if this retion exists on this page.
157 */
158 public function is_known_region($region) {
159 return array_key_exists($region, $this->regions);
160 }
161
162 /**
163 * @param $region a block region that exists on this page.
164 * @return array of block instances.
165 */
166 public function get_blocks_for_region($region) {
167 $this->check_is_loaded();
168 $this->check_region_is_known($region);
169 return $this->blocksbyregion[$region];
170 }
171
172 /**
173 * Get the list of all installed blocks.
174 * @return array contents of the block table.
175 */
176 public function get_installed_blocks() {
177 global $DB;
178 if (is_null($this->allblocks)) {
179 $this->allblocks = $DB->get_records('block');
180 }
181 return $this->allblocks;
182 }
183
86b5ea0f 184/// Setter methods =============================================================
185
186 /**
187 * @param string $region add a named region where blocks may appear on the
188 * current page. This is an internal name, like 'side-pre', not a string to
189 * display in the UI.
190 */
191 public function add_region($region) {
192 $this->check_not_yet_loaded();
193 $this->regions[$region] = 1;
194 }
195
196 /**
197 * @param array $regions this utility method calls add_region for each array element.
198 */
199 public function add_regions($regions) {
200 foreach ($regions as $region) {
201 $this->add_region($region);
202 }
203 }
204
205 /**
206 * @param string $defaultregion the internal names of the region where new
207 * blocks should be added by default, and where any blocks from an
208 * unrecognised region are shown.
209 */
210 public function set_default_region($defaultregion) {
211 $this->check_not_yet_loaded();
08eab897 212 $this->check_region_is_known($defaultregion);
86b5ea0f 213 $this->defaultregion = $defaultregion;
214 }
215
08eab897 216/// Actions ====================================================================
217
218 /**
219 * This method actually loads the blocks for our page from the database.
220 */
221 public function load_blocks($includeinvisible = NULL) {
222 global $DB;
223 $this->check_not_yet_loaded();
224
225 if (is_null($includeinvisible)) {
226 $includeinvisible = $this->page->user_is_editing();
227 }
228 if ($includeinvisible) {
229 $visiblecheck = 'AND (bp.visible = 1 OR bp.visible IS NULL)';
230 } else {
231 $visiblecheck = '';
232 }
233
234 $context = $this->page->context;
235 $contexttest = 'bi.contextid = :contextid2';
236 $parentcontextparams = array();
237 $parentcontextids = get_parent_contexts($context);
238 if ($parentcontextids) {
239 list($parentcontexttest, $parentcontextparams) =
240 $DB->get_in_or_equal($parentcontextids, SQL_PARAMS_NAMED, 'parentcontext0000');
241 $contexttest = "($contexttest OR (bi.showinsubcontexts = 1 AND bi.contextid $parentcontexttest))";
242 }
243
244 $pagetypepatterns = $this->matching_page_type_patterns($this->page->pagetype);
245 list($pagetypepatterntest, $pagetypepatternparams) =
246 $DB->get_in_or_equal($pagetypepatterns, SQL_PARAMS_NAMED, 'pagetypepatterntest0000');
247
248 $params = array(
249 'subpage1' => $this->page->subpage,
250 'subpage2' => $this->page->subpage,
251 'contextid1' => $context->id,
252 'contextid2' => $context->id,
253 'pagetype' => $this->page->pagetype,
254 );
255 $sql = "SELECT
256 bi.id,
257 bi.blockname,
258 bi.contextid,
259 bi.showinsubcontexts,
260 bi.pagetypepattern,
261 bi.subpagepattern,
262 bp.visible,
263 COALESCE(bp.region, bi.defaultregion) AS region,
264 COALESCE(bp.weight, bi.defaultweight) AS weight,
265 bi.configdata
266
267 FROM {block_instances} bi
268 JOIN {block} b ON bi.blockname = b.name
269 LEFT JOIN {block_positions} bp ON bp.blockinstanceid = bi.id
270 AND bp.contextid = :contextid1
271 AND bp.pagetype = :pagetype
272 AND bp.subpage = :subpage1
273
274 WHERE
275 $contexttest
276 AND bi.pagetypepattern $pagetypepatterntest
277 AND (bi.subpagepattern IS NULL OR bi.subpagepattern = :subpage2)
278 $visiblecheck
279 AND b.visible = 1
280
281 ORDER BY
282 COALESCE(bp.region, bi.defaultregion),
283 COALESCE(bp.weight, bi.defaultweight),
284 bi.id";
285 $blockinstances = $DB->get_recordset_sql($sql, $params + $parentcontextparams + $pagetypepatternparams);
286
287 $this->blocksbyregion = array();
288 foreach ($this->regions as $region => $notused) {
289 $this->blocksbyregion[$region] = array();
290 }
291 $unknown = array();
292
293 foreach ($blockinstances as $bi) {
294 if ($this->is_known_region($bi->region)) {
295 $this->blocksbyregion[$bi->region][] = $bi;
296 } else {
297 $unknown[] = $bi;
298 }
299 }
300 $this->blocksbyregion[$this->defaultregion] = array_merge($this->blocksbyregion[$this->defaultregion], $unknown);
301 }
302
303 /**
304 * Add a block to the current page, or related pages. The block is added to
305 * context $this->page->contextid. If $pagetypepattern $subpagepattern
306 * @param string $blockname The type of block to add.
307 * @param string $region the block region on this page to add the block to.
308 * @param integer $weight determines the order where this block appears in the region.
309 * @param boolean $showinsubcontexts whether this block appears in subcontexts, or just the current context.
310 * @param string|null $pagetypepattern which page types this block should appear on. Defaults to just the current page type.
311 * @param string|null $subpagepattern which subpage this block should appear on. NULL = any (the default), otherwise only the specified subpage.
312 */
313 public function add_block($blockname, $region, $weight, $showinsubcontexts, $pagetypepattern = NULL, $subpagepattern = NULL) {
314 global $DB;
315 $this->check_known_block_type($blockname);
316 $this->check_region_is_known($region);
317
318 if (empty($pagetypepattern)) {
319 $pagetypepattern = $this->page->pagetype;
320 }
321
322 $blockinstance = new stdClass;
323 $blockinstance->blockname = $blockname;
324 $blockinstance->contextid = $this->page->context->id;
325 $blockinstance->showinsubcontexts = !empty($showinsubcontexts);
326 $blockinstance->pagetypepattern = $pagetypepattern;
327 $blockinstance->subpagepattern = $subpagepattern;
328 $blockinstance->defaultregion = $region;
329 $blockinstance->defaultweight = $weight;
330 $blockinstance->configdata = '';
331 $DB->insert_record('block_instances', $blockinstance);
332 }
333
86b5ea0f 334/// Inner workings =============================================================
335
08eab897 336 /**
337 * Given a specific page type, return all the page type patterns that might
338 * match it.
339 * @param string $pagetype for example 'course-view-weeks' or 'mod-quiz-view'.
340 * @return array an array of all the page type patterns that might match this page type.
341 */
342 protected function matching_page_type_patterns($pagetype) {
343 $patterns = array($pagetype, '*');
344 $bits = explode('-', $pagetype);
345 if (count($bits) == 3 && $bits[0] == 'mod') {
346 if ($bits[2] == 'view') {
347 $patterns[] = 'mod-*-view';
348 } else if ($bits[2] == 'index') {
349 $patterns[] = 'mod-*-index';
350 }
351 }
352 while (count($bits) > 0) {
353 $patterns[] = implode('-', $bits) . '-*';
354 array_pop($bits);
355 }
356 return $patterns;
357 }
358
86b5ea0f 359 protected function check_not_yet_loaded() {
08eab897 360 if (!is_null($this->blocksbyregion)) {
86b5ea0f 361 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.');
362 }
363 }
364
08eab897 365 protected function check_is_loaded() {
366 if (is_null($this->blocksbyregion)) {
367 throw new coding_exception('block_manager has not yet loaded the blocks, to it is too soon to request the information you asked for.');
368 }
369 }
370
371 protected function check_known_block_type($blockname, $includeinvisible = false) {
372 if (!$this->is_known_block_type($blockname, $includeinvisible)) {
373 if ($this->is_known_block_type($blockname, true)) {
374 throw new coding_exception('Unknown block type ' . $blockname);
375 } else {
376 throw new coding_exception('Block type ' . $blockname . ' has been disabled by the administrator.');
377 }
378 }
379 }
380
381 protected function check_region_is_known($region) {
382 if (!$this->is_known_region($region)) {
383 throw new coding_exception('Trying to reference an unknown block region ' . $region);
384 }
86b5ea0f 385 }
386}
387
08eab897 388/// Helper functions for working with block classes ============================
389
390/**
391 * Call a class method (one that does not requrie a block instance) on a block class.
392 * @param string $blockname the name of the block.
393 * @param string $method the method name.
394 * @param array $param parameters to pass to the method.
395 * @return mixed whatever the method returns.
396 */
11306331 397function block_method_result($blockname, $method, $param = NULL) {
0f3fe4b6 398 if(!block_load_class($blockname)) {
399 return NULL;
400 }
11306331 401 return call_user_func(array('block_'.$blockname, $method), $param);
0f3fe4b6 402}
403
08eab897 404/**
405 * Creates a new object of the specified block class.
406 * @param string $blockname the name of the block.
407 * @param $instance block_instances DB table row (optional).
408 * @return block_base the requested block instance.
409 */
9b4b78fd 410function block_instance($blockname, $instance = NULL) {
0f3fe4b6 411 if(!block_load_class($blockname)) {
412 return false;
413 }
e89d741a 414 $classname = 'block_'.$blockname;
f032aa7a 415 $retval = new $classname;
9b4b78fd 416 if($instance !== NULL) {
1345403a 417 $retval->_load_instance($instance);
9b4b78fd 418 }
419 return $retval;
0f3fe4b6 420}
421
08eab897 422/**
423 * Load the block class for a particular type of block.
424 * @param string $blockname the name of the block.
425 * @return boolean success or failure.
426 */
0f3fe4b6 427function block_load_class($blockname) {
428 global $CFG;
429
a9033ad5 430 if(empty($blockname)) {
c7a9e293 431 return false;
432 }
433
e89d741a 434 $classname = 'block_'.$blockname;
a9033ad5 435
436 if(class_exists($classname)) {
437 return true;
438 }
439
440 require_once($CFG->dirroot.'/blocks/moodleblock.class.php');
e9a20759 441 @include_once($CFG->dirroot.'/blocks/'.$blockname.'/block_'.$blockname.'.php'); // do not throw errors if block code not present
0f3fe4b6 442
0f3fe4b6 443 return class_exists($classname);
444}
445
08eab897 446/// Functions that have been deprecated by block_manager =======================
f032aa7a 447
08eab897 448/**
449 * @deprecated since Moodle 2.0 - use $page->blocks->get
450 * This function returns an array with the IDs of any blocks that you can add to your page.
451 * Parameters are passed by reference for speed; they are not modified at all.
452 * @param $page the page object.
453 * @param $pageblocks Not used.
454 * @return array of block type ids.
455 */
456function blocks_get_missing(&$page, &$pageblocks) {
457 return array_keys($page->blocks->get_addable_blocks());
f032aa7a 458}
459
460function blocks_remove_inappropriate($page) {
461 $pageblocks = blocks_get_by_page($page);
462
463 if(empty($pageblocks)) {
464 return;
465 }
466
d529807a 467 if(($pageformat = $page->pagetype) == NULL) {
f032aa7a 468 return;
469 }
470
471 foreach($pageblocks as $position) {
472 foreach($position as $instance) {
473 $block = blocks_get_record($instance->blockid);
5bbbe0be 474 if(!blocks_name_allowed_in_format($block->name, $pageformat)) {
8a47e075 475 blocks_delete_instance($instance);
f032aa7a 476 }
477 }
478 }
479}
480
5bbbe0be 481function blocks_name_allowed_in_format($name, $pageformat) {
cd2bc3c9 482 $accept = NULL;
483 $maxdepth = -1;
484 $formats = block_method_result($name, 'applicable_formats');
485 if (!$formats) {
486 $formats = array();
487 }
488 foreach ($formats as $format => $allowed) {
489 $formatregex = '/^'.str_replace('*', '[^-]*', $format).'.*$/';
490 $depth = substr_count($format, '-');
491 if (preg_match($formatregex, $pageformat) && $depth > $maxdepth) {
492 $maxdepth = $depth;
493 $accept = $allowed;
5bbbe0be 494 }
495 }
cd2bc3c9 496 if ($accept === NULL) {
5bbbe0be 497 $accept = !empty($formats['all']);
498 }
499 return $accept;
500}
501
0d6b9d4f 502function blocks_delete_instance($instance,$pinned=false) {
58ff964f 503 global $DB;
f032aa7a 504
e9a20759 505 // Get the block object and call instance_delete() if possible
7fa944ca 506 if($record = blocks_get_record($instance->blockid)) {
507 if($obj = block_instance($record->name, $instance)) {
e9a20759 508 // Return value ignored
509 $obj->instance_delete();
510 }
b33dd23a 511 }
512
0d6b9d4f 513 if (!empty($pinned)) {
66b10689 514 $DB->delete_records('block_pinned_old', array('id'=>$instance->id));
0d6b9d4f 515 // And now, decrement the weight of all blocks after this one
66b10689 516 $sql = "UPDATE {block_pinned_old}
58ff964f 517 SET weight = weight - 1
518 WHERE pagetype = ? AND position = ? AND weight > ?";
519 $params = array($instance->pagetype, $instance->position, $instance->weight);
520 $DB->execute($sql, $params);
0d6b9d4f 521 } else {
522 // Now kill the db record;
66b10689 523 $DB->delete_records('block_instance_old', array('oldid'=>$instance->id));
19f5b2db 524 delete_context(CONTEXT_BLOCK, $instance->id);
0d6b9d4f 525 // And now, decrement the weight of all blocks after this one
66b10689 526 $sql = "UPDATE {block_instance_old}
58ff964f 527 SET weight = weight - 1
528 WHERE pagetype = ? AND pageid = ?
529 AND position = ? AND weight > ?";
530 $params = array($instance->pagetype, $instance->pageid, $instance->position, $instance->weight);
531 $DB->execute($sql, $params);
0d6b9d4f 532 }
2e477369 533 return true;
f032aa7a 534}
535
c8e0b579 536// Accepts an array of block instances and checks to see if any of them have content to display
537// (causing them to calculate their content in the process). Returns true or false. Parameter passed
538// by reference for speed; the array is actually not modified.
dffd4bb9 539function blocks_have_content(&$pageblocks, $position) {
0d6b9d4f 540
541 if (empty($pageblocks) || !is_array($pageblocks) || !array_key_exists($position,$pageblocks)) {
542 return false;
543 }
66c7e47b 544 // use a for() loop to get references to the array elements
545 // foreach() cannot fetch references in PHP v4.x
546 for ($n=0; $n<count($pageblocks[$position]);$n++) {
547 $instance = &$pageblocks[$position][$n];
6b726eb4 548 if (empty($instance->visible)) {
7933cc6b 549 continue;
550 }
3ef642d9 551 if(!$record = blocks_get_record($instance->blockid)) {
7933cc6b 552 continue;
553 }
3ef642d9 554 if(!$obj = block_instance($record->name, $instance)) {
9b4b78fd 555 continue;
556 }
3ef642d9 557 if(!$obj->is_empty()) {
afd1ec02 558 // cache rec and obj
66c7e47b 559 // for blocks_print_group()
b1a308be 560 $instance->rec = $record;
afd1ec02 561 $instance->obj = $obj;
3ef642d9 562 return true;
0f3fe4b6 563 }
564 }
9b4b78fd 565
0f3fe4b6 566 return false;
567}
568
c8e0b579 569// This function prints one group of blocks in a page
570// Parameters passed by reference for speed; they are not modified.
66492322 571function blocks_print_group(&$page, &$pageblocks, $position) {
6ad71d66 572 global $COURSE, $CFG, $USER;
66492322 573
6b726eb4 574 if (empty($pageblocks[$position])) {
575 $groupblocks = array();
b35fc182 576 $maxweight = 0;
6b726eb4 577 } else {
578 $groupblocks = $pageblocks[$position];
579 $maxweight = max(array_keys($groupblocks));
9b4b78fd 580 }
3cdc4597 581
6b726eb4 582
583 foreach ($groupblocks as $instance) {
4374ee2c 584 if (!empty($instance->pinned)) {
585 $maxweight--;
586 }
587 }
0f3fe4b6 588
f032aa7a 589 $isediting = $page->user_is_editing();
6b726eb4 590
591
592 foreach($groupblocks as $instance) {
593
afd1ec02 594
66c7e47b 595 // $instance may have ->rec and ->obj
596 // cached from when we walked $pageblocks
597 // in blocks_have_content()
598 if (empty($instance->rec)) {
6b726eb4 599 if (empty($instance->blockid)) {
600 continue; // Can't do anything
601 }
66c7e47b 602 $block = blocks_get_record($instance->blockid);
603 } else {
604 $block = $instance->rec;
605 }
f809df70 606
607 if (empty($block)) {
608 // Block doesn't exist! We should delete this instance!
609 continue;
610 }
611
6b726eb4 612 if (empty($block->visible)) {
9b4b78fd 613 // Disabled by the admin
614 continue;
615 }
afd1ec02 616
66c7e47b 617 if (empty($instance->obj)) {
618 if (!$obj = block_instance($block->name, $instance)) {
619 // Invalid block
620 continue;
621 }
622 } else {
623 $obj = $instance->obj;
ab0e4dd4 624 }
0f3fe4b6 625
d7f688d4 626 $editalways = false;
0d6b9d4f 627
6b726eb4 628
0d6b9d4f 629 if (($isediting && empty($instance->pinned)) || !empty($editalways)) {
9b4b78fd 630 $options = 0;
f032aa7a 631 // The block can be moved up if it's NOT the first one in its position. If it is, we look at the OR clause:
632 // the first block might still be able to move up if the page says so (i.e., it will change position)
633 $options |= BLOCK_MOVE_UP * ($instance->weight != 0 || ($page->blocks_move_position($instance, BLOCK_MOVE_UP) != $instance->position));
634 // Same thing for downward movement
635 $options |= BLOCK_MOVE_DOWN * ($instance->weight != $maxweight || ($page->blocks_move_position($instance, BLOCK_MOVE_DOWN) != $instance->position));
636 // For left and right movements, it's up to the page to tell us whether they are allowed
637 $options |= BLOCK_MOVE_RIGHT * ($page->blocks_move_position($instance, BLOCK_MOVE_RIGHT) != $instance->position);
638 $options |= BLOCK_MOVE_LEFT * ($page->blocks_move_position($instance, BLOCK_MOVE_LEFT ) != $instance->position);
639 // Finally, the block can be configured if the block class either allows multiple instances, or if it specifically
640 // allows instance configuration (multiple instances override that one). It doesn't have anything to do with what the
641 // administrator has allowed for this block in the site admin options.
642 $options |= BLOCK_CONFIGURE * ( $obj->instance_allow_multiple() || $obj->instance_allow_config() );
1345403a 643 $obj->_add_edit_controls($options);
9b4b78fd 644 }
0f3fe4b6 645
0a0bb380 646 if (!$instance->visible && empty($COURSE->javascriptportal)) {
647 if ($isediting) {
1345403a 648 $obj->_print_shadow();
0f3fe4b6 649 }
0a0bb380 650 } else {
4a1a14c9 651 global $COURSE;
d23157d8 652 if(!empty($COURSE->javascriptportal)) {
653 $COURSE->javascriptportal->currentblocksection = $position;
654 }
1345403a 655 $obj->_print_block();
9b4b78fd 656 }
d23157d8 657 if (!empty($COURSE->javascriptportal)
658 && (empty($instance->pinned) || !$instance->pinned)) {
afd1ec02 659 $COURSE->javascriptportal->block_add('inst'.$instance->id, !$instance->visible);
d23157d8 660 }
661 } // End foreach
662
282c1695 663 // Check if
664 // we are on the default position/side AND
665 // we're editing the page AND
666 // (
667 // we have the capability to manage blocks OR
668 // we are in myMoodle page AND have the capibility to manage myMoodle blocks
669 // )
6ad71d66 670
671 // for constant PAGE_MY_MOODLE
672 include_once($CFG->dirroot.'/my/pagelib.php');
afd1ec02 673
6ad71d66 674 $coursecontext = get_context_instance(CONTEXT_COURSE, $COURSE->id);
d529807a 675 $myownblogpage = (isset($page->filtertype) && isset($page->filterselect) && $page->pagetype=='blog-view' && $page->filtertype=='user' && $page->filterselect == $USER->id);
afd1ec02 676
6ad71d66 677 $managecourseblocks = has_capability('moodle/site:manageblocks', $coursecontext);
d529807a 678 $editmymoodle = $page->pagetype == PAGE_MY_MOODLE && has_capability('moodle/my:manageblocks', $coursecontext);
afd1ec02 679
86b5ea0f 680 if ($page->blocks->get_default_region() == $position &&
0be6f678 681 $page->user_is_editing() &&
ee1efe0b 682 ($managecourseblocks || $editmymoodle || $myownblogpage || defined('ADMIN_STICKYBLOCKS'))) {
6ad71d66 683
66492322 684 blocks_print_adminblock($page, $pageblocks);
685 }
0f3fe4b6 686}
687
c8e0b579 688// This iterates over an array of blocks and calculates the preferred width
689// Parameter passed by reference for speed; it's not modified.
690function blocks_preferred_width(&$instances) {
0f3fe4b6 691 $width = 0;
692
9b4b78fd 693 if(empty($instances) || !is_array($instances)) {
0f3fe4b6 694 return 0;
695 }
ff6191b7 696
697 $blocks = blocks_get_record();
698
9b4b78fd 699 foreach($instances as $instance) {
700 if(!$instance->visible) {
fe78a3dc 701 continue;
0c9c6363 702 }
ff6191b7 703
f809df70 704 if (!array_key_exists($instance->blockid, $blocks)) {
705 // Block doesn't exist! We should delete this instance!
706 continue;
707 }
708
ff6191b7 709 if(!$blocks[$instance->blockid]->visible) {
9b687320 710 continue;
711 }
ff6191b7 712 $pref = block_method_result($blocks[$instance->blockid]->name, 'preferred_width');
9b4b78fd 713 if($pref === NULL) {
714 continue;
715 }
716 if($pref > $width) {
717 $width = $pref;
0f3fe4b6 718 }
719 }
720 return $width;
721}
722
08eab897 723/**
724 * Get the block record for a particulr blockid.
725 * @param $blockid block type id. If null, an array of all block types is returned.
726 * @param $notusedanymore No longer used.
727 * @return array|object row from block table, or all rows.
728 */
729function blocks_get_record($blockid = NULL, $notusedanymore = false) {
730 global $PAGE;
731 $blocks = $PAGE->blocks->get_installed_blocks();
732 if ($blockid === NULL) {
733 return $blocks;
734 } else if (isset($blocks[$blockid])) {
735 return $blocks[$blockid];
736 } else {
737 return false;
9b4b78fd 738 }
9b4b78fd 739}
740
741function blocks_find_block($blockid, $blocksarray) {
0d6b9d4f 742 if (empty($blocksarray)) {
743 return false;
744 }
9b4b78fd 745 foreach($blocksarray as $blockgroup) {
0d6b9d4f 746 if (empty($blockgroup)) {
747 continue;
748 }
9b4b78fd 749 foreach($blockgroup as $instance) {
750 if($instance->blockid == $blockid) {
751 return $instance;
752 }
753 }
754 }
755 return false;
756}
757
758function blocks_find_instance($instanceid, $blocksarray) {
759 foreach($blocksarray as $subarray) {
760 foreach($subarray as $instance) {
761 if($instance->id == $instanceid) {
762 return $instance;
763 }
764 }
765 }
766 return false;
767}
768
ec79d3e4 769// Simple entry point for anyone that wants to use blocks
ee6055eb 770function blocks_setup(&$PAGE,$pinned=BLOCKS_PINNED_FALSE) {
771 switch ($pinned) {
772 case BLOCKS_PINNED_TRUE:
773 $pageblocks = blocks_get_pinned($PAGE);
774 break;
775 case BLOCKS_PINNED_BOTH:
776 $pageblocks = blocks_get_by_page_pinned($PAGE);
777 break;
778 case BLOCKS_PINNED_FALSE:
779 default:
780 $pageblocks = blocks_get_by_page($PAGE);
781 break;
782 }
783 blocks_execute_url_action($PAGE, $pageblocks,($pinned==BLOCKS_PINNED_TRUE));
ec79d3e4 784 return $pageblocks;
785}
786
b1631fef 787function blocks_execute_action($page, &$pageblocks, $blockaction, $instanceorid, $pinned=false, $redirect=true) {
58ff964f 788 global $CFG, $USER, $DB;
9b4b78fd 789
a9c75a9c 790 if (is_int($instanceorid)) {
9b4b78fd 791 $blockid = $instanceorid;
a9c75a9c 792 } else if (is_object($instanceorid)) {
9b4b78fd 793 $instance = $instanceorid;
794 }
0f3fe4b6 795
796 switch($blockaction) {
9b4b78fd 797 case 'config':
9b4b78fd 798 $block = blocks_get_record($instance->blockid);
e82d6cac 799 // Hacky hacky tricky stuff to get the original human readable block title,
800 // even if the block has configured its title to be something else.
afd1ec02 801 // Create the object WITHOUT instance data.
e82d6cac 802 $blockobject = block_instance($block->name);
9b4b78fd 803 if ($blockobject === false) {
11306331 804 break;
805 }
afd1ec02 806
11306331 807 // First of all check to see if the block wants to be edited
808 if(!$blockobject->user_can_edit()) {
809 break;
9b4b78fd 810 }
11306331 811
e82d6cac 812 // Now get the title and AFTER that load up the instance
813 $blocktitle = $blockobject->get_title();
814 $blockobject->_load_instance($instance);
afd1ec02 815
27ec21a0 816 // Define the data we're going to silently include in the instance config form here,
9b4b78fd 817 // so we can strip them from the submitted data BEFORE serializing it.
818 $hiddendata = array(
19f5b2db 819 'sesskey' => sesskey(),
9b4b78fd 820 'instanceid' => $instance->id,
821 'blockaction' => 'config'
822 );
f032aa7a 823
824 // To this data, add anything the page itself needs to display
ad52c04f 825 $hiddendata = $page->url->params($hiddendata);
9b4b78fd 826
294ce987 827 if ($data = data_submitted()) {
9b4b78fd 828 $remove = array_keys($hiddendata);
829 foreach($remove as $item) {
830 unset($data->$item);
0f3fe4b6 831 }
58ff964f 832 if(!$blockobject->instance_config_save($data, $pinned)) {
e49ef64a 833 print_error('cannotsaveblock');
0f3fe4b6 834 }
9b4b78fd 835 // And nothing more, continue with displaying the page
0f3fe4b6 836 }
9b4b78fd 837 else {
f032aa7a 838 // We need to show the config screen, so we highjack the display logic and then die
e82d6cac 839 $strheading = get_string('blockconfiga', 'moodle', $blocktitle);
edb42f09 840 $page->print_header(get_string('pageheaderconfigablock', 'moodle'), array($strheading => ''));
b9709905 841
842 echo '<div class="block-config" id="'.$block->name.'">'; /// Make CSS easier
0be6f678 843
edb42f09 844 print_heading($strheading);
ad52c04f 845 echo '<form method="post" name="block-config" action="'. $page->url->out(false) .'">';
9b4b78fd 846 echo '<p>';
847 foreach($hiddendata as $name => $val) {
27ec21a0 848 echo '<input type="hidden" name="'. $name .'" value="'. $val .'" />';
0f3fe4b6 849 }
9b4b78fd 850 echo '</p>';
851 $blockobject->instance_config_print();
852 echo '</form>';
b9709905 853
854 echo '</div>';
ad5d5997 855 $PAGE->set_pagetype('blocks-' . $block->name);
9b4b78fd 856 print_footer();
f032aa7a 857 die(); // Do not go on with the other page-related stuff
0f3fe4b6 858 }
859 break;
9b4b78fd 860 case 'toggle':
861 if(empty($instance)) {
e49ef64a 862 print_error('invalidblockinstance', '', '', $blockaction);
0f3fe4b6 863 }
9b4b78fd 864 $instance->visible = ($instance->visible) ? 0 : 1;
0d6b9d4f 865 if (!empty($pinned)) {
66b10689 866 $DB->update_record('block_pinned_old', $instance);
0d6b9d4f 867 } else {
66b10689 868 $DB->update_record('block_instance_old', $instance);
0d6b9d4f 869 }
9b4b78fd 870 break;
871 case 'delete':
872 if(empty($instance)) {
e49ef64a 873 print_error('invalidblockinstance', '', '', $blockaction);
0f3fe4b6 874 }
0d6b9d4f 875 blocks_delete_instance($instance, $pinned);
0f3fe4b6 876 break;
877 case 'moveup':
9b4b78fd 878 if(empty($instance)) {
e49ef64a 879 print_error('invalidblockinstance', '', '', $blockaction);
9b4b78fd 880 }
f032aa7a 881
882 if($instance->weight == 0) {
883 // The block is the first one, so a move "up" probably means it changes position
884 // Where is the instance going to be moved?
885 $newpos = $page->blocks_move_position($instance, BLOCK_MOVE_UP);
6b853ff4 886 $newweight = (empty($pageblocks[$newpos]) ? 0 : max(array_keys($pageblocks[$newpos])) + 1);
f032aa7a 887
0d6b9d4f 888 blocks_execute_repositioning($instance, $newpos, $newweight, $pinned);
89a5baab 889 }
f032aa7a 890 else {
891 // The block is just moving upwards in the same position.
892 // This configuration will make sure that even if somehow the weights
893 // become not continuous, block move operations will eventually bring
894 // the situation back to normal without printing any warnings.
895 if(!empty($pageblocks[$instance->position][$instance->weight - 1])) {
896 $other = $pageblocks[$instance->position][$instance->weight - 1];
897 }
898 if(!empty($other)) {
899 ++$other->weight;
0d6b9d4f 900 if (!empty($pinned)) {
66b10689 901 $DB->update_record('block_pinned_old', $other);
0d6b9d4f 902 } else {
66b10689 903 $DB->update_record('block_instance_old', $other);
afd1ec02 904 }
f032aa7a 905 }
906 --$instance->weight;
0d6b9d4f 907 if (!empty($pinned)) {
66b10689 908 $DB->update_record('block_pinned_old', $instance);
0d6b9d4f 909 } else {
66b10689 910 $DB->update_record('block_instance_old', $instance);
0d6b9d4f 911 }
0f3fe4b6 912 }
913 break;
914 case 'movedown':
9b4b78fd 915 if(empty($instance)) {
e49ef64a 916 print_error('invalidblockinstance', '', '', $blockaction);
9b4b78fd 917 }
f032aa7a 918
919 if($instance->weight == max(array_keys($pageblocks[$instance->position]))) {
920 // The block is the last one, so a move "down" probably means it changes position
921 // Where is the instance going to be moved?
922 $newpos = $page->blocks_move_position($instance, BLOCK_MOVE_DOWN);
6b853ff4 923 $newweight = (empty($pageblocks[$newpos]) ? 0 : max(array_keys($pageblocks[$newpos])) + 1);
f032aa7a 924
0d6b9d4f 925 blocks_execute_repositioning($instance, $newpos, $newweight, $pinned);
89a5baab 926 }
f032aa7a 927 else {
928 // The block is just moving downwards in the same position.
929 // This configuration will make sure that even if somehow the weights
930 // become not continuous, block move operations will eventually bring
931 // the situation back to normal without printing any warnings.
932 if(!empty($pageblocks[$instance->position][$instance->weight + 1])) {
933 $other = $pageblocks[$instance->position][$instance->weight + 1];
934 }
935 if(!empty($other)) {
936 --$other->weight;
0d6b9d4f 937 if (!empty($pinned)) {
66b10689 938 $DB->update_record('block_pinned_old', $other);
0d6b9d4f 939 } else {
66b10689 940 $DB->update_record('block_instance_old', $other);
0d6b9d4f 941 }
f032aa7a 942 }
943 ++$instance->weight;
0d6b9d4f 944 if (!empty($pinned)) {
66b10689 945 $DB->update_record('block_pinned_old', $instance);
0d6b9d4f 946 } else {
66b10689 947 $DB->update_record('block_instance_old', $instance);
0d6b9d4f 948 }
0f3fe4b6 949 }
950 break;
9b4b78fd 951 case 'moveleft':
952 if(empty($instance)) {
e49ef64a 953 print_error('invalidblockinstance', '', '', $blockaction);
9b4b78fd 954 }
f032aa7a 955
956 // Where is the instance going to be moved?
957 $newpos = $page->blocks_move_position($instance, BLOCK_MOVE_LEFT);
6b853ff4 958 $newweight = (empty($pageblocks[$newpos]) ? 0 : max(array_keys($pageblocks[$newpos])) + 1);
f032aa7a 959
0d6b9d4f 960 blocks_execute_repositioning($instance, $newpos, $newweight, $pinned);
0f3fe4b6 961 break;
9b4b78fd 962 case 'moveright':
963 if(empty($instance)) {
e49ef64a 964 print_error('invalidblockinstance', '', '', $blockaction);
9b4b78fd 965 }
f032aa7a 966
967 // Where is the instance going to be moved?
968 $newpos = $page->blocks_move_position($instance, BLOCK_MOVE_RIGHT);
6b853ff4 969 $newweight = (empty($pageblocks[$newpos]) ? 0 : max(array_keys($pageblocks[$newpos])) + 1);
f032aa7a 970
0d6b9d4f 971 blocks_execute_repositioning($instance, $newpos, $newweight, $pinned);
9b4b78fd 972 break;
973 case 'add':
974 // Add a new instance of this block, if allowed
975 $block = blocks_get_record($blockid);
0f3fe4b6 976
3cacefda 977 if(empty($block) || !$block->visible) {
978 // Only allow adding if the block exists and is enabled
11306331 979 break;
9b4b78fd 980 }
0f3fe4b6 981
89a5baab 982 if(!$block->multiple && blocks_find_block($blockid, $pageblocks) !== false) {
983 // If no multiples are allowed and we already have one, return now
11306331 984 break;
985 }
986
987 if(!block_method_result($block->name, 'user_can_addto', $page)) {
988 // If the block doesn't want to be added...
989 break;
89a5baab 990 }
991
86b5ea0f 992 $newpos = $page->blocks->get_default_region();
0d6b9d4f 993 if (!empty($pinned)) {
58ff964f 994 $sql = "SELECT 1, MAX(weight) + 1 AS nextfree
66b10689 995 FROM {block_pinned_old}
58ff964f 996 WHERE pagetype = ? AND position = ?";
f230ce19 997 $params = array($page->pagetype, $newpos);
58ff964f 998
0d6b9d4f 999 } else {
58ff964f 1000 $sql = "SELECT 1, MAX(weight) + 1 AS nextfree
66b10689 1001 FROM {block_instance_old}
58ff964f 1002 WHERE pageid = ? AND pagetype = ? AND position = ?";
f230ce19 1003 $params = array($page->get_id(), $page->pagetype, $newpos);
0d6b9d4f 1004 }
58ff964f 1005 $weight = $DB->get_record_sql($sql, $params);
9b4b78fd 1006
1007 $newinstance = new stdClass;
1008 $newinstance->blockid = $blockid;
0d6b9d4f 1009 if (empty($pinned)) {
1010 $newinstance->pageid = $page->get_id();
1011 }
f230ce19 1012 $newinstance->pagetype = $page->pagetype;
f032aa7a 1013 $newinstance->position = $newpos;
ff679897 1014 $newinstance->weight = empty($weight->nextfree) ? 0 : $weight->nextfree;
9b4b78fd 1015 $newinstance->visible = 1;
1016 $newinstance->configdata = '';
0d6b9d4f 1017 if (!empty($pinned)) {
66b10689 1018 $newinstance->id = $DB->insert_record('block_pinned_old', $newinstance);
0d6b9d4f 1019 } else {
66b10689 1020 $newinstance->id = $DB->insert_record('block_instance_old', $newinstance);
0d6b9d4f 1021 }
b33dd23a 1022
1023 // If the new instance was created, allow it to do additional setup
1024 if($newinstance && ($obj = block_instance($block->name, $newinstance))) {
1025 // Return value ignored
1026 $obj->instance_create();
1027 }
1028
9b4b78fd 1029 break;
1030 }
f032aa7a 1031
b1631fef 1032 if ($redirect) {
1033 // In order to prevent accidental duplicate actions, redirect to a page with a clean url
ad52c04f 1034 redirect($page->url->out());
b1631fef 1035 }
f032aa7a 1036}
1037
da71112b 1038// You can use this to get the blocks to respond to URL actions without much hassle
0d6b9d4f 1039function blocks_execute_url_action(&$PAGE, &$pageblocks,$pinned=false) {
02cc05a7 1040 $blockaction = optional_param('blockaction', '', PARAM_ALPHA);
da71112b 1041
3edc57e1 1042 if (empty($blockaction) || !$PAGE->user_allowed_editing() || !confirm_sesskey()) {
da71112b 1043 return;
1044 }
1045
1046 $instanceid = optional_param('instanceid', 0, PARAM_INT);
1047 $blockid = optional_param('blockid', 0, PARAM_INT);
afd1ec02 1048
da71112b 1049 if (!empty($blockid)) {
0d6b9d4f 1050 blocks_execute_action($PAGE, $pageblocks, strtolower($blockaction), $blockid, $pinned);
da71112b 1051
1052 }
1053 else if (!empty($instanceid)) {
1054 $instance = blocks_find_instance($instanceid, $pageblocks);
0d6b9d4f 1055 blocks_execute_action($PAGE, $pageblocks, strtolower($blockaction), $instance, $pinned);
da71112b 1056 }
1057}
1058
f032aa7a 1059// This shouldn't be used externally at all, it's here for use by blocks_execute_action()
1060// in order to reduce code repetition.
29ca8b88 1061function blocks_execute_repositioning(&$instance, $newpos, $newweight, $pinned=false) {
58ff964f 1062 global $DB;
f032aa7a 1063
c4308cfa 1064 // If it's staying where it is, don't do anything, unless overridden
29ca8b88 1065 if ($newpos == $instance->position) {
f032aa7a 1066 return;
1067 }
1068
1069 // Close the weight gap we 'll leave behind
0d6b9d4f 1070 if (!empty($pinned)) {
66b10689 1071 $sql = "UPDATE {block_instance_old}
58ff964f 1072 SET weight = weight - 1
1073 WHERE pagetype = ? AND position = ? AND weight > ?";
1074 $params = array($instance->pagetype, $instance->position, $instance->weight);
1075
0d6b9d4f 1076 } else {
66b10689 1077 $sql = "UPDATE {block_instance_old}
58ff964f 1078 SET weight = weight - 1
1079 WHERE pagetype = ? AND pageid = ?
1080 AND position = ? AND weight > ?";
1081 $params = array($instance->pagetype, $instance->pageid, $instance->position, $instance->weight);
0d6b9d4f 1082 }
58ff964f 1083 $DB->execute($sql, $params);
f032aa7a 1084
1085 $instance->position = $newpos;
1086 $instance->weight = $newweight;
1087
0d6b9d4f 1088 if (!empty($pinned)) {
66b10689 1089 $DB->update_record('block_pinned_old', $instance);
0d6b9d4f 1090 } else {
66b10689 1091 $DB->update_record('block_instance_old', $instance);
0d6b9d4f 1092 }
1093}
1094
29ca8b88 1095
1096/**
1097 * Moves a block to the new position (column) and weight (sort order).
1098 * @param $instance - The block instance to be moved.
1099 * @param $destpos - BLOCK_POS_LEFT or BLOCK_POS_RIGHT. The destination column.
1100 * @param $destweight - The destination sort order. If NULL, we add to the end
1101 * of the destination column.
1102 * @param $pinned - Are we moving pinned blocks? We can only move pinned blocks
1103 * to a new position withing the pinned list. Likewise, we
1104 * can only moved non-pinned blocks to a new position within
1105 * the non-pinned list.
1106 * @return boolean (success or failure).
1107 */
1108function blocks_move_block($page, &$instance, $destpos, $destweight=NULL, $pinned=false) {
58ff964f 1109 global $CFG, $DB;
afd1ec02 1110
29ca8b88 1111 if ($pinned) {
1112 $blocklist = blocks_get_pinned($page);
1113 } else {
1114 $blocklist = blocks_get_by_page($page);
1115 }
afd1ec02 1116
29ca8b88 1117 if ($blocklist[$instance->position][$instance->weight]->id != $instance->id) {
1118 // The source block instance is not where we think it is.
c4308cfa 1119 return false;
d23157d8 1120 }
afd1ec02 1121
29ca8b88 1122 // First we close the gap that will be left behind when we take out the
1123 // block from it's current column.
1124 if ($pinned) {
66b10689 1125 $closegapsql = "UPDATE {block_instance_old}
afd1ec02 1126 SET weight = weight - 1
58ff964f 1127 WHERE weight > ? AND position = ? AND pagetype = ?";
1128 $params = array($instance->weight, $instance->position, $instance->pagetype);
e028ed34 1129 } else {
66b10689 1130 $closegapsql = "UPDATE {block_instance_old}
afd1ec02 1131 SET weight = weight - 1
58ff964f 1132 WHERE weight > ? AND position = ?
1133 AND pagetype = ? AND pageid = ?";
1134 $params = array($instance->weight, $instance->position, $instance->pagetype, $instance->pageid);
29ca8b88 1135 }
58ff964f 1136 if (!$DB->execute($closegapsql, $params)) {
29ca8b88 1137 return false;
77e65ff7 1138 }
afd1ec02 1139
29ca8b88 1140 // Now let's make space for the block being moved.
1141 if ($pinned) {
66b10689 1142 $opengapsql = "UPDATE {block_instance_old}
afd1ec02 1143 SET weight = weight + 1
58ff964f 1144 WHERE weight >= ? AND position = ? AND pagetype = ?";
1145 $params = array($destweight, $destpos, $instance->pagetype);
d23157d8 1146 } else {
66b10689 1147 $opengapsql = "UPDATE {block_instance_old}
58ff964f 1148 SET weight = weight + 1
1149 WHERE weight >= ? AND position = ?
1150 AND pagetype = ? AND pageid = ?";
1151 $params = array($destweight, $destpos, $instance->pagetype, $instance->pageid);
29ca8b88 1152 }
655b09ca 1153 if (!$DB->execute($opengapsql, $params)) {
29ca8b88 1154 return false;
c4308cfa 1155 }
afd1ec02 1156
29ca8b88 1157 // Move the block.
1158 $instance->position = $destpos;
1159 $instance->weight = $destweight;
e028ed34 1160
29ca8b88 1161 if ($pinned) {
66b10689 1162 $table = 'block_pinned_old';
29ca8b88 1163 } else {
66b10689 1164 $table = 'block_instance_old';
29ca8b88 1165 }
58ff964f 1166 return $DB->update_record($table, $instance);
e028ed34 1167}
1168
d23157d8 1169
1170/**
1171 * Returns an array consisting of 2 arrays:
1172 * 1) Array of pinned blocks for position BLOCK_POS_LEFT
1173 * 2) Array of pinned blocks for position BLOCK_POS_RIGHT
1174 */
0d6b9d4f 1175function blocks_get_pinned($page) {
58ff964f 1176 global $DB;
afd1ec02 1177
0d6b9d4f 1178 $visible = true;
58ff964f 1179 $select = "pagetype = ?";
f230ce19 1180 $params = array($page->pagetype);
58ff964f 1181
1182 if ($visible) {
1183 $select .= " AND visible = 1";
1184 }
1185
66b10689 1186 $blocks = $DB->get_records_select('block_pinned_old', $select, $params, 'position, weight');
0d6b9d4f 1187
86b5ea0f 1188 $positions = $page->blocks->get_regions();
0d6b9d4f 1189 $arr = array();
1190
1191 foreach($positions as $key => $position) {
1192 $arr[$position] = array();
1193 }
1194
1195 if(empty($blocks)) {
1196 return $arr;
1197 }
1198
1199 foreach($blocks as $block) {
1200 $block->pinned = true; // so we know we can't move it.
ddf1935f 1201 // make up an instanceid if we can..
1202 $block->pageid = $page->get_id();
0d6b9d4f 1203 $arr[$block->position][$block->weight] = $block;
1204 }
1205
afd1ec02 1206 return $arr;
0d6b9d4f 1207}
1208
1209
d23157d8 1210/**
1211 * Similar to blocks_get_by_page(), except that, the array returned includes
1212 * pinned blocks as well. Pinned blocks are always appended before normal
1213 * block instances.
1214 */
0d6b9d4f 1215function blocks_get_by_page_pinned($page) {
1216 $pinned = blocks_get_pinned($page);
1217 $user = blocks_get_by_page($page);
afd1ec02 1218
0d6b9d4f 1219 $weights = array();
1220
1221 foreach ($pinned as $pos => $arr) {
1222 $weights[$pos] = count($arr);
1223 }
1224
1225 foreach ($user as $pos => $blocks) {
1226 if (!array_key_exists($pos,$pinned)) {
1227 $pinned[$pos] = array();
1228 }
1229 if (!array_key_exists($pos,$weights)) {
1230 $weights[$pos] = 0;
1231 }
77e65ff7 1232 foreach ($blocks as $block) {
0d6b9d4f 1233 $pinned[$pos][$weights[$pos]] = $block;
1234 $weights[$pos]++;
1235 }
1236 }
1237 return $pinned;
0f3fe4b6 1238}
1239
d23157d8 1240
1241/**
1242 * Returns an array of blocks for the page. Pinned blocks are excluded.
1243 */
9b4b78fd 1244function blocks_get_by_page($page) {
58ff964f 1245 global $DB;
1246
66b10689 1247 $blocks = $DB->get_records_select('block_instance_old', "pageid = ? AND ? LIKE (" . $DB->sql_concat('pagetype', "'%'") . ")",
f230ce19 1248 array($page->get_id(), $page->pagetype), 'position, weight');
f032aa7a 1249
86b5ea0f 1250 $positions = $page->blocks->get_regions();
f032aa7a 1251 $arr = array();
1252 foreach($positions as $key => $position) {
1253 $arr[$position] = array();
1254 }
0f3fe4b6 1255
9b4b78fd 1256 if(empty($blocks)) {
1257 return $arr;
1258 }
0f3fe4b6 1259
77e65ff7 1260 foreach($blocks as $block) {
9b4b78fd 1261 $arr[$block->position][$block->weight] = $block;
0f3fe4b6 1262 }
d23157d8 1263 return $arr;
9b4b78fd 1264}
0f3fe4b6 1265
d23157d8 1266
9b4b78fd 1267//This function prints the block to admin blocks as necessary
c1d8705f 1268function blocks_print_adminblock(&$page, &$pageblocks) {
9b4b78fd 1269 global $USER;
0f3fe4b6 1270
08eab897 1271 $missingblocks = array_keys($page->blocks->get_addable_blocks());
c1d8705f 1272
9b4b78fd 1273 if (!empty($missingblocks)) {
74fd9ff9 1274 $strblocks = '<div class="title"><h2>';
303fe9f4 1275 $strblocks .= get_string('blocks');
552adc27 1276 $strblocks .= '</h2></div>';
c1d8705f 1277 $stradd = get_string('add');
9b4b78fd 1278 foreach ($missingblocks as $blockid) {
1279 $block = blocks_get_record($blockid);
9b4b78fd 1280 $blockobject = block_instance($block->name);
1281 if ($blockobject === false) {
1282 continue;
1283 }
11306331 1284 if(!$blockobject->user_can_addto($page)) {
1285 continue;
1286 }
9b4b78fd 1287 $menu[$block->id] = $blockobject->get_title();
1288 }
4a4c30d2 1289 asort($menu);
0f3fe4b6 1290
ad52c04f 1291 $target = $page->url->out(array('sesskey' => sesskey(), 'blockaction' => 'add'));
f032aa7a 1292 $content = popup_form($target.'&amp;blockid=', $menu, 'add_block', '', $stradd .'...', '', '', true);
afd8402c 1293 print_side_block($strblocks, $content, NULL, NULL, NULL, array('class' => 'block_adminblock'));
0f3fe4b6 1294 }
0f3fe4b6 1295}
1296
19f5b2db 1297/**
1298 * Delete all the blocks from a particular page.
1299 *
1300 * @param string $pagetype the page type.
1301 * @param integer $pageid the page id.
1302 * @return success of failure.
1303 */
1304function blocks_delete_all_on_page($pagetype, $pageid) {
1305 global $DB;
66b10689 1306 if ($instances = $DB->get_records('block_instance_old', array('pageid' => $pageid, 'pagetype' => $pagetype))) {
19f5b2db 1307 foreach ($instances as $instance) {
1308 delete_context(CONTEXT_BLOCK, $instance->id); // Ingore any failures here.
1309 }
1310 }
66b10689 1311 return $DB->delete_records('block_instance_old', array('pageid' => $pageid, 'pagetype' => $pagetype));
19f5b2db 1312}
1313
1314// Dispite what this function is called, it seems to be mostly used to populate
1315// the default blocks when a new course (or whatever) is created.
9b4b78fd 1316function blocks_repopulate_page($page) {
7e0489f4 1317 global $CFG, $DB;
5b224948 1318
9b4b78fd 1319 $allblocks = blocks_get_record();
1320
1321 if(empty($allblocks)) {
e49ef64a 1322 print_error('cannotgetblock');
9b4b78fd 1323 }
1324
f032aa7a 1325 // Assemble the information to correlate block names to ids
9b4b78fd 1326 $idforname = array();
1327 foreach($allblocks as $block) {
1328 $idforname[$block->name] = $block->id;
1329 }
1330
f032aa7a 1331 /// If the site override has been defined, it is the only valid one.
1332 if (!empty($CFG->defaultblocks_override)) {
1333 $blocknames = $CFG->defaultblocks_override;
1334 }
1335 else {
1336 $blocknames = $page->blocks_get_default();
1337 }
afd1ec02 1338
86b5ea0f 1339 $positions = $page->blocks->get_regions();
f032aa7a 1340 $posblocks = explode(':', $blocknames);
1341
1342 // Now one array holds the names of the positions, and the other one holds the blocks
1343 // that are going to go in each position. Luckily for us, both arrays are numerically
1344 // indexed and the indexes match, so we can work straight away... but CAREFULLY!
9b4b78fd 1345
f032aa7a 1346 // Ready to start creating block instances, but first drop any existing ones
f230ce19 1347 blocks_delete_all_on_page($page->pagetype, $page->get_id());
f032aa7a 1348
1349 // Here we slyly count $posblocks and NOT $positions. This can actually make a difference
1350 // if the textual representation has undefined slots in the end. So we only work with as many
1351 // positions were retrieved, not with all the page says it has available.
1352 $numpositions = count($posblocks);
1353 for($i = 0; $i < $numpositions; ++$i) {
1354 $position = $positions[$i];
1355 $blocknames = explode(',', $posblocks[$i]);
9b4b78fd 1356 $weight = 0;
1357 foreach($blocknames as $blockname) {
1358 $newinstance = new stdClass;
1359 $newinstance->blockid = $idforname[$blockname];
f032aa7a 1360 $newinstance->pageid = $page->get_id();
f230ce19 1361 $newinstance->pagetype = $page->pagetype;
9b4b78fd 1362 $newinstance->position = $position;
1363 $newinstance->weight = $weight;
1364 $newinstance->visible = 1;
1365 $newinstance->configdata = '';
3cacefda 1366
1367 if(!empty($newinstance->blockid)) {
1368 // Only add block if it was recognized
66b10689 1369 $DB->insert_record('block_instance_old', $newinstance);
3cacefda 1370 ++$weight;
1371 }
0f3fe4b6 1372 }
1373 }
9b4b78fd 1374
1375 return true;
0f3fe4b6 1376}
1377
f10306b9 1378?>