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