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