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 |
33 | define('BLOCK_MOVE_LEFT', 0x01); |
34 | define('BLOCK_MOVE_RIGHT', 0x02); |
35 | define('BLOCK_MOVE_UP', 0x04); |
36 | define('BLOCK_MOVE_DOWN', 0x08); |
9b4b78fd |
37 | define('BLOCK_CONFIGURE', 0x10); |
0f3fe4b6 |
38 | |
bb46a4fa |
39 | define('BLOCK_POS_LEFT', 'side-pre'); |
40 | define('BLOCK_POS_RIGHT', 'side-post'); |
0e9af917 |
41 | |
ee6055eb |
42 | define('BLOCKS_PINNED_TRUE',0); |
43 | define('BLOCKS_PINNED_FALSE',1); |
44 | define('BLOCKS_PINNED_BOTH',2); |
45 | |
f032aa7a |
46 | require_once($CFG->libdir.'/pagelib.php'); |
61abc65b |
47 | |
f4d38d20 |
48 | class 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 |
81 | class 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 | */ |
552 | abstract 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 |
590 | function 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 |
604 | function 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 |
625 | function 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 |
654 | function 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 |
663 | function 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 |
686 | function 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 | */ |
713 | function 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 | */ |
735 | function 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 | */ |
748 | function 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 |
762 | function 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 |
769 | function 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 |
818 | function 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 | */ |
828 | function 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 | |
840 | function 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 |
858 | function 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 |
864 | function 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 |
1079 | function 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 |
1099 | function 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 | */ |
1148 | function 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 |
1217 | function 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 |
1257 | function 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 |
1286 | function 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 |
1295 | function 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.'&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 | */ |
1326 | function 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 | */ |
1341 | function 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 | */ |
1358 | function 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 | */ |
1406 | function 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 | */ |
1419 | function 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 | ?> |