PageRenderTime 87ms CodeModel.GetById 32ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/blocklib.php

https://bitbucket.org/ceu/moodle_demo
PHP | 1465 lines | 1066 code | 210 blank | 189 comment | 293 complexity | 014923efb6734d7f7402affe7c42cb8a MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.0, LGPL-2.1

Large files files are truncated, but you can click here to view the full file

  1. <?php //$Id: blocklib.php,v 1.129.2.12 2010/08/19 14:06:03 sam_marshall Exp $
  2. //This library includes all the necessary stuff to use blocks in course pages
  3. define('BLOCK_MOVE_LEFT', 0x01);
  4. define('BLOCK_MOVE_RIGHT', 0x02);
  5. define('BLOCK_MOVE_UP', 0x04);
  6. define('BLOCK_MOVE_DOWN', 0x08);
  7. define('BLOCK_CONFIGURE', 0x10);
  8. define('BLOCK_POS_LEFT', 'l');
  9. define('BLOCK_POS_RIGHT', 'r');
  10. define('BLOCKS_PINNED_TRUE',0);
  11. define('BLOCKS_PINNED_FALSE',1);
  12. define('BLOCKS_PINNED_BOTH',2);
  13. require_once($CFG->libdir.'/pagelib.php');
  14. require_once($CFG->dirroot.'/course/lib.php'); // needed to solve all those: Call to undefined function: print_recent_activity() when adding Recent Activity
  15. // Returns false if this block is incompatible with the current version of Moodle.
  16. function block_is_compatible($blockname) {
  17. global $CFG;
  18. $file = @file($CFG->dirroot.'/blocks/'.$blockname.'/block_'.$blockname.'.php'); // ignore errors when file does not exist
  19. if(empty($file)) {
  20. return NULL;
  21. }
  22. foreach($file as $line) {
  23. // If you find MoodleBlock (appearing in the class declaration) it's not compatible
  24. if(strpos($line, 'MoodleBlock')) {
  25. return false;
  26. }
  27. // But if we find a { it means the class declaration is over, so it's compatible
  28. else if(strpos($line, '{')) {
  29. return true;
  30. }
  31. }
  32. return NULL;
  33. }
  34. // Returns the case-sensitive name of the class' constructor function. This includes both
  35. // PHP5- and PHP4-style constructors. If no appropriate constructor can be found, returns NULL.
  36. // If there is no such class, returns boolean false.
  37. function get_class_constructor($classname) {
  38. // Caching
  39. static $constructors = array();
  40. if(!class_exists($classname)) {
  41. return false;
  42. }
  43. // Tests indicate this doesn't hurt even in PHP5.
  44. $classname = strtolower($classname);
  45. // Return cached value, if exists
  46. if(isset($constructors[$classname])) {
  47. return $constructors[$classname];
  48. }
  49. // Get a list of methods. After examining several different ways of
  50. // doing the check, (is_callable, method_exists, function_exists etc)
  51. // it seems that this is the most reliable one.
  52. $methods = get_class_methods($classname);
  53. // PHP5 constructor?
  54. if(phpversion() >= '5') {
  55. if(in_array('__construct', $methods)) {
  56. return $constructors[$classname] = '__construct';
  57. }
  58. }
  59. // If we have PHP5 but no magic constructor, we have to lowercase the methods
  60. $methods = array_map('strtolower', $methods);
  61. if(in_array($classname, $methods)) {
  62. return $constructors[$classname] = $classname;
  63. }
  64. return $constructors[$classname] = NULL;
  65. }
  66. //This function retrieves a method-defined property of a class WITHOUT instantiating an object
  67. function block_method_result($blockname, $method, $param = NULL) {
  68. if(!block_load_class($blockname)) {
  69. return NULL;
  70. }
  71. return call_user_func(array('block_'.$blockname, $method), $param);
  72. }
  73. //This function creates a new object of the specified block class
  74. function block_instance($blockname, $instance = NULL) {
  75. if(!block_load_class($blockname)) {
  76. return false;
  77. }
  78. $classname = 'block_'.$blockname;
  79. $retval = new $classname;
  80. if($instance !== NULL) {
  81. $retval->_load_instance($instance);
  82. }
  83. return $retval;
  84. }
  85. //This function loads the necessary class files for a block
  86. //Whenever you want to load a block, use this first
  87. function block_load_class($blockname) {
  88. global $CFG;
  89. if(empty($blockname)) {
  90. return false;
  91. }
  92. $classname = 'block_'.$blockname;
  93. if(class_exists($classname)) {
  94. return true;
  95. }
  96. require_once($CFG->dirroot.'/blocks/moodleblock.class.php');
  97. @include_once($CFG->dirroot.'/blocks/'.$blockname.'/block_'.$blockname.'.php'); // do not throw errors if block code not present
  98. return class_exists($classname);
  99. }
  100. // This function returns an array with the IDs of any blocks that you can add to your page.
  101. // Parameters are passed by reference for speed; they are not modified at all.
  102. function blocks_get_missing(&$page, &$pageblocks) {
  103. $missingblocks = array();
  104. $allblocks = blocks_get_record();
  105. $pageformat = $page->get_format_name();
  106. if(!empty($allblocks)) {
  107. foreach($allblocks as $block) {
  108. if($block->visible && (!blocks_find_block($block->id, $pageblocks) || $block->multiple)) {
  109. // And if it's applicable for display in this format...
  110. if(blocks_name_allowed_in_format($block->name, $pageformat)) {
  111. // ...add it to the missing blocks
  112. $missingblocks[] = $block->id;
  113. }
  114. }
  115. }
  116. }
  117. return $missingblocks;
  118. }
  119. function blocks_remove_inappropriate($page) {
  120. $pageblocks = blocks_get_by_page($page);
  121. if(empty($pageblocks)) {
  122. return;
  123. }
  124. if(($pageformat = $page->get_format_name()) == NULL) {
  125. return;
  126. }
  127. foreach($pageblocks as $position) {
  128. foreach($position as $instance) {
  129. $block = blocks_get_record($instance->blockid);
  130. if(!blocks_name_allowed_in_format($block->name, $pageformat)) {
  131. blocks_delete_instance($instance);
  132. }
  133. }
  134. }
  135. }
  136. function blocks_name_allowed_in_format($name, $pageformat) {
  137. $accept = NULL;
  138. $depth = -1;
  139. if ($formats = block_method_result($name, 'applicable_formats')) {
  140. foreach($formats as $format => $allowed) {
  141. $thisformat = '^'.str_replace('*', '[^-]*', $format).'.*$';
  142. if(ereg($thisformat, $pageformat)) {
  143. if(($scount = substr_count($format, '-')) > $depth) {
  144. $depth = $scount;
  145. $accept = $allowed;
  146. }
  147. }
  148. }
  149. }
  150. if($accept === NULL) {
  151. $accept = !empty($formats['all']);
  152. }
  153. return $accept;
  154. }
  155. function blocks_delete_instance($instance,$pinned=false) {
  156. global $CFG;
  157. // Get the block object and call instance_delete() if possible
  158. if($record = blocks_get_record($instance->blockid)) {
  159. if($obj = block_instance($record->name, $instance)) {
  160. // Return value ignored
  161. $obj->instance_delete();
  162. }
  163. }
  164. if (!empty($pinned)) {
  165. delete_records('block_pinned', 'id', $instance->id);
  166. // And now, decrement the weight of all blocks after this one
  167. execute_sql('UPDATE '.$CFG->prefix.'block_pinned SET weight = weight - 1 WHERE pagetype = \''.$instance->pagetype.
  168. '\' AND position = \''.$instance->position.
  169. '\' AND weight > '.$instance->weight, false);
  170. } else {
  171. // Now kill the db record;
  172. delete_records('block_instance', 'id', $instance->id);
  173. delete_context(CONTEXT_BLOCK, $instance->id);
  174. // And now, decrement the weight of all blocks after this one
  175. execute_sql('UPDATE '.$CFG->prefix.'block_instance SET weight = weight - 1 WHERE pagetype = \''.$instance->pagetype.
  176. '\' AND pageid = '.$instance->pageid.' AND position = \''.$instance->position.
  177. '\' AND weight > '.$instance->weight, false);
  178. }
  179. return true;
  180. }
  181. // Accepts an array of block instances and checks to see if any of them have content to display
  182. // (causing them to calculate their content in the process). Returns true or false. Parameter passed
  183. // by reference for speed; the array is actually not modified.
  184. function blocks_have_content(&$pageblocks, $position) {
  185. if (empty($pageblocks) || !is_array($pageblocks) || !array_key_exists($position,$pageblocks)) {
  186. return false;
  187. }
  188. // use a for() loop to get references to the array elements
  189. // foreach() cannot fetch references in PHP v4.x
  190. for ($n=0; $n<count($pageblocks[$position]);$n++) {
  191. $instance = &$pageblocks[$position][$n];
  192. if (empty($instance->visible)) {
  193. continue;
  194. }
  195. if(!$record = blocks_get_record($instance->blockid)) {
  196. continue;
  197. }
  198. if (empty($record->visible)) {
  199. continue;
  200. }
  201. if(!$obj = block_instance($record->name, $instance)) {
  202. continue;
  203. }
  204. if(!$obj->is_empty()) {
  205. // cache rec and obj
  206. // for blocks_print_group()
  207. $instance->rec = $record;
  208. $instance->obj = $obj;
  209. return true;
  210. }
  211. }
  212. return false;
  213. }
  214. // This function prints one group of blocks in a page
  215. // Parameters passed by reference for speed; they are not modified.
  216. function blocks_print_group(&$page, &$pageblocks, $position) {
  217. global $COURSE, $CFG, $USER;
  218. $isediting = $page->user_is_editing();
  219. if (empty($pageblocks[$position])) {
  220. $groupblocks = array();
  221. $maxweight = 0;
  222. } else {
  223. $groupblocks = $pageblocks[$position];
  224. $maxweight = max(array_keys($groupblocks));
  225. }
  226. if (!empty($CFG->ajaxcapable) && $CFG->ajaxcapable && !empty($COURSE->javascriptportal) && $isediting) {
  227. $COURSE->javascriptportal->currentblocksection = $position;
  228. $COURSE->javascriptportal->block_add($position.'inst0', FALSE);
  229. }
  230. foreach ($groupblocks as $instance) {
  231. if (!empty($instance->pinned)) {
  232. $maxweight--;
  233. }
  234. }
  235. foreach($groupblocks as $instance) {
  236. // $instance may have ->rec and ->obj
  237. // cached from when we walked $pageblocks
  238. // in blocks_have_content()
  239. if (empty($instance->rec)) {
  240. if (empty($instance->blockid)) {
  241. continue; // Can't do anything
  242. }
  243. $block = blocks_get_record($instance->blockid);
  244. } else {
  245. $block = $instance->rec;
  246. }
  247. if (empty($block)) {
  248. // Block doesn't exist! We should delete this instance!
  249. continue;
  250. }
  251. if (empty($block->visible)) {
  252. // Disabled by the admin
  253. continue;
  254. }
  255. if (empty($instance->obj)) {
  256. if (!$obj = block_instance($block->name, $instance)) {
  257. // Invalid block
  258. continue;
  259. }
  260. } else {
  261. $obj = $instance->obj;
  262. }
  263. $editalways = $page->edit_always();
  264. if (($isediting && empty($instance->pinned)) || !empty($editalways)) {
  265. $options = 0;
  266. // 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:
  267. // the first block might still be able to move up if the page says so (i.e., it will change position)
  268. $options |= BLOCK_MOVE_UP * ($instance->weight != 0 || ($page->blocks_move_position($instance, BLOCK_MOVE_UP) != $instance->position));
  269. // Same thing for downward movement
  270. $options |= BLOCK_MOVE_DOWN * ($instance->weight != $maxweight || ($page->blocks_move_position($instance, BLOCK_MOVE_DOWN) != $instance->position));
  271. // For left and right movements, it's up to the page to tell us whether they are allowed
  272. $options |= BLOCK_MOVE_RIGHT * ($page->blocks_move_position($instance, BLOCK_MOVE_RIGHT) != $instance->position);
  273. $options |= BLOCK_MOVE_LEFT * ($page->blocks_move_position($instance, BLOCK_MOVE_LEFT ) != $instance->position);
  274. // Finally, the block can be configured if the block class either allows multiple instances, or if it specifically
  275. // allows instance configuration (multiple instances override that one). It doesn't have anything to do with what the
  276. // administrator has allowed for this block in the site admin options.
  277. $options |= BLOCK_CONFIGURE * ( $obj->instance_allow_multiple() || $obj->instance_allow_config() );
  278. $obj->_add_edit_controls($options);
  279. }
  280. if (!$instance->visible && empty($COURSE->javascriptportal)) {
  281. if ($isediting) {
  282. $obj->_print_shadow();
  283. }
  284. } else {
  285. global $COURSE;
  286. if(!empty($COURSE->javascriptportal)) {
  287. $COURSE->javascriptportal->currentblocksection = $position;
  288. }
  289. $obj->_print_block();
  290. }
  291. if (!empty($COURSE->javascriptportal)
  292. && (empty($instance->pinned) || !$instance->pinned)) {
  293. $COURSE->javascriptportal->block_add('inst'.$instance->id, !$instance->visible);
  294. }
  295. } // End foreach
  296. // Check if
  297. // we are on the default position/side AND
  298. // we're editing the page AND
  299. // (
  300. // we have the capability to manage blocks OR
  301. // we are in myMoodle page AND have the capibility to manage myMoodle blocks
  302. // )
  303. // for constant PAGE_MY_MOODLE
  304. include_once($CFG->dirroot.'/my/pagelib.php');
  305. $coursecontext = get_context_instance(CONTEXT_COURSE, $COURSE->id);
  306. $myownblogpage = (isset($page->filtertype) && isset($page->filterselect) && $page->type=='blog-view' && $page->filtertype=='user' && $page->filterselect == $USER->id);
  307. $managecourseblocks = has_capability('moodle/site:manageblocks', $coursecontext);
  308. $editmymoodle = $page->type == PAGE_MY_MOODLE && has_capability('moodle/my:manageblocks', $coursecontext);
  309. if ($page->blocks_default_position() == $position &&
  310. $page->user_is_editing() &&
  311. ($managecourseblocks || $editmymoodle || $myownblogpage || defined('ADMIN_STICKYBLOCKS'))) {
  312. print_side_block(NULL,NULL, NULL, NULL, NULL, array('id'=> BLOCK_POS_RIGHT.'inst0', 'class'=>'tempblockhandler'));
  313. blocks_print_adminblock($page, $pageblocks);
  314. } else if ($page->user_is_editing() &&
  315. ($managecourseblocks || $editmymoodle || $myownblogpage || defined('ADMIN_STICKYBLOCKS'))) {
  316. print_side_block(NULL,NULL, NULL, NULL, NULL, array('id'=> BLOCK_POS_LEFT.'inst0', 'class'=>'tempblockhandler'));
  317. }
  318. }
  319. // This iterates over an array of blocks and calculates the preferred width
  320. // Parameter passed by reference for speed; it's not modified.
  321. function blocks_preferred_width(&$instances) {
  322. $width = 0;
  323. if(empty($instances) || !is_array($instances)) {
  324. return 0;
  325. }
  326. $blocks = blocks_get_record();
  327. foreach($instances as $instance) {
  328. if(!$instance->visible) {
  329. continue;
  330. }
  331. if (!array_key_exists($instance->blockid, $blocks)) {
  332. // Block doesn't exist! We should delete this instance!
  333. continue;
  334. }
  335. if(!$blocks[$instance->blockid]->visible) {
  336. continue;
  337. }
  338. $pref = block_method_result($blocks[$instance->blockid]->name, 'preferred_width');
  339. if($pref === NULL) {
  340. continue;
  341. }
  342. if($pref > $width) {
  343. $width = $pref;
  344. }
  345. }
  346. return $width;
  347. }
  348. function blocks_get_record($blockid = NULL, $invalidate = false) {
  349. static $cache = NULL;
  350. if($invalidate || empty($cache)) {
  351. $cache = get_records('block');
  352. }
  353. if($blockid === NULL) {
  354. return $cache;
  355. }
  356. return (isset($cache[$blockid])? $cache[$blockid] : false);
  357. }
  358. function blocks_find_block($blockid, $blocksarray) {
  359. if (empty($blocksarray)) {
  360. return false;
  361. }
  362. foreach($blocksarray as $blockgroup) {
  363. if (empty($blockgroup)) {
  364. continue;
  365. }
  366. foreach($blockgroup as $instance) {
  367. if($instance->blockid == $blockid) {
  368. return $instance;
  369. }
  370. }
  371. }
  372. return false;
  373. }
  374. function blocks_find_instance($instanceid, $blocksarray) {
  375. foreach($blocksarray as $subarray) {
  376. foreach($subarray as $instance) {
  377. if($instance->id == $instanceid) {
  378. return $instance;
  379. }
  380. }
  381. }
  382. return false;
  383. }
  384. // Simple entry point for anyone that wants to use blocks
  385. function blocks_setup(&$PAGE,$pinned=BLOCKS_PINNED_FALSE) {
  386. switch ($pinned) {
  387. case BLOCKS_PINNED_TRUE:
  388. $pageblocks = blocks_get_pinned($PAGE);
  389. break;
  390. case BLOCKS_PINNED_BOTH:
  391. $pageblocks = blocks_get_by_page_pinned($PAGE);
  392. break;
  393. case BLOCKS_PINNED_FALSE:
  394. default:
  395. $pageblocks = blocks_get_by_page($PAGE);
  396. break;
  397. }
  398. blocks_execute_url_action($PAGE, $pageblocks,($pinned==BLOCKS_PINNED_TRUE));
  399. return $pageblocks;
  400. }
  401. function blocks_execute_action($page, &$pageblocks, $blockaction, $instanceorid, $pinned=false, $redirect=true) {
  402. global $CFG;
  403. if (is_int($instanceorid)) {
  404. $blockid = $instanceorid;
  405. } else if (is_object($instanceorid)) {
  406. $instance = $instanceorid;
  407. }
  408. switch($blockaction) {
  409. case 'config':
  410. global $USER;
  411. $block = blocks_get_record($instance->blockid);
  412. // Hacky hacky tricky stuff to get the original human readable block title,
  413. // even if the block has configured its title to be something else.
  414. // Create the object WITHOUT instance data.
  415. $blockobject = block_instance($block->name);
  416. if ($blockobject === false) {
  417. break;
  418. }
  419. // First of all check to see if the block wants to be edited
  420. if(!$blockobject->user_can_edit()) {
  421. break;
  422. }
  423. // Now get the title and AFTER that load up the instance
  424. $blocktitle = $blockobject->get_title();
  425. $blockobject->_load_instance($instance);
  426. optional_param('submitted', 0, PARAM_INT);
  427. // Define the data we're going to silently include in the instance config form here,
  428. // so we can strip them from the submitted data BEFORE serializing it.
  429. $hiddendata = array(
  430. 'sesskey' => $USER->sesskey,
  431. 'instanceid' => $instance->id,
  432. 'blockaction' => 'config'
  433. );
  434. // To this data, add anything the page itself needs to display
  435. $hiddendata = array_merge($hiddendata, $page->url_get_parameters());
  436. if ($data = data_submitted()) {
  437. $remove = array_keys($hiddendata);
  438. foreach($remove as $item) {
  439. unset($data->$item);
  440. }
  441. if(!$blockobject->instance_config_save($data,$pinned)) {
  442. error('Error saving block configuration');
  443. }
  444. // And nothing more, continue with displaying the page
  445. }
  446. else {
  447. // We need to show the config screen, so we highjack the display logic and then die
  448. $strheading = get_string('blockconfiga', 'moodle', $blocktitle);
  449. $page->print_header(get_string('pageheaderconfigablock', 'moodle'), array($strheading => ''));
  450. echo '<div class="block-config" id="'.$block->name.'">'; /// Make CSS easier
  451. print_heading($strheading);
  452. echo '<form method="post" name="block-config" action="'. $page->url_get_path() .'">';
  453. echo '<p>';
  454. foreach($hiddendata as $name => $val) {
  455. echo '<input type="hidden" name="'. $name .'" value="'. $val .'" />';
  456. }
  457. echo '</p>';
  458. $blockobject->instance_config_print();
  459. echo '</form>';
  460. echo '</div>';
  461. $CFG->pagepath = 'blocks/' . $block->name;
  462. print_footer();
  463. die(); // Do not go on with the other page-related stuff
  464. }
  465. break;
  466. case 'toggle':
  467. if(empty($instance)) {
  468. error('Invalid block instance for '.$blockaction);
  469. }
  470. $instance->visible = ($instance->visible) ? 0 : 1;
  471. if (!empty($pinned)) {
  472. update_record('block_pinned', $instance);
  473. } else {
  474. update_record('block_instance', $instance);
  475. }
  476. break;
  477. case 'delete':
  478. if(empty($instance)) {
  479. error('Invalid block instance for '. $blockaction);
  480. }
  481. blocks_delete_instance($instance, $pinned);
  482. break;
  483. case 'moveup':
  484. if (empty($instance)) {
  485. error('Invalid block instance for '. $blockaction);
  486. }
  487. if ($instance->weight == 0) {
  488. // The block is the first one, so a move "up" probably means it changes position
  489. // Where is the instance going to be moved?
  490. $newpos = $page->blocks_move_position($instance, BLOCK_MOVE_UP);
  491. $newweight = (empty($pageblocks[$newpos]) ? 0 : max(array_keys($pageblocks[$newpos])) + 1);
  492. blocks_execute_repositioning($instance, $newpos, $newweight, $pinned);
  493. } else {
  494. // The block is just moving upwards in the same position.
  495. // This configuration will make sure that even if somehow the weights
  496. // become not continuous, block move operations will eventually bring
  497. // the situation back to normal without printing any warnings.
  498. if (!empty($pageblocks[$instance->position][$instance->weight - 1])) {
  499. //define instance's position in the array
  500. foreach ($pageblocks[$instance->position] as $instancekeysindex => $index ){
  501. if ($pageblocks[$instance->position][$instancekeysindex]->id == $instance->id){
  502. $instanceindex = $instancekeysindex;
  503. }
  504. }
  505. $other = $pageblocks[$instance->position][$instanceindex - 1];
  506. }
  507. if (!empty($other)) {
  508. ++$other->weight;
  509. if (!empty($pinned)) {
  510. update_record('block_pinned', $other);
  511. } else {
  512. update_record('block_instance', $other);
  513. }
  514. }
  515. --$instance->weight;
  516. if (!empty($pinned)) {
  517. update_record('block_pinned', $instance);
  518. } else {
  519. update_record('block_instance', $instance);
  520. }
  521. }
  522. break;
  523. case 'movedown':
  524. if (empty($instance)) {
  525. error('Invalid block instance for '. $blockaction);
  526. }
  527. if ($instance->weight == max(array_keys($pageblocks[$instance->position]))) {
  528. // The block is the last one, so a move "down" probably means it changes position
  529. // Where is the instance going to be moved?
  530. $newpos = $page->blocks_move_position($instance, BLOCK_MOVE_DOWN);
  531. $newweight = (empty($pageblocks[$newpos]) ? 0 : max(array_keys($pageblocks[$newpos])) + 1);
  532. blocks_execute_repositioning($instance, $newpos, $newweight, $pinned);
  533. }
  534. else {
  535. // The block is just moving downwards in the same position.
  536. // This configuration will make sure that even if somehow the weights
  537. // become not continuous, block move operations will eventually bring
  538. // the situation back to normal without printing any warnings.
  539. if (!empty($pageblocks[$instance->position][$instance->weight + 1])) {
  540. //define instance's position in the array
  541. foreach ($pageblocks[$instance->position] as $instancekeysindex => $index ){
  542. if ($pageblocks[$instance->position][$instancekeysindex]->id == $instance->id){
  543. $instanceindex = $instancekeysindex;
  544. }
  545. }
  546. $other = $pageblocks[$instance->position][$instanceindex + 1];
  547. }
  548. if (!empty($other)) {
  549. --$other->weight;
  550. if (!empty($pinned)) {
  551. update_record('block_pinned', $other);
  552. } else {
  553. update_record('block_instance', $other);
  554. }
  555. }
  556. ++$instance->weight;
  557. if (!empty($pinned)) {
  558. update_record('block_pinned', $instance);
  559. } else {
  560. update_record('block_instance', $instance);
  561. }
  562. }
  563. break;
  564. case 'moveleft':
  565. if(empty($instance)) {
  566. error('Invalid block instance for '. $blockaction);
  567. }
  568. // Where is the instance going to be moved?
  569. $newpos = $page->blocks_move_position($instance, BLOCK_MOVE_LEFT);
  570. $newweight = 0;
  571. if (!empty($pinned) && !empty($pageblocks[$newpos]) ){
  572. $newweight = $pageblocks[$newpos][max(array_keys($pageblocks[$newpos])) ]->weight + 1;
  573. } else if(!empty($pageblocks[$newpos]) && (!array_key_exists('pinned', $pageblocks[$newpos][max(array_keys($pageblocks[$newpos]))])) ){
  574. $newweight = $pageblocks[$newpos][max(array_keys($pageblocks[$newpos])) ]->weight + 1;
  575. }
  576. blocks_execute_repositioning($instance, $newpos, $newweight, $pinned);
  577. break;
  578. case 'moveright':
  579. if(empty($instance)) {
  580. error('Invalid block instance for '. $blockaction);
  581. }
  582. // Where is the instance going to be moved?
  583. $newpos = $page->blocks_move_position($instance, BLOCK_MOVE_RIGHT);
  584. $newweight = 0;
  585. if (!empty($pinned) && !empty($pageblocks[$newpos]) ){
  586. $newweight = $pageblocks[$newpos][max(array_keys($pageblocks[$newpos])) ]->weight + 1;
  587. }else if(!empty($pageblocks[$newpos]) && (!array_key_exists('pinned', $pageblocks[$newpos][max(array_keys($pageblocks[$newpos]))])) ){
  588. $newweight = $pageblocks[$newpos][max(array_keys($pageblocks[$newpos])) ]->weight + 1;
  589. }
  590. blocks_execute_repositioning($instance, $newpos, $newweight, $pinned);
  591. break;
  592. case 'add':
  593. // Add a new instance of this block, if allowed
  594. $block = blocks_get_record($blockid);
  595. if(empty($block) || !$block->visible) {
  596. // Only allow adding if the block exists and is enabled
  597. break;
  598. }
  599. if(!$block->multiple && blocks_find_block($blockid, $pageblocks) !== false) {
  600. // If no multiples are allowed and we already have one, return now
  601. break;
  602. }
  603. if(!block_method_result($block->name, 'user_can_addto', $page)) {
  604. // If the block doesn't want to be added...
  605. break;
  606. }
  607. $newpos = $page->blocks_default_position();
  608. if (!empty($pinned)) {
  609. $sql = 'SELECT 1, max(weight) + 1 AS nextfree FROM '. $CFG->prefix .'block_pinned WHERE '
  610. .' pagetype = \''. $page->get_type() .'\' AND position = \''. $newpos .'\'';
  611. } else {
  612. $sql = 'SELECT 1, max(weight) + 1 AS nextfree FROM '. $CFG->prefix .'block_instance WHERE pageid = '. $page->get_id()
  613. .' AND pagetype = \''. $page->get_type() .'\' AND position = \''. $newpos .'\'';
  614. }
  615. $weight = get_record_sql($sql);
  616. $newinstance = new stdClass;
  617. $newinstance->blockid = $blockid;
  618. if (empty($pinned)) {
  619. $newinstance->pageid = $page->get_id();
  620. }
  621. $newinstance->pagetype = $page->get_type();
  622. $newinstance->position = $newpos;
  623. $newinstance->weight = empty($weight->nextfree) ? 0 : $weight->nextfree;
  624. $newinstance->visible = 1;
  625. $newinstance->configdata = '';
  626. if (!empty($pinned)) {
  627. $newinstance->id = insert_record('block_pinned', $newinstance);
  628. } else {
  629. $newinstance->id = insert_record('block_instance', $newinstance);
  630. }
  631. // If the new instance was created, allow it to do additional setup
  632. if($newinstance && ($obj = block_instance($block->name, $newinstance))) {
  633. // Return value ignored
  634. $obj->instance_create();
  635. }
  636. break;
  637. }
  638. if ($redirect) {
  639. // In order to prevent accidental duplicate actions, redirect to a page with a clean url
  640. redirect($page->url_get_full());
  641. }
  642. }
  643. // You can use this to get the blocks to respond to URL actions without much hassle
  644. function blocks_execute_url_action(&$PAGE, &$pageblocks,$pinned=false) {
  645. $blockaction = optional_param('blockaction', '', PARAM_ALPHA);
  646. if (empty($blockaction) || !$PAGE->user_allowed_editing() || !confirm_sesskey()) {
  647. return;
  648. }
  649. $instanceid = optional_param('instanceid', 0, PARAM_INT);
  650. $blockid = optional_param('blockid', 0, PARAM_INT);
  651. if (!empty($blockid)) {
  652. blocks_execute_action($PAGE, $pageblocks, strtolower($blockaction), $blockid, $pinned);
  653. }
  654. else if (!empty($instanceid)) {
  655. $instance = blocks_find_instance($instanceid, $pageblocks);
  656. blocks_execute_action($PAGE, $pageblocks, strtolower($blockaction), $instance, $pinned);
  657. }
  658. }
  659. // This shouldn't be used externally at all, it's here for use by blocks_execute_action()
  660. // in order to reduce code repetition.
  661. function blocks_execute_repositioning(&$instance, $newpos, $newweight, $pinned=false) {
  662. global $CFG;
  663. // If it's staying where it is, don't do anything, unless overridden
  664. if ($newpos == $instance->position) {
  665. return;
  666. }
  667. // Close the weight gap we 'll leave behind
  668. if (!empty($pinned)) {
  669. $sql = 'UPDATE '. $CFG->prefix .'block_pinned SET weight = weight - 1 '.
  670. 'WHERE pagetype = \''. $instance->pagetype.
  671. '\' AND position = \'' .$instance->position.
  672. '\' AND weight > '. $instance->weight;
  673. } else {
  674. $sql = 'UPDATE '. $CFG->prefix .'block_instance SET weight = weight - 1 '.
  675. 'WHERE pagetype = \''. $instance->pagetype.
  676. '\' AND pageid = '. $instance->pageid .
  677. ' AND position = \'' .$instance->position.
  678. '\' AND weight > '. $instance->weight;
  679. }
  680. execute_sql($sql,false);
  681. $instance->position = $newpos;
  682. $instance->weight = $newweight;
  683. if (!empty($pinned)) {
  684. update_record('block_pinned', $instance);
  685. } else {
  686. update_record('block_instance', $instance);
  687. }
  688. }
  689. /**
  690. * Moves a block to the new position (column) and weight (sort order).
  691. * @param $instance - The block instance to be moved.
  692. * @param $destpos - BLOCK_POS_LEFT or BLOCK_POS_RIGHT. The destination column.
  693. * @param $destweight - The destination sort order. If NULL, we add to the end
  694. * of the destination column.
  695. * @param $pinned - Are we moving pinned blocks? We can only move pinned blocks
  696. * to a new position withing the pinned list. Likewise, we
  697. * can only moved non-pinned blocks to a new position within
  698. * the non-pinned list.
  699. * @return boolean (success or failure).
  700. */
  701. function blocks_move_block($page, &$instance, $destpos, $destweight=NULL, $pinned=false) {
  702. global $CFG;
  703. if ($pinned) {
  704. $blocklist = blocks_get_pinned($page);
  705. } else {
  706. $blocklist = blocks_get_by_page($page);
  707. }
  708. if ($blocklist[$instance->position][$instance->weight]->id != $instance->id) {
  709. // The source block instance is not where we think it is.
  710. return false;
  711. }
  712. // First we close the gap that will be left behind when we take out the
  713. // block from it's current column.
  714. if ($pinned) {
  715. $closegapsql = "UPDATE {$CFG->prefix}block_pinned
  716. SET weight = weight - 1
  717. WHERE weight > '$instance->weight'
  718. AND position = '$instance->position'
  719. AND pagetype = '$instance->pagetype'";
  720. } else {
  721. $closegapsql = "UPDATE {$CFG->prefix}block_instance
  722. SET weight = weight - 1
  723. WHERE weight > '$instance->weight'
  724. AND position = '$instance->position'
  725. AND pagetype = '$instance->pagetype'
  726. AND pageid = '$instance->pageid'";
  727. }
  728. if (!execute_sql($closegapsql, false)) {
  729. return false;
  730. }
  731. // Now let's make space for the block being moved.
  732. if ($pinned) {
  733. $opengapsql = "UPDATE {$CFG->prefix}block_pinned
  734. SET weight = weight + 1
  735. WHERE weight >= '$destweight'
  736. AND position = '$destpos'
  737. AND pagetype = '$instance->pagetype'";
  738. } else {
  739. $opengapsql = "UPDATE {$CFG->prefix}block_instance
  740. SET weight = weight + 1
  741. WHERE weight >= '$destweight'
  742. AND position = '$destpos'
  743. AND pagetype = '$instance->pagetype'
  744. AND pageid = '$instance->pageid'";
  745. }
  746. if (!execute_sql($opengapsql, false)) {
  747. return false;
  748. }
  749. // Move the block.
  750. $instance->position = $destpos;
  751. $instance->weight = $destweight;
  752. if ($pinned) {
  753. $table = 'block_pinned';
  754. } else {
  755. $table = 'block_instance';
  756. }
  757. return update_record($table, $instance);
  758. }
  759. /**
  760. * Returns an array consisting of 2 arrays:
  761. * 1) Array of pinned blocks for position BLOCK_POS_LEFT
  762. * 2) Array of pinned blocks for position BLOCK_POS_RIGHT
  763. */
  764. function blocks_get_pinned($page) {
  765. $visible = true;
  766. if (method_exists($page,'edit_always')) {
  767. if ($page->edit_always()) {
  768. $visible = false;
  769. }
  770. }
  771. $blocks = get_records_select('block_pinned', 'pagetype = \''. $page->get_type() .
  772. '\''.(($visible) ? 'AND visible = 1' : ''), 'position, weight');
  773. $positions = $page->blocks_get_positions();
  774. $arr = array();
  775. foreach($positions as $key => $position) {
  776. $arr[$position] = array();
  777. }
  778. if(empty($blocks)) {
  779. return $arr;
  780. }
  781. foreach($blocks as $block) {
  782. $block->pinned = true; // so we know we can't move it.
  783. // make up an instanceid if we can..
  784. $block->pageid = $page->get_id();
  785. $arr[$block->position][$block->weight] = $block;
  786. }
  787. return $arr;
  788. }
  789. /**
  790. * Similar to blocks_get_by_page(), except that, the array returned includes
  791. * pinned blocks as well. Pinned blocks are always appended before normal
  792. * block instances.
  793. */
  794. function blocks_get_by_page_pinned($page) {
  795. $pinned = blocks_get_pinned($page);
  796. $user = blocks_get_by_page($page);
  797. $weights = array();
  798. foreach ($pinned as $pos => $arr) {
  799. $weights[$pos] = count($arr);
  800. }
  801. foreach ($user as $pos => $blocks) {
  802. if (!array_key_exists($pos,$pinned)) {
  803. $pinned[$pos] = array();
  804. }
  805. if (!array_key_exists($pos,$weights)) {
  806. $weights[$pos] = 0;
  807. }
  808. foreach ($blocks as $block) {
  809. $pinned[$pos][$weights[$pos]] = $block;
  810. $weights[$pos]++;
  811. }
  812. }
  813. return $pinned;
  814. }
  815. /**
  816. * Returns an array of blocks for the page. Pinned blocks are excluded.
  817. */
  818. function blocks_get_by_page($page) {
  819. $blocks = get_records_select('block_instance', "pageid = '". $page->get_id() .
  820. "' AND pagetype = '". $page->get_type() ."'", 'position, weight');
  821. $positions = $page->blocks_get_positions();
  822. $arr = array();
  823. foreach($positions as $key => $position) {
  824. $arr[$position] = array();
  825. }
  826. if(empty($blocks)) {
  827. return $arr;
  828. }
  829. foreach($blocks as $block) {
  830. $arr[$block->position][$block->weight] = $block;
  831. }
  832. return $arr;
  833. }
  834. //This function prints the block to admin blocks as necessary
  835. function blocks_print_adminblock(&$page, &$pageblocks) {
  836. global $USER;
  837. $missingblocks = blocks_get_missing($page, $pageblocks);
  838. if (!empty($missingblocks)) {
  839. $strblocks = '<div class="title"><h2>';
  840. $strblocks .= get_string('blocks');
  841. $strblocks .= '</h2></div>';
  842. $stradd = get_string('add');
  843. foreach ($missingblocks as $blockid) {
  844. $block = blocks_get_record($blockid);
  845. $blockobject = block_instance($block->name);
  846. if ($blockobject === false) {
  847. continue;
  848. }
  849. if(!$blockobject->user_can_addto($page)) {
  850. continue;
  851. }
  852. $menu[$block->id] = $blockobject->get_title();
  853. }
  854. asort($menu);
  855. $target = $page->url_get_full(array('sesskey' => $USER->sesskey, 'blockaction' => 'add'));
  856. $content = popup_form($target.'&amp;blockid=', $menu, 'add_block', '', $stradd .'...', '', '', true);
  857. print_side_block($strblocks, $content, NULL, NULL, NULL, array('class' => 'block_adminblock'));
  858. }
  859. }
  860. /**
  861. * Delete all the blocks from a particular page.
  862. *
  863. * @param string $pagetype the page type.
  864. * @param integer $pageid the page id.
  865. * @return success of failure.
  866. */
  867. function blocks_delete_all_on_page($pagetype, $pageid) {
  868. if ($instances = get_records_select('block_instance', "pageid = $pageid AND pagetype = '$pagetype'")) {
  869. foreach ($instances as $instance) {
  870. delete_context(CONTEXT_BLOCK, $instance->id); // Ingore any failures here.
  871. }
  872. }
  873. return delete_records('block_instance', 'pageid', $pageid, 'pagetype', $pagetype);
  874. }
  875. // Dispite what this function is called, it seems to be mostly used to populate
  876. // the default blocks when a new course (or whatever) is created.
  877. function blocks_repopulate_page($page) {
  878. global $CFG;
  879. $allblocks = blocks_get_record();
  880. if(empty($allblocks)) {
  881. error('Could not retrieve blocks from the database');
  882. }
  883. // Assemble the information to correlate block names to ids
  884. $idforname = array();
  885. foreach($allblocks as $block) {
  886. $idforname[$block->name] = $block->id;
  887. }
  888. /// If the site override has been defined, it is the only valid one.
  889. if (!empty($CFG->defaultblocks_override)) {
  890. $blocknames = $CFG->defaultblocks_override;
  891. }
  892. else {
  893. $blocknames = $page->blocks_get_default();
  894. }
  895. $positions = $page->blocks_get_positions();
  896. $posblocks = explode(':', $blocknames);
  897. // Now one array holds the names of the positions, and the other one holds the blocks
  898. // that are going to go in each position. Luckily for us, both arrays are numerically
  899. // indexed and the indexes match, so we can work straight away... but CAREFULLY!
  900. // Ready to start creating block instances, but first drop any existing ones
  901. blocks_delete_all_on_page($page->get_type(), $page->get_id());
  902. // Here we slyly count $posblocks and NOT $positions. This can actually make a difference
  903. // if the textual representation has undefined slots in the end. So we only work with as many
  904. // positions were retrieved, not with all the page says it has available.
  905. $numpositions = count($posblocks);
  906. for($i = 0; $i < $numpositions; ++$i) {
  907. $position = $positions[$i];
  908. $blocknames = explode(',', $posblocks[$i]);
  909. $weight = 0;
  910. foreach($blocknames as $blockname) {
  911. $newinstance = new stdClass;
  912. $newinstance->blockid = $idforname[$blockname];
  913. $newinstance->pageid = $page->get_id();
  914. $newinstance->pagetype = $page->get_type();
  915. $newinstance->position = $position;
  916. $newinstance->weight = $weight;
  917. $newinstance->visible = 1;
  918. $newinstance->configdata = '';
  919. if(!empty($newinstance->blockid)) {
  920. // Only add block if it was recognized
  921. insert_record('block_instance', $newinstance);
  922. ++$weight;
  923. }
  924. }
  925. }
  926. return true;
  927. }
  928. function upgrade_blocks_db($continueto) {
  929. /// This function upgrades the blocks tables, if necessary
  930. /// It's called from admin/index.php
  931. global $CFG, $db;
  932. require_once ($CFG->dirroot .'/blocks/version.php'); // Get code versions
  933. if (empty($CFG->blocks_version)) { // Blocks have never been installed.
  934. $strdatabaseupgrades = get_string('databaseupgrades');
  935. print_header($strdatabaseupgrades, $strdatabaseupgrades,
  936. build_navigation(array(array('name' => $strdatabaseupgrades, 'link' => null, 'type' => 'misc'))), '',
  937. upgrade_get_javascript(), false, '&nbsp;', '&nbsp;');
  938. upgrade_log_start();
  939. print_heading('blocks');
  940. $db->debug=true;
  941. /// Both old .sql files and new install.xml are supported
  942. /// but we priorize install.xml (XMLDB) if present
  943. $status = false;
  944. if (file_exists($CFG->dirroot . '/blocks/db/install.xml')) {
  945. $status = install_from_xmldb_file($CFG->dirroot . '/blocks/db/install.xml'); //New method
  946. } else if (file_exists($CFG->dirroot . '/blocks/db/' . $CFG->dbtype . '.sql')) {
  947. $status = modify_database($CFG->dirroot . '/blocks/db/' . $CFG->dbtype . '.sql'); //Old method
  948. }
  949. $db->debug = false;
  950. if ($status) {
  951. if (set_config('blocks_version', $blocks_version)) {
  952. notify(get_string('databasesuccess'), 'notifysuccess');
  953. notify(get_string('databaseupgradeblocks', '', $blocks_version), 'notifysuccess');
  954. print_continue($continueto);
  955. print_footer('none');
  956. exit;
  957. } else {
  958. error('Upgrade of blocks system failed! (Could not update version in config table)');
  959. }
  960. } else {
  961. error('Blocks tables could NOT be set up successfully!');
  962. }
  963. }
  964. /// Upgrading code starts here
  965. $oldupgrade = false;
  966. $newupgrade = false;
  967. if (is_readable($CFG->dirroot . '/blocks/db/' . $CFG->dbtype . '.php')) {
  968. include_once($CFG->dirroot . '/blocks/db/' . $CFG->dbtype . '.php'); // defines old upgrading function
  969. $oldupgrade = true;
  970. }
  971. if (is_readable($CFG->dirroot . '/blocks/db/upgrade.php')) {
  972. include_once($CFG->dirroot . '/blocks/db/upgrade.php'); // defines new upgrading function
  973. $newupgrade = true;
  974. }
  975. if ($blocks_version > $CFG->blocks_version) { // Upgrade tables
  976. $strdatabaseupgrades = get_string('databaseupgrades');
  977. print_header($strdatabaseupgrades, $strdatabaseupgrades,
  978. build_navigation(array(array('name' => $strdatabaseupgrades, 'link' => null, 'type' => 'misc'))), '', upgrade_get_javascript());
  979. upgrade_log_start();
  980. print_heading('blocks');
  981. /// Run de old and new upgrade functions for the module
  982. $oldupgrade_function = 'blocks_upgrade';
  983. $newupgrade_function = 'xmldb_blocks_upgrade';
  984. /// First, the old function if exists
  985. $oldupgrade_status = true;
  986. if ($oldupgrade && function_exists($oldupgrade_function)) {
  987. $db->debug = true;
  988. $oldupgrade_status = $oldupgrade_function($CFG->blocks_version);
  989. } else if ($oldupgrade) {
  990. notify ('Upgrade function ' . $oldupgrade_function . ' was not available in ' .
  991. '/blocks/db/' . $CFG->dbtype . '.php');
  992. }
  993. /// Then, the new function if exists and the old one was ok
  994. $newupgrade_status = true;
  995. if ($newupgrade && function_exists($newupgrade_function) && $oldupgrade_status) {
  996. $db->debug = true;
  997. $newupgrade_status = $newupgrade_function($CFG->blocks_version);
  998. } else if ($newupgrade) {
  999. notify ('Upgrade function ' . $newupgrade_function . ' was not available in ' .
  1000. '/blocks/db/upgrade.php');
  1001. }
  1002. $db->debug=false;
  1003. /// Now analyze upgrade results
  1004. if ($oldupgrade_status && $newupgrade_status) { // No upgrading failed
  1005. if (set_config('blocks_version', $blocks_version)) {
  1006. notify(get_string('databasesuccess'), 'notifysuccess');
  1007. notify(get_string('databaseupgradeblocks', '', $blocks_version), 'notifysuccess');
  1008. print_continue($continueto);
  1009. print_footer('none');
  1010. exit;
  1011. } else {
  1012. error('Upgrade of blocks system failed! (Could not update version in config table)');
  1013. }
  1014. } else {
  1015. error('Upgrade failed! See blocks/version.php');
  1016. }
  1017. } else if ($blocks_version < $CFG->blocks_version) {
  1018. upgrade_log_start();
  1019. notify('WARNING!!! The Blocks version you are using is OLDER than the version that made these databases!');
  1020. }
  1021. upgrade_log_finish();
  1022. }
  1023. //This function finds all available blocks and install them
  1024. //into blocks table or do all the upgrade process if newer
  1025. function upgrade_blocks_plugins($continueto) {
  1026. global $CFG, $db;
  1027. $blocktitles = array();
  1028. $invalidblocks = array();
  1029. $validblocks = array();
  1030. $notices = array();
  1031. //Count the number of blocks in db
  1032. $blockcount = count_records('block');
  1033. //If there isn't records. This is the first install, so I remember it
  1034. if ($blockcount == 0) {
  1035. $first_install = true;
  1036. } else {
  1037. $first_install = false;
  1038. }
  1039. $site = get_site();
  1040. if (!$blocks = get_list_of_plugins('blocks', 'db') ) {
  1041. error('No blocks installed!');
  1042. }
  1043. include_once($CFG->dirroot .'/blocks/moodleblock.class.php');
  1044. if(!class_exists('block_base')) {
  1045. error('Class block_base is not defined or file not found for /blocks/moodleblock.class.php');
  1046. }
  1047. foreach ($blocks as $blockname) {
  1048. if ($blockname == 'NEWBLOCK') { // Someone has unzipped the template, ignore it
  1049. continue;
  1050. }
  1051. if(!block_is_compatible($blockname)) {
  1052. // This is an old-style block
  1053. //$notices[] = 'Block '. $blockname .' is not compatible with the current version of Mooodle and needs to be updated by a programmer.';
  1054. $invalidblocks[] = $blockname;
  1055. continue;
  1056. }
  1057. $fullblock = $CFG->dirroot .'/blocks/'. $blockname;
  1058. if ( is_readable($fullblock.'/block_'.$blockname.'.php')) {
  1059. include_once($fullblock.'/block_'.$blockname.'.php');
  1060. } else {
  1061. $notices[] = 'Block '. $blockname .': '. $fullblock .'/block_'. $blockname .'.php was not readable';
  1062. continue;
  1063. }
  1064. $oldupgrade = false;
  1065. $newupgrade = false;
  1066. if ( @is_dir($fullblock .'/db/')) {
  1067. if ( @is_readable($fullblock .'/db/'. $CFG->dbtype .'.php')) {
  1068. include_once($fullblock .'/db/'. $CFG->dbtype .'.php'); // defines old upgrading function
  1069. $oldupgrade = true;
  1070. }
  1071. if ( @is_readable($fullblock .'/db/upgrade.php')) {
  1072. include_once($fullblock .'/db/upgrade.php'); // defines new upgrading function
  1073. $newupgrade = true;
  1074. }
  1075. }
  1076. $classname = 'block_'.$blockname;
  1077. if(!class_exists($classname)) {
  1078. $notices[] = 'Block '. $blockname .': '. $classname .' not implemented';
  1079. continue;
  1080. }
  1081. // Here is the place to see if the block implements a constructor (old style),
  1082. // an init() function (new style) or nothing at all (error time).
  1083. $constructor = get_class_constructor($classname);
  1084. if(empty($constructor)) {
  1085. // No constructor
  1086. $notices[] = 'Block '. $blockname .': class does not have a constructor';
  1087. $invalidblocks[] = $blockname;
  1088. continue;
  1089. }
  1090. $block = new stdClass; // This may be used to update the db below
  1091. $blockobj = new $classname; // This is what we 'll be testing
  1092. // Inherits from block_base?
  1093. if(!is

Large files files are truncated, but you can click here to view the full file