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