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