dcb1bd3c |
1 | <?php //$Id$ |
0f3fe4b6 |
2 | |
3 | //This library includes all the necessary stuff to use blocks in course pages |
4 | |
0f3fe4b6 |
5 | define('BLOCK_MOVE_LEFT', 0x01); |
6 | define('BLOCK_MOVE_RIGHT', 0x02); |
7 | define('BLOCK_MOVE_UP', 0x04); |
8 | define('BLOCK_MOVE_DOWN', 0x08); |
9b4b78fd |
9 | define('BLOCK_CONFIGURE', 0x10); |
0f3fe4b6 |
10 | |
9b4b78fd |
11 | define('BLOCK_POS_LEFT', 'l'); |
12 | define('BLOCK_POS_RIGHT', 'r'); |
0e9af917 |
13 | |
f032aa7a |
14 | require_once($CFG->libdir.'/pagelib.php'); |
0f3fe4b6 |
15 | |
33bee34c |
16 | // Returns the case-sensitive name of the class' constructor function. This includes both |
17 | // PHP5- and PHP4-style constructors. If no appropriate constructor can be found, returns NULL. |
18 | // If there is no such class, returns boolean false. |
19 | function get_class_constructor($classname) { |
20 | // Caching |
21 | static $constructors = array(); |
22 | |
23 | if(!class_exists($classname)) { |
24 | return false; |
25 | } |
26 | |
27 | // Tests indicate this doesn't hurt even in PHP5. |
28 | $classname = strtolower($classname); |
29 | |
30 | // Return cached value, if exists |
31 | if(isset($constructors[$classname])) { |
32 | return $constructors[$classname]; |
33 | } |
34 | |
35 | // Get a list of methods. After examining several different ways of |
36 | // doing the check, (is_callable, method_exists, function_exists etc) |
37 | // it seems that this is the most reliable one. |
38 | $methods = get_class_methods($classname); |
39 | |
40 | // PHP5 constructor? |
41 | if(phpversion() >= '5') { |
42 | if(in_array('__construct', $methods)) { |
43 | return $constructors[$classname] = '__construct'; |
44 | } |
45 | } |
46 | |
47 | // If we have PHP5 but no magic constructor, we have to lowercase the methods |
48 | $methods = array_map('strtolower', $methods); |
49 | |
50 | if(in_array($classname, $methods)) { |
51 | return $constructors[$classname] = $classname; |
52 | } |
53 | |
54 | return $constructors[$classname] = NULL; |
55 | } |
56 | |
0f3fe4b6 |
57 | //This function retrieves a method-defined property of a class WITHOUT instantiating an object |
58 | //It seems that the only way to use the :: operator with variable class names is eval() :( |
59 | //For caveats with this technique, see the PHP docs on operator :: |
60 | function block_method_result($blockname, $method) { |
61 | if(!block_load_class($blockname)) { |
62 | return NULL; |
63 | } |
e89d741a |
64 | return eval('return block_'.$blockname.'::'.$method.'();'); |
0f3fe4b6 |
65 | } |
66 | |
67 | //This function creates a new object of the specified block class |
9b4b78fd |
68 | function block_instance($blockname, $instance = NULL) { |
0f3fe4b6 |
69 | if(!block_load_class($blockname)) { |
70 | return false; |
71 | } |
e89d741a |
72 | $classname = 'block_'.$blockname; |
f032aa7a |
73 | $retval = new $classname; |
9b4b78fd |
74 | if($instance !== NULL) { |
1345403a |
75 | $retval->_load_instance($instance); |
9b4b78fd |
76 | } |
77 | return $retval; |
0f3fe4b6 |
78 | } |
79 | |
80 | //This function loads the necessary class files for a block |
81 | //Whenever you want to load a block, use this first |
82 | function block_load_class($blockname) { |
83 | global $CFG; |
84 | |
c7a9e293 |
85 | if (empty($blockname)) { |
86 | return false; |
87 | } |
88 | |
febeb161 |
89 | include_once($CFG->dirroot.'/blocks/moodleblock.class.php'); |
e89d741a |
90 | $classname = 'block_'.$blockname; |
febeb161 |
91 | include_once($CFG->dirroot.'/blocks/'.$blockname.'/block_'.$blockname.'.php'); |
0f3fe4b6 |
92 | |
93 | // After all this, return value indicating success or failure |
94 | return class_exists($classname); |
95 | } |
96 | |
c8e0b579 |
97 | // This function returns an array with the IDs of any blocks that you can add to your page. |
98 | // Parameters are passed by reference for speed; they are not modified at all. |
99 | function blocks_get_missing(&$page, &$pageblocks) { |
f032aa7a |
100 | |
101 | $missingblocks = array(); |
102 | $allblocks = blocks_get_record(); |
103 | $pageformat = $page->get_format_name(); |
104 | |
105 | if(!empty($allblocks)) { |
106 | foreach($allblocks as $block) { |
107 | if($block->visible && (!blocks_find_block($block->id, $pageblocks) || $block->multiple)) { |
108 | // And if it's applicable for display in this format... |
109 | $formats = block_method_result($block->name, 'applicable_formats'); |
8a47e075 |
110 | $accept = NULL; |
111 | foreach($formats as $format => $allowed) { |
112 | $thisformat = '^'.str_replace('*', '[^-]*', $format).'.*$'; |
113 | if(ereg($thisformat, $pageformat)) { |
114 | $accept = $allowed; |
115 | break; |
116 | } |
117 | } |
118 | if($accept === NULL) { |
119 | // ...or in all pages... |
120 | $accept = !empty($formats['all']); |
121 | } |
122 | if(!empty($accept)) { |
123 | // ...add it to the missing blocks |
f032aa7a |
124 | $missingblocks[] = $block->id; |
125 | } |
126 | } |
127 | } |
128 | } |
129 | return $missingblocks; |
130 | } |
131 | |
132 | function blocks_remove_inappropriate($page) { |
133 | $pageblocks = blocks_get_by_page($page); |
134 | |
135 | if(empty($pageblocks)) { |
136 | return; |
137 | } |
138 | |
139 | if(($pageformat = $page->get_format_name()) == NULL) { |
140 | return; |
141 | } |
142 | |
143 | foreach($pageblocks as $position) { |
144 | foreach($position as $instance) { |
145 | $block = blocks_get_record($instance->blockid); |
146 | $formats = block_method_result($block->name, 'applicable_formats'); |
8a47e075 |
147 | $accept = NULL; |
148 | foreach($formats as $format => $allowed) { |
149 | $thisformat = '^'.str_replace('*', '[^-]*', $format).'.*$'; |
150 | if(ereg($thisformat, $pageformat)) { |
151 | $accept = $allowed; |
152 | break; |
153 | } |
154 | } |
155 | if($accept === NULL) { |
156 | $accept = !empty($formats['all']); |
157 | } |
158 | if(empty($accept)) { |
159 | blocks_delete_instance($instance); |
f032aa7a |
160 | } |
161 | } |
162 | } |
163 | } |
164 | |
165 | function blocks_delete_instance($instance) { |
166 | global $CFG; |
167 | |
168 | delete_records('block_instance', 'id', $instance->id); |
169 | // And now, decrement the weight of all blocks after this one |
170 | execute_sql('UPDATE '.$CFG->prefix.'block_instance SET weight = weight - 1 WHERE pagetype = \''.$instance->pagetype. |
171 | '\' AND pageid = '.$instance->pageid.' AND position = \''.$instance->position. |
172 | '\' AND weight > '.$instance->weight, false); |
173 | } |
174 | |
c8e0b579 |
175 | // Accepts an array of block instances and checks to see if any of them have content to display |
176 | // (causing them to calculate their content in the process). Returns true or false. Parameter passed |
177 | // by reference for speed; the array is actually not modified. |
178 | function blocks_have_content(&$instances) { |
9b4b78fd |
179 | foreach($instances as $instance) { |
7933cc6b |
180 | if (!$instance->visible) { |
181 | continue; |
182 | } |
183 | if (!$record = blocks_get_record($instance->blockid)) { |
184 | continue; |
185 | } |
186 | if (!$obj = block_instance($record->name, $instance)) { |
9b4b78fd |
187 | continue; |
188 | } |
9b4b78fd |
189 | $content = $obj->get_content(); |
190 | $type = $obj->get_content_type(); |
191 | switch($type) { |
192 | case BLOCK_TYPE_LIST: |
193 | if(!empty($content->items) || !empty($content->footer)) { |
194 | return true; |
195 | } |
196 | break; |
197 | case BLOCK_TYPE_TEXT: |
198 | case BLOCK_TYPE_NUKE: |
199 | if(!empty($content->text) || !empty($content->footer)) { |
200 | return true; |
201 | } |
202 | break; |
0f3fe4b6 |
203 | } |
204 | } |
9b4b78fd |
205 | |
0f3fe4b6 |
206 | return false; |
207 | } |
208 | |
c8e0b579 |
209 | // This function prints one group of blocks in a page |
210 | // Parameters passed by reference for speed; they are not modified. |
211 | function blocks_print_group(&$page, &$instances) { |
9b4b78fd |
212 | |
213 | if(empty($instances)) { |
214 | return; |
215 | } |
0f3fe4b6 |
216 | |
f032aa7a |
217 | $isediting = $page->user_is_editing(); |
0f3fe4b6 |
218 | |
9b4b78fd |
219 | $maxweight = max(array_keys($instances)); |
0f3fe4b6 |
220 | |
9b4b78fd |
221 | foreach($instances as $instance) { |
222 | $block = blocks_get_record($instance->blockid); |
223 | if(!$block->visible) { |
224 | // Disabled by the admin |
225 | continue; |
226 | } |
0f3fe4b6 |
227 | |
ab0e4dd4 |
228 | if (!$obj = block_instance($block->name, $instance)) { |
229 | // Invalid block |
230 | continue; |
231 | } |
0f3fe4b6 |
232 | |
f032aa7a |
233 | if ($isediting) { |
9b4b78fd |
234 | $options = 0; |
f032aa7a |
235 | // 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: |
236 | // the first block might still be able to move up if the page says so (i.e., it will change position) |
237 | $options |= BLOCK_MOVE_UP * ($instance->weight != 0 || ($page->blocks_move_position($instance, BLOCK_MOVE_UP) != $instance->position)); |
238 | // Same thing for downward movement |
239 | $options |= BLOCK_MOVE_DOWN * ($instance->weight != $maxweight || ($page->blocks_move_position($instance, BLOCK_MOVE_DOWN) != $instance->position)); |
240 | // For left and right movements, it's up to the page to tell us whether they are allowed |
241 | $options |= BLOCK_MOVE_RIGHT * ($page->blocks_move_position($instance, BLOCK_MOVE_RIGHT) != $instance->position); |
242 | $options |= BLOCK_MOVE_LEFT * ($page->blocks_move_position($instance, BLOCK_MOVE_LEFT ) != $instance->position); |
243 | // Finally, the block can be configured if the block class either allows multiple instances, or if it specifically |
244 | // allows instance configuration (multiple instances override that one). It doesn't have anything to do with what the |
245 | // administrator has allowed for this block in the site admin options. |
246 | $options |= BLOCK_CONFIGURE * ( $obj->instance_allow_multiple() || $obj->instance_allow_config() ); |
1345403a |
247 | $obj->_add_edit_controls($options); |
9b4b78fd |
248 | } |
0f3fe4b6 |
249 | |
9b4b78fd |
250 | if(!$instance->visible) { |
251 | if($isediting) { |
1345403a |
252 | $obj->_print_shadow(); |
0f3fe4b6 |
253 | } |
254 | } |
9b4b78fd |
255 | else { |
1345403a |
256 | $obj->_print_block(); |
9b4b78fd |
257 | } |
0f3fe4b6 |
258 | } |
259 | } |
260 | |
c8e0b579 |
261 | // This iterates over an array of blocks and calculates the preferred width |
262 | // Parameter passed by reference for speed; it's not modified. |
263 | function blocks_preferred_width(&$instances) { |
0f3fe4b6 |
264 | $width = 0; |
265 | |
9b4b78fd |
266 | if(empty($instances) || !is_array($instances)) { |
0f3fe4b6 |
267 | return 0; |
268 | } |
9b4b78fd |
269 | foreach($instances as $instance) { |
270 | if(!$instance->visible) { |
fe78a3dc |
271 | continue; |
0c9c6363 |
272 | } |
9b4b78fd |
273 | $block = blocks_get_record($instance->blockid); |
274 | $pref = block_method_result($block->name, 'preferred_width'); |
275 | if($pref === NULL) { |
276 | continue; |
277 | } |
278 | if($pref > $width) { |
279 | $width = $pref; |
0f3fe4b6 |
280 | } |
281 | } |
282 | return $width; |
283 | } |
284 | |
9b4b78fd |
285 | function blocks_get_record($blockid = NULL, $invalidate = false) { |
286 | static $cache = NULL; |
0f3fe4b6 |
287 | |
9b4b78fd |
288 | if($invalidate || empty($cache)) { |
c6a2a401 |
289 | $cache = get_records('block', '', '', 'name'); |
9b4b78fd |
290 | } |
0f3fe4b6 |
291 | |
9b4b78fd |
292 | if($blockid === NULL) { |
293 | return $cache; |
294 | } |
0f3fe4b6 |
295 | |
9b4b78fd |
296 | return (isset($cache[$blockid])? $cache[$blockid] : false); |
297 | } |
298 | |
299 | function blocks_find_block($blockid, $blocksarray) { |
300 | foreach($blocksarray as $blockgroup) { |
301 | foreach($blockgroup as $instance) { |
302 | if($instance->blockid == $blockid) { |
303 | return $instance; |
304 | } |
305 | } |
306 | } |
307 | return false; |
308 | } |
309 | |
310 | function blocks_find_instance($instanceid, $blocksarray) { |
311 | foreach($blocksarray as $subarray) { |
312 | foreach($subarray as $instance) { |
313 | if($instance->id == $instanceid) { |
314 | return $instance; |
315 | } |
316 | } |
317 | } |
318 | return false; |
319 | } |
320 | |
321 | function blocks_execute_action($page, &$pageblocks, $blockaction, $instanceorid) { |
322 | global $CFG; |
323 | |
a9c75a9c |
324 | if (is_int($instanceorid)) { |
9b4b78fd |
325 | $blockid = $instanceorid; |
a9c75a9c |
326 | } else if (is_object($instanceorid)) { |
9b4b78fd |
327 | $instance = $instanceorid; |
328 | } |
0f3fe4b6 |
329 | |
330 | switch($blockaction) { |
9b4b78fd |
331 | case 'config': |
f032aa7a |
332 | global $USER; |
9b4b78fd |
333 | $block = blocks_get_record($instance->blockid); |
334 | $blockobject = block_instance($block->name, $instance); |
335 | if ($blockobject === false) { |
336 | continue; |
337 | } |
338 | optional_param('submitted', 0, PARAM_INT); |
339 | |
27ec21a0 |
340 | // Define the data we're going to silently include in the instance config form here, |
9b4b78fd |
341 | // so we can strip them from the submitted data BEFORE serializing it. |
342 | $hiddendata = array( |
343 | 'sesskey' => $USER->sesskey, |
9b4b78fd |
344 | 'instanceid' => $instance->id, |
345 | 'blockaction' => 'config' |
346 | ); |
f032aa7a |
347 | |
348 | // To this data, add anything the page itself needs to display |
349 | $hiddendata = array_merge($hiddendata, $page->url_get_parameters()); |
9b4b78fd |
350 | |
351 | if($data = data_submitted()) { |
352 | $remove = array_keys($hiddendata); |
353 | foreach($remove as $item) { |
354 | unset($data->$item); |
0f3fe4b6 |
355 | } |
9b4b78fd |
356 | if(!$blockobject->instance_config_save($data)) { |
357 | error('Error saving block configuration'); |
0f3fe4b6 |
358 | } |
9b4b78fd |
359 | // And nothing more, continue with displaying the page |
0f3fe4b6 |
360 | } |
9b4b78fd |
361 | else { |
f032aa7a |
362 | // We need to show the config screen, so we highjack the display logic and then die |
edb42f09 |
363 | $strheading = get_string('blockconfiga', 'moodle', $block->name); |
364 | $page->print_header(get_string('pageheaderconfigablock', 'moodle'), array($strheading => '')); |
365 | print_heading($strheading); |
f032aa7a |
366 | echo '<form method="post" action="'. $page->url_get_path() .'">'; |
9b4b78fd |
367 | echo '<p>'; |
368 | foreach($hiddendata as $name => $val) { |
27ec21a0 |
369 | echo '<input type="hidden" name="'. $name .'" value="'. $val .'" />'; |
0f3fe4b6 |
370 | } |
9b4b78fd |
371 | echo '</p>'; |
372 | $blockobject->instance_config_print(); |
373 | echo '</form>'; |
374 | print_footer(); |
f032aa7a |
375 | die(); // Do not go on with the other page-related stuff |
0f3fe4b6 |
376 | } |
377 | break; |
9b4b78fd |
378 | case 'toggle': |
379 | if(empty($instance)) { |
380 | error('Invalid block instance for '.$blockaction); |
0f3fe4b6 |
381 | } |
9b4b78fd |
382 | $instance->visible = ($instance->visible) ? 0 : 1; |
383 | update_record('block_instance', $instance); |
384 | break; |
385 | case 'delete': |
386 | if(empty($instance)) { |
a9c75a9c |
387 | error('Invalid block instance for '. $blockaction); |
0f3fe4b6 |
388 | } |
9b4b78fd |
389 | blocks_delete_instance($instance); |
0f3fe4b6 |
390 | break; |
391 | case 'moveup': |
9b4b78fd |
392 | if(empty($instance)) { |
a9c75a9c |
393 | error('Invalid block instance for '. $blockaction); |
9b4b78fd |
394 | } |
f032aa7a |
395 | |
396 | if($instance->weight == 0) { |
397 | // The block is the first one, so a move "up" probably means it changes position |
398 | // Where is the instance going to be moved? |
399 | $newpos = $page->blocks_move_position($instance, BLOCK_MOVE_UP); |
6b853ff4 |
400 | $newweight = (empty($pageblocks[$newpos]) ? 0 : max(array_keys($pageblocks[$newpos])) + 1); |
f032aa7a |
401 | |
402 | blocks_execute_repositioning($instance, $newpos, $newweight); |
89a5baab |
403 | } |
f032aa7a |
404 | else { |
405 | // The block is just moving upwards in the same position. |
406 | // This configuration will make sure that even if somehow the weights |
407 | // become not continuous, block move operations will eventually bring |
408 | // the situation back to normal without printing any warnings. |
409 | if(!empty($pageblocks[$instance->position][$instance->weight - 1])) { |
410 | $other = $pageblocks[$instance->position][$instance->weight - 1]; |
411 | } |
412 | if(!empty($other)) { |
413 | ++$other->weight; |
414 | update_record('block_instance', $other); |
415 | } |
416 | --$instance->weight; |
417 | update_record('block_instance', $instance); |
0f3fe4b6 |
418 | } |
419 | break; |
420 | case 'movedown': |
9b4b78fd |
421 | if(empty($instance)) { |
a9c75a9c |
422 | error('Invalid block instance for '. $blockaction); |
9b4b78fd |
423 | } |
f032aa7a |
424 | |
425 | if($instance->weight == max(array_keys($pageblocks[$instance->position]))) { |
426 | // The block is the last one, so a move "down" probably means it changes position |
427 | // Where is the instance going to be moved? |
428 | $newpos = $page->blocks_move_position($instance, BLOCK_MOVE_DOWN); |
6b853ff4 |
429 | $newweight = (empty($pageblocks[$newpos]) ? 0 : max(array_keys($pageblocks[$newpos])) + 1); |
f032aa7a |
430 | |
431 | blocks_execute_repositioning($instance, $newpos, $newweight); |
89a5baab |
432 | } |
f032aa7a |
433 | else { |
434 | // The block is just moving downwards in the same position. |
435 | // This configuration will make sure that even if somehow the weights |
436 | // become not continuous, block move operations will eventually bring |
437 | // the situation back to normal without printing any warnings. |
438 | if(!empty($pageblocks[$instance->position][$instance->weight + 1])) { |
439 | $other = $pageblocks[$instance->position][$instance->weight + 1]; |
440 | } |
441 | if(!empty($other)) { |
442 | --$other->weight; |
443 | update_record('block_instance', $other); |
444 | } |
445 | ++$instance->weight; |
446 | update_record('block_instance', $instance); |
0f3fe4b6 |
447 | } |
448 | break; |
9b4b78fd |
449 | case 'moveleft': |
450 | if(empty($instance)) { |
a9c75a9c |
451 | error('Invalid block instance for '. $blockaction); |
9b4b78fd |
452 | } |
f032aa7a |
453 | |
454 | // Where is the instance going to be moved? |
455 | $newpos = $page->blocks_move_position($instance, BLOCK_MOVE_LEFT); |
6b853ff4 |
456 | $newweight = (empty($pageblocks[$newpos]) ? 0 : max(array_keys($pageblocks[$newpos])) + 1); |
f032aa7a |
457 | |
458 | blocks_execute_repositioning($instance, $newpos, $newweight); |
0f3fe4b6 |
459 | break; |
9b4b78fd |
460 | case 'moveright': |
461 | if(empty($instance)) { |
a9c75a9c |
462 | error('Invalid block instance for '. $blockaction); |
9b4b78fd |
463 | } |
f032aa7a |
464 | |
465 | // Where is the instance going to be moved? |
466 | $newpos = $page->blocks_move_position($instance, BLOCK_MOVE_RIGHT); |
6b853ff4 |
467 | $newweight = (empty($pageblocks[$newpos]) ? 0 : max(array_keys($pageblocks[$newpos])) + 1); |
f032aa7a |
468 | |
469 | blocks_execute_repositioning($instance, $newpos, $newweight); |
9b4b78fd |
470 | break; |
471 | case 'add': |
472 | // Add a new instance of this block, if allowed |
473 | $block = blocks_get_record($blockid); |
0f3fe4b6 |
474 | |
3cacefda |
475 | if(empty($block) || !$block->visible) { |
476 | // Only allow adding if the block exists and is enabled |
9b4b78fd |
477 | return false; |
478 | } |
0f3fe4b6 |
479 | |
89a5baab |
480 | if(!$block->multiple && blocks_find_block($blockid, $pageblocks) !== false) { |
481 | // If no multiples are allowed and we already have one, return now |
482 | return false; |
483 | } |
484 | |
f032aa7a |
485 | $newpos = $page->blocks_default_position(); |
486 | $weight = get_record_sql('SELECT 1, max(weight) + 1 AS nextfree FROM '. $CFG->prefix .'block_instance WHERE pageid = '. $page->get_id() .' AND pagetype = \''. $page->get_type() .'\' AND position = \''. $newpos .'\''); |
9b4b78fd |
487 | |
488 | $newinstance = new stdClass; |
489 | $newinstance->blockid = $blockid; |
f032aa7a |
490 | $newinstance->pageid = $page->get_id(); |
491 | $newinstance->pagetype = $page->get_type(); |
492 | $newinstance->position = $newpos; |
9b4b78fd |
493 | $newinstance->weight = $weight->nextfree; |
494 | $newinstance->visible = 1; |
495 | $newinstance->configdata = ''; |
496 | insert_record('block_instance', $newinstance); |
497 | break; |
498 | } |
f032aa7a |
499 | |
500 | // In order to prevent accidental duplicate actions, redirect to a page with a clean url |
501 | redirect($page->url_get_full()); |
502 | } |
503 | |
da71112b |
504 | // You can use this to get the blocks to respond to URL actions without much hassle |
505 | function blocks_execute_url_action(&$PAGE, &$pageblocks) { |
506 | $blockaction = optional_param('blockaction'); |
507 | |
3edc57e1 |
508 | if (empty($blockaction) || !$PAGE->user_allowed_editing() || !confirm_sesskey()) { |
da71112b |
509 | return; |
510 | } |
511 | |
512 | $instanceid = optional_param('instanceid', 0, PARAM_INT); |
513 | $blockid = optional_param('blockid', 0, PARAM_INT); |
514 | |
515 | if (!empty($blockid)) { |
516 | blocks_execute_action($PAGE, $pageblocks, strtolower($blockaction), $blockid); |
517 | |
518 | } |
519 | else if (!empty($instanceid)) { |
520 | $instance = blocks_find_instance($instanceid, $pageblocks); |
521 | blocks_execute_action($PAGE, $pageblocks, strtolower($blockaction), $instance); |
522 | } |
523 | } |
524 | |
f032aa7a |
525 | // This shouldn't be used externally at all, it's here for use by blocks_execute_action() |
526 | // in order to reduce code repetition. |
527 | function blocks_execute_repositioning(&$instance, $newpos, $newweight) { |
528 | global $CFG; |
529 | |
530 | // If it's staying where it is, don't do anything |
531 | if($newpos == $instance->position) { |
532 | return; |
533 | } |
534 | |
535 | // Close the weight gap we 'll leave behind |
536 | execute_sql('UPDATE '. $CFG->prefix .'block_instance SET weight = weight - 1 WHERE pagetype = \''. $instance->pagetype. |
537 | '\' AND pageid = '. $instance->pageid .' AND position = \'' .$instance->position. |
538 | '\' AND weight > '. $instance->weight, |
539 | false); |
540 | |
541 | $instance->position = $newpos; |
542 | $instance->weight = $newweight; |
543 | |
544 | update_record('block_instance', $instance); |
0f3fe4b6 |
545 | } |
546 | |
9b4b78fd |
547 | function blocks_get_by_page($page) { |
f032aa7a |
548 | $blocks = get_records_select('block_instance', 'pageid = '. $page->get_id() .' AND pagetype = \''. $page->get_type() .'\'', 'position, weight'); |
549 | |
550 | $positions = $page->blocks_get_positions(); |
551 | $arr = array(); |
552 | foreach($positions as $key => $position) { |
553 | $arr[$position] = array(); |
554 | } |
0f3fe4b6 |
555 | |
9b4b78fd |
556 | if(empty($blocks)) { |
557 | return $arr; |
558 | } |
0f3fe4b6 |
559 | |
9b4b78fd |
560 | foreach($blocks as $block) { |
561 | $arr[$block->position][$block->weight] = $block; |
0f3fe4b6 |
562 | } |
563 | |
9b4b78fd |
564 | return $arr; |
565 | } |
0f3fe4b6 |
566 | |
9b4b78fd |
567 | //This function prints the block to admin blocks as necessary |
c1d8705f |
568 | function blocks_print_adminblock(&$page, &$pageblocks) { |
9b4b78fd |
569 | global $USER; |
0f3fe4b6 |
570 | |
c1d8705f |
571 | $missingblocks = blocks_get_missing($page, $pageblocks); |
572 | |
9b4b78fd |
573 | if (!empty($missingblocks)) { |
c1d8705f |
574 | $strblocks = get_string('blocks'); |
575 | $stradd = get_string('add'); |
9b4b78fd |
576 | foreach ($missingblocks as $blockid) { |
577 | $block = blocks_get_record($blockid); |
9b4b78fd |
578 | $blockobject = block_instance($block->name); |
579 | if ($blockobject === false) { |
580 | continue; |
581 | } |
582 | $menu[$block->id] = $blockobject->get_title(); |
583 | } |
0f3fe4b6 |
584 | |
f032aa7a |
585 | $target = $page->url_get_full(array('sesskey' => $USER->sesskey, 'blockaction' => 'add')); |
586 | $content = popup_form($target.'&blockid=', $menu, 'add_block', '', $stradd .'...', '', '', true); |
e1f76e76 |
587 | print_side_block($strblocks, $content, NULL, NULL, NULL, array('id' => 'block_adminblock')); |
0f3fe4b6 |
588 | } |
0f3fe4b6 |
589 | } |
590 | |
9b4b78fd |
591 | function blocks_repopulate_page($page) { |
592 | global $CFG; |
5b224948 |
593 | |
9b4b78fd |
594 | $allblocks = blocks_get_record(); |
595 | |
596 | if(empty($allblocks)) { |
597 | error('Could not retrieve blocks from the database'); |
598 | } |
599 | |
f032aa7a |
600 | // Assemble the information to correlate block names to ids |
9b4b78fd |
601 | $idforname = array(); |
602 | foreach($allblocks as $block) { |
603 | $idforname[$block->name] = $block->id; |
604 | } |
605 | |
f032aa7a |
606 | /// If the site override has been defined, it is the only valid one. |
607 | if (!empty($CFG->defaultblocks_override)) { |
608 | $blocknames = $CFG->defaultblocks_override; |
609 | } |
610 | else { |
611 | $blocknames = $page->blocks_get_default(); |
612 | } |
613 | |
614 | $positions = $page->blocks_get_positions(); |
615 | $posblocks = explode(':', $blocknames); |
616 | |
617 | // Now one array holds the names of the positions, and the other one holds the blocks |
618 | // that are going to go in each position. Luckily for us, both arrays are numerically |
619 | // indexed and the indexes match, so we can work straight away... but CAREFULLY! |
9b4b78fd |
620 | |
f032aa7a |
621 | // Ready to start creating block instances, but first drop any existing ones |
622 | delete_records('block_instance', 'pageid', $page->get_id(), 'pagetype', $page->get_type()); |
623 | |
624 | // Here we slyly count $posblocks and NOT $positions. This can actually make a difference |
625 | // if the textual representation has undefined slots in the end. So we only work with as many |
626 | // positions were retrieved, not with all the page says it has available. |
627 | $numpositions = count($posblocks); |
628 | for($i = 0; $i < $numpositions; ++$i) { |
629 | $position = $positions[$i]; |
630 | $blocknames = explode(',', $posblocks[$i]); |
9b4b78fd |
631 | $weight = 0; |
632 | foreach($blocknames as $blockname) { |
633 | $newinstance = new stdClass; |
634 | $newinstance->blockid = $idforname[$blockname]; |
f032aa7a |
635 | $newinstance->pageid = $page->get_id(); |
636 | $newinstance->pagetype = $page->get_type(); |
9b4b78fd |
637 | $newinstance->position = $position; |
638 | $newinstance->weight = $weight; |
639 | $newinstance->visible = 1; |
640 | $newinstance->configdata = ''; |
3cacefda |
641 | |
642 | if(!empty($newinstance->blockid)) { |
643 | // Only add block if it was recognized |
644 | insert_record('block_instance', $newinstance); |
645 | ++$weight; |
646 | } |
0f3fe4b6 |
647 | } |
648 | } |
9b4b78fd |
649 | |
650 | return true; |
0f3fe4b6 |
651 | } |
652 | |
653 | function upgrade_blocks_db($continueto) { |
654 | /// This function upgrades the blocks tables, if necessary |
655 | /// It's called from admin/index.php |
656 | |
657 | global $CFG, $db; |
658 | |
a9c75a9c |
659 | require_once ($CFG->dirroot .'/blocks/version.php'); // Get code versions |
0f3fe4b6 |
660 | |
661 | if (empty($CFG->blocks_version)) { // Blocks have never been installed. |
a9c75a9c |
662 | $strdatabaseupgrades = get_string('databaseupgrades'); |
0f3fe4b6 |
663 | print_header($strdatabaseupgrades, $strdatabaseupgrades, $strdatabaseupgrades, |
a9c75a9c |
664 | '', '', false, ' ', ' '); |
0f3fe4b6 |
665 | |
666 | $db->debug=true; |
a9c75a9c |
667 | if (modify_database($CFG->dirroot .'/blocks/db/'. $CFG->dbtype .'.sql')) { |
0f3fe4b6 |
668 | $db->debug = false; |
a9c75a9c |
669 | if (set_config('blocks_version', $blocks_version)) { |
c7a9e293 |
670 | notify(get_string('databasesuccess'), 'notifysuccess'); |
a9c75a9c |
671 | notify(get_string('databaseupgradeblocks', '', $blocks_version)); |
0f3fe4b6 |
672 | print_continue($continueto); |
673 | exit; |
674 | } else { |
a9c75a9c |
675 | error('Upgrade of blocks system failed! (Could not update version in config table)'); |
0f3fe4b6 |
676 | } |
677 | } else { |
a9c75a9c |
678 | error('Blocks tables could NOT be set up successfully!'); |
0f3fe4b6 |
679 | } |
680 | } |
681 | |
682 | |
683 | if ($blocks_version > $CFG->blocks_version) { // Upgrade tables |
a9c75a9c |
684 | $strdatabaseupgrades = get_string('databaseupgrades'); |
0f3fe4b6 |
685 | print_header($strdatabaseupgrades, $strdatabaseupgrades, $strdatabaseupgrades); |
686 | |
a9c75a9c |
687 | require_once ($CFG->dirroot .'/blocks/db/'. $CFG->dbtype .'.php'); |
0f3fe4b6 |
688 | |
689 | $db->debug=true; |
690 | if (blocks_upgrade($CFG->blocks_version)) { |
691 | $db->debug=false; |
a9c75a9c |
692 | if (set_config('blocks_version', $blocks_version)) { |
c7a9e293 |
693 | notify(get_string('databasesuccess'), 'notifysuccess'); |
a9c75a9c |
694 | notify(get_string('databaseupgradeblocks', '', $blocks_version)); |
0f3fe4b6 |
695 | print_continue($continueto); |
696 | exit; |
697 | } else { |
a9c75a9c |
698 | error('Upgrade of blocks system failed! (Could not update version in config table)'); |
0f3fe4b6 |
699 | } |
700 | } else { |
701 | $db->debug=false; |
a9c75a9c |
702 | error('Upgrade failed! See blocks/version.php'); |
0f3fe4b6 |
703 | } |
704 | |
705 | } else if ($blocks_version < $CFG->blocks_version) { |
a9c75a9c |
706 | notify('WARNING!!! The code you are using is OLDER than the version that made these databases!'); |
0f3fe4b6 |
707 | } |
708 | } |
709 | |
710 | //This function finds all available blocks and install them |
711 | //into blocks table or do all the upgrade process if newer |
712 | function upgrade_blocks_plugins($continueto) { |
713 | |
714 | global $CFG; |
715 | |
716 | $blocktitles = array(); |
717 | $invalidblocks = array(); |
718 | $validblocks = array(); |
719 | $notices = array(); |
720 | |
721 | //Count the number of blocks in db |
9b4b78fd |
722 | $blockcount = count_records('block'); |
0f3fe4b6 |
723 | //If there isn't records. This is the first install, so I remember it |
724 | if ($blockcount == 0) { |
725 | $first_install = true; |
726 | } else { |
727 | $first_install = false; |
728 | } |
729 | |
730 | $site = get_site(); |
731 | |
9b4b78fd |
732 | if (!$blocks = get_list_of_plugins('blocks', 'db') ) { |
a9c75a9c |
733 | error('No blocks installed!'); |
0f3fe4b6 |
734 | } |
735 | |
a9c75a9c |
736 | include_once($CFG->dirroot .'/blocks/moodleblock.class.php'); |
e89d741a |
737 | if(!class_exists('block_base')) { |
738 | error('Class block_base is not defined or file not found for /blocks/moodleblock.class.php'); |
0f3fe4b6 |
739 | } |
740 | |
741 | foreach ($blocks as $blockname) { |
742 | |
a9c75a9c |
743 | if ($blockname == 'NEWBLOCK') { // Someone has unzipped the template, ignore it |
0f3fe4b6 |
744 | continue; |
745 | } |
746 | |
a9c75a9c |
747 | $fullblock = $CFG->dirroot .'/blocks/'. $blockname; |
0f3fe4b6 |
748 | |
9b4b78fd |
749 | if ( is_readable($fullblock.'/block_'.$blockname.'.php')) { |
750 | include_once($fullblock.'/block_'.$blockname.'.php'); |
0f3fe4b6 |
751 | } else { |
a9c75a9c |
752 | $notices[] = 'Block '. $blockname .': '. $fullblock .'/block_'. $blockname .'.php was not readable'; |
0f3fe4b6 |
753 | continue; |
754 | } |
755 | |
a9c75a9c |
756 | if ( @is_dir($fullblock .'/db/')) { |
757 | if ( @is_readable($fullblock .'/db/'. $CFG->dbtype .'.php')) { |
89a5baab |
758 | include_once($fullblock .'/db/'. $CFG->dbtype .'.php'); // defines upgrading function |
828c4e09 |
759 | } else { |
a9c75a9c |
760 | $notices[] ='Block '. $blockname .': '. $fullblock .'/db/'. $CFG->dbtype .'.php was not readable'; |
828c4e09 |
761 | continue; |
762 | } |
0f3fe4b6 |
763 | } |
764 | |
e89d741a |
765 | $classname = 'block_'.$blockname; |
0f3fe4b6 |
766 | if(!class_exists($classname)) { |
a9c75a9c |
767 | $notices[] = 'Block '. $blockname .': '. $classname .' not implemented'; |
0f3fe4b6 |
768 | continue; |
769 | } |
770 | |
9b4b78fd |
771 | // Here is the place to see if the block implements a constructor (old style), |
772 | // an init() function (new style) or nothing at all (error time). |
773 | |
33bee34c |
774 | $constructor = get_class_constructor($classname); |
775 | if(empty($constructor)) { |
0f3fe4b6 |
776 | // No constructor |
a9c75a9c |
777 | $notices[] = 'Block '. $blockname .': class does not have a constructor'; |
0f3fe4b6 |
778 | $invalidblocks[] = $blockname; |
779 | continue; |
780 | } |
9b4b78fd |
781 | $methods = get_class_methods($classname); |
782 | if(!in_array('init', $methods)) { |
783 | // This is an old-style block |
a9c75a9c |
784 | $notices[] = 'Block '. $blockname .' is an old style block and needs to be updated by a programmer.'; |
9b4b78fd |
785 | $invalidblocks[] = $blockname; |
786 | continue; |
787 | } |
0f3fe4b6 |
788 | |
9b4b78fd |
789 | $block = new stdClass; // This may be used to update the db below |
790 | $blockobj = new $classname; // This is what we 'll be testing |
0f3fe4b6 |
791 | |
e89d741a |
792 | // Inherits from block_base? |
793 | if(!is_subclass_of($blockobj, 'block_base')) { |
794 | $notices[] = 'Block '. $blockname .': class does not inherit from block_base'; |
0f3fe4b6 |
795 | continue; |
796 | } |
797 | |
798 | // OK, it's as we all hoped. For further tests, the object will do them itself. |
799 | if(!$blockobj->_self_test()) { |
a9c75a9c |
800 | $notices[] = 'Block '. $blockname .': self test failed'; |
0f3fe4b6 |
801 | continue; |
802 | } |
803 | $block->version = $blockobj->get_version(); |
804 | |
805 | if (!isset($block->version)) { |
a9c75a9c |
806 | $notices[] = 'Block '. $blockname .': has no version support. It must be updated by a programmer.'; |
0f3fe4b6 |
807 | continue; |
808 | } |
809 | |
810 | $block->name = $blockname; // The name MUST match the directory |
811 | $blocktitle = $blockobj->get_title(); |
812 | |
9b4b78fd |
813 | if ($currblock = get_record('block', 'name', $block->name)) { |
0f3fe4b6 |
814 | if ($currblock->version == $block->version) { |
815 | // do nothing |
816 | } else if ($currblock->version < $block->version) { |
817 | if (empty($updated_blocks)) { |
a9c75a9c |
818 | $strblocksetup = get_string('blocksetup'); |
819 | print_header($strblocksetup, $strblocksetup, $strblocksetup, '', '', false, ' ', ' '); |
0f3fe4b6 |
820 | } |
b88f7004 |
821 | print_heading('New version of '.$blocktitle.' ('.$block->name.') exists'); |
822 | $upgrade_function = $block->name.'_upgrade'; |
0f3fe4b6 |
823 | if (function_exists($upgrade_function)) { |
824 | $db->debug=true; |
825 | if ($upgrade_function($currblock->version, $block)) { |
b88f7004 |
826 | |
827 | $upgradesuccess = true; |
0f3fe4b6 |
828 | } else { |
b88f7004 |
829 | $upgradesuccess = false; |
830 | } |
831 | $db->debug=false; |
832 | } |
833 | else { |
834 | $upgradesuccess = true; |
835 | } |
836 | if(!$upgradesuccess) { |
a9c75a9c |
837 | notify('Upgrading block '. $block->name .' from '. $currblock->version .' to '. $block->version .' FAILED!'); |
b88f7004 |
838 | } |
839 | else { |
9b4b78fd |
840 | // OK so far, now update the block record |
b88f7004 |
841 | $block->id = $currblock->id; |
9b4b78fd |
842 | if (! update_record('block', $block)) { |
a9c75a9c |
843 | error('Could not update block '. $block->name .' record in block table!'); |
0f3fe4b6 |
844 | } |
c7a9e293 |
845 | notify(get_string('blocksuccess', '', $blocktitle), 'notifysuccess'); |
b88f7004 |
846 | echo '<hr />'; |
0f3fe4b6 |
847 | } |
848 | $updated_blocks = true; |
849 | } else { |
a9c75a9c |
850 | error('Version mismatch: block '. $block->name .' can\'t downgrade '. $currblock->version .' -> '. $block->version .'!'); |
0f3fe4b6 |
851 | } |
852 | |
853 | } else { // block not installed yet, so install it |
854 | |
9b4b78fd |
855 | // If it allows multiples, start with it enabled |
856 | $block->multiple = $blockobj->instance_allow_multiple(); |
857 | |
0f3fe4b6 |
858 | // [pj] Normally this would be inline in the if, but we need to |
859 | // check for NULL (necessary for 4.0.5 <= PHP < 4.2.0) |
860 | $conflictblock = array_search($blocktitle, $blocktitles); |
861 | if($conflictblock !== false && $conflictblock !== NULL) { |
0f3fe4b6 |
862 | // Duplicate block titles are not allowed, they confuse people |
863 | // AND PHP's associative arrays ;) |
864 | error('<strong>Naming conflict</strong>: block <strong>'.$block->name.'</strong> has the same title with an existing block, <strong>'.$conflictblock.'</strong>!'); |
865 | } |
866 | if (empty($updated_blocks)) { |
a9c75a9c |
867 | $strblocksetup = get_string('blocksetup'); |
868 | print_header($strblocksetup, $strblocksetup, $strblocksetup, '', '', false, ' ', ' '); |
0f3fe4b6 |
869 | } |
870 | print_heading($block->name); |
871 | $updated_blocks = true; |
872 | $db->debug = true; |
a71bfa1c |
873 | @set_time_limit(0); // To allow slow databases to complete the long SQL |
a9c75a9c |
874 | if (!is_dir($fullblock .'/db/') || modify_database($fullblock .'/db/'. $CFG->dbtype .'.sql')) { |
0f3fe4b6 |
875 | $db->debug = false; |
9b4b78fd |
876 | if ($block->id = insert_record('block', $block)) { |
c7a9e293 |
877 | notify(get_string('blocksuccess', '', $blocktitle), 'notifysuccess'); |
a9c75a9c |
878 | echo '<hr />'; |
0f3fe4b6 |
879 | } else { |
a9c75a9c |
880 | error($block->name .' block could not be added to the block list!'); |
0f3fe4b6 |
881 | } |
882 | } else { |
a9c75a9c |
883 | error('Block '. $block->name .' tables could NOT be set up successfully!'); |
0f3fe4b6 |
884 | } |
885 | } |
886 | |
887 | $blocktitles[$block->name] = $blocktitle; |
888 | } |
889 | |
890 | if(!empty($notices)) { |
891 | foreach($notices as $notice) { |
892 | notify($notice); |
893 | } |
894 | } |
895 | |
9b4b78fd |
896 | // Finally, if we are in the first_install of BLOCKS (this means that we are |
897 | // upgrading from Moodle < 1.3), put blocks in all existing courses. |
0f3fe4b6 |
898 | if ($first_install) { |
899 | //Iterate over each course |
9b4b78fd |
900 | if ($courses = get_records('course')) { |
0f3fe4b6 |
901 | foreach ($courses as $course) { |
7542a4e5 |
902 | $page = page_create_object(PAGE_COURSE_VIEW, $course->id); |
9b4b78fd |
903 | blocks_repopulate_page($page); |
0f3fe4b6 |
904 | } |
905 | } |
906 | } |
907 | |
e02c35b2 |
908 | if (!empty($CFG->siteblocksadded)) { /// This is a once-off hack to make a proper upgrade |
7542a4e5 |
909 | $page = page_create_object(PAGE_COURSE_VIEW, SITEID); |
9b4b78fd |
910 | blocks_repopulate_page($page); |
e02c35b2 |
911 | delete_records('config', 'name', 'siteblocksadded'); |
912 | } |
913 | |
0f3fe4b6 |
914 | if (!empty($updated_blocks)) { |
915 | print_continue($continueto); |
916 | die; |
917 | } |
918 | } |
919 | |
0f3fe4b6 |
920 | ?> |