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