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