Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 310 and 311] [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403] [Versions 39 and 311]

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  namespace core_block;
  18  
  19  use core_block_external;
  20  use externallib_advanced_testcase;
  21  
  22  defined('MOODLE_INTERNAL') || die();
  23  
  24  global $CFG;
  25  
  26  require_once($CFG->dirroot . '/webservice/tests/helpers.php');
  27  require_once($CFG->dirroot . '/my/lib.php');
  28  
  29  /**
  30   * External block functions unit tests
  31   *
  32   * @package    core_block
  33   * @category   external
  34   * @copyright  2015 Juan Leyva <juan@moodle.com>
  35   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  36   * @since      Moodle 3.0
  37   */
  38  class externallib_test extends externallib_advanced_testcase {
  39  
  40      /**
  41       * Test get_course_blocks
  42       */
  43      public function test_get_course_blocks() {
  44          global $DB, $FULLME;
  45  
  46          $this->resetAfterTest(true);
  47  
  48          $user = $this->getDataGenerator()->create_user();
  49          $course = $this->getDataGenerator()->create_course();
  50          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
  51          $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id);
  52  
  53          $page = new \moodle_page();
  54          $page->set_context(\context_course::instance($course->id));
  55          $page->set_pagelayout('course');
  56          $course->format = course_get_format($course)->get_format();
  57          $page->set_pagetype('course-view-' . $course->format);
  58          $page->blocks->load_blocks();
  59          $newblock = 'calendar_upcoming';
  60          $page->blocks->add_block_at_end_of_default_region($newblock);
  61          $this->setUser($user);
  62  
  63          // Check for the new block.
  64          $result = core_block_external::get_course_blocks($course->id);
  65          // We need to execute the return values cleaning process to simulate the web service server.
  66          $result = \external_api::clean_returnvalue(core_block_external::get_course_blocks_returns(), $result);
  67  
  68          // Expect the new block.
  69          $this->assertCount(1, $result['blocks']);
  70          $this->assertEquals($newblock, $result['blocks'][0]['name']);
  71      }
  72  
  73      /**
  74       * Test get_course_blocks on site home
  75       */
  76      public function test_get_course_blocks_site_home() {
  77          global $DB, $FULLME;
  78  
  79          $this->resetAfterTest(true);
  80  
  81          $user = $this->getDataGenerator()->create_user();
  82  
  83          $page = new \moodle_page();
  84          $page->set_context(\context_course::instance(SITEID));
  85          $page->set_pagelayout('frontpage');
  86          $page->set_pagetype('site-index');
  87          $page->blocks->load_blocks();
  88          $newblock = 'calendar_upcoming';
  89          $page->blocks->add_block_at_end_of_default_region($newblock);
  90          $this->setUser($user);
  91  
  92          // Check for the new block.
  93          $result = core_block_external::get_course_blocks(SITEID);
  94          // We need to execute the return values cleaning process to simulate the web service server.
  95          $result = \external_api::clean_returnvalue(core_block_external::get_course_blocks_returns(), $result);
  96  
  97          // Expect the new block.
  98          $this->assertCount(1, $result['blocks']);
  99          $this->assertEquals($newblock, $result['blocks'][0]['name']);
 100      }
 101  
 102      /**
 103       * Test get_course_blocks
 104       */
 105      public function test_get_course_blocks_overrides() {
 106          global $DB, $CFG, $FULLME;
 107  
 108          $this->resetAfterTest(true);
 109  
 110          $CFG->defaultblocks_override = 'search_forums,course_list:calendar_upcoming,recent_activity';
 111  
 112          $user = $this->getDataGenerator()->create_user();
 113          $course = $this->getDataGenerator()->create_course();
 114          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
 115          $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id);
 116  
 117          $this->setUser($user);
 118  
 119          // Try default blocks.
 120          $result = core_block_external::get_course_blocks($course->id);
 121          // We need to execute the return values cleaning process to simulate the web service server.
 122          $result = \external_api::clean_returnvalue(core_block_external::get_course_blocks_returns(), $result);
 123  
 124          // Expect 4 default blocks.
 125          $this->assertCount(4, $result['blocks']);
 126  
 127          $expectedblocks = array('navigation', 'settings', 'search_forums', 'course_list',
 128                                  'calendar_upcoming', 'recent_activity');
 129          foreach ($result['blocks'] as $block) {
 130              if (!in_array($block['name'], $expectedblocks)) {
 131                  $this->fail("Unexpected block found: " . $block['name']);
 132              }
 133          }
 134  
 135      }
 136  
 137      /**
 138       * Test get_course_blocks contents
 139       */
 140      public function test_get_course_blocks_contents() {
 141          global $DB, $FULLME;
 142  
 143          $this->resetAfterTest(true);
 144  
 145          $user = $this->getDataGenerator()->create_user();
 146          $course = $this->getDataGenerator()->create_course();
 147          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
 148          $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id);
 149          $coursecontext = \context_course::instance($course->id);
 150  
 151          // Create a HTML block.
 152          $title = 'Some course info';
 153          $body = 'Some course info<br /><p>Some contents</p>';
 154          $bodyformat = FORMAT_MOODLE;
 155          $page = new \moodle_page();
 156          $page->set_context($coursecontext);
 157          $page->set_pagelayout('course');
 158          $course->format = course_get_format($course)->get_format();
 159          $page->set_pagetype('course-view-' . $course->format);
 160          $page->blocks->load_blocks();
 161          $newblock = 'html';
 162          $page->blocks->add_block_at_end_of_default_region($newblock);
 163  
 164          $this->setUser($user);
 165          // Re-create the page.
 166          $page = new \moodle_page();
 167          $page->set_context($coursecontext);
 168          $page->set_pagelayout('course');
 169          $course->format = course_get_format($course)->get_format();
 170          $page->set_pagetype('course-view-' . $course->format);
 171          $page->blocks->load_blocks();
 172          $blocks = $page->blocks->get_blocks_for_region($page->blocks->get_default_region());
 173          $block = end($blocks);
 174          $block = block_instance('html', $block->instance);
 175          $nonscalar = [
 176              'something' => true,
 177          ];
 178          $configdata = (object) [
 179              'title' => $title,
 180              'text' => [
 181                  'itemid' => 0,
 182                  'text' => $body,
 183                  'format' => $bodyformat,
 184              ],
 185              'nonscalar' => $nonscalar
 186          ];
 187          $block->instance_config_save((object) $configdata);
 188          $filename = 'img.png';
 189          $filerecord = array(
 190              'contextid' => \context_block::instance($block->instance->id)->id,
 191              'component' => 'block_html',
 192              'filearea' => 'content',
 193              'itemid' => 0,
 194              'filepath' => '/',
 195              'filename' => $filename,
 196          );
 197          // Create an area to upload the file.
 198          $fs = get_file_storage();
 199          // Create a file from the string that we made earlier.
 200          $file = $fs->create_file_from_string($filerecord, 'some fake content (should be an image).');
 201  
 202          // Check for the new block.
 203          $result = core_block_external::get_course_blocks($course->id, true);
 204          // We need to execute the return values cleaning process to simulate the web service server.
 205          $result = \external_api::clean_returnvalue(core_block_external::get_course_blocks_returns(), $result);
 206  
 207          // Expect the new block.
 208          $this->assertCount(1, $result['blocks']);
 209          $this->assertEquals($title, $result['blocks'][0]['contents']['title']);
 210          $this->assertEquals($body, $result['blocks'][0]['contents']['content']);
 211          $this->assertEquals(FORMAT_HTML, $result['blocks'][0]['contents']['contentformat']);    // Format change for external.
 212          $this->assertEquals('', $result['blocks'][0]['contents']['footer']);
 213          $this->assertCount(1, $result['blocks'][0]['contents']['files']);
 214          $this->assertEquals($newblock, $result['blocks'][0]['name']);
 215          $configcounts = 0;
 216          foreach ($result['blocks'][0]['configs'] as $config) {
 217              if ($config['type'] = 'plugin' && $config['name'] == 'allowcssclasses' && $config['value'] == json_encode('0')) {
 218                  $configcounts++;
 219              } else if ($config['type'] = 'instance' && $config['name'] == 'text' && $config['value'] == json_encode($body)) {
 220                  $configcounts++;
 221              } else if ($config['type'] = 'instance' && $config['name'] == 'title' && $config['value'] == json_encode($title)) {
 222                  $configcounts++;
 223              } else if ($config['type'] = 'instance' && $config['name'] == 'format' && $config['value'] == json_encode('0')) {
 224                  $configcounts++;
 225              } else if ($config['type'] = 'instance' && $config['name'] == 'nonscalar' &&
 226                      $config['value'] == json_encode($nonscalar)) {
 227                  $configcounts++;
 228              }
 229          }
 230          $this->assertEquals(5, $configcounts);
 231      }
 232  
 233      /**
 234       * Test get_course_blocks contents with mathjax.
 235       */
 236      public function test_get_course_blocks_contents_with_mathjax() {
 237          global $DB, $CFG;
 238  
 239          require_once($CFG->dirroot . '/lib/externallib.php');
 240  
 241          $this->resetAfterTest(true);
 242  
 243          // Enable MathJax filter in content and headings.
 244          $this->configure_filters([
 245              ['name' => 'mathjaxloader', 'state' => TEXTFILTER_ON, 'move' => -1, 'applytostrings' => true],
 246          ]);
 247  
 248          // Create a few stuff to test with.
 249          $user = $this->getDataGenerator()->create_user();
 250          $course = $this->getDataGenerator()->create_course();
 251          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
 252          $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id);
 253          $coursecontext = \context_course::instance($course->id);
 254  
 255          // Create a HTML block.
 256          $title = 'My block $$(a+b)=2$$';
 257          $body = 'My block contents $$(a+b)=2$$';
 258          $bodyformat = FORMAT_MOODLE;
 259          $page = new \moodle_page();
 260          $page->set_context($coursecontext);
 261          $page->set_pagelayout('course');
 262          $course->format = course_get_format($course)->get_format();
 263          $page->set_pagetype('course-view-' . $course->format);
 264          $page->blocks->load_blocks();
 265          $newblock = 'html';
 266          $page->blocks->add_block_at_end_of_default_region($newblock);
 267  
 268          $this->setUser($user);
 269          // Re-create the page.
 270          $page = new \moodle_page();
 271          $page->set_context($coursecontext);
 272          $page->set_pagelayout('course');
 273          $course->format = course_get_format($course)->get_format();
 274          $page->set_pagetype('course-view-' . $course->format);
 275          $page->blocks->load_blocks();
 276          $blocks = $page->blocks->get_blocks_for_region($page->blocks->get_default_region());
 277          $block = end($blocks);
 278          $block = block_instance('html', $block->instance);
 279          $nonscalar = [
 280              'something' => true,
 281          ];
 282          $configdata = (object) [
 283              'title' => $title,
 284              'text' => [
 285                  'itemid' => 0,
 286                  'text' => $body,
 287                  'format' => $bodyformat,
 288              ],
 289              'nonscalar' => $nonscalar
 290          ];
 291          $block->instance_config_save((object) $configdata);
 292  
 293          // Check for the new block.
 294          $result = core_block_external::get_course_blocks($course->id, true);
 295          $result = \external_api::clean_returnvalue(core_block_external::get_course_blocks_returns(), $result);
 296  
 297          // Format the original data.
 298          $sitecontext = \context_system::instance();
 299          $title = external_format_string($title, $coursecontext->id);
 300          list($body, $bodyformat) = external_format_text($body, $bodyformat, $coursecontext->id, 'block_html', 'content');
 301  
 302          // Check that the block data is formatted.
 303          $this->assertCount(1, $result['blocks']);
 304          $this->assertStringContainsString('<span class="filter_mathjaxloader_equation">',
 305                  $result['blocks'][0]['contents']['title']);
 306          $this->assertStringContainsString('<span class="filter_mathjaxloader_equation">',
 307                  $result['blocks'][0]['contents']['content']);
 308          $this->assertEquals($title, $result['blocks'][0]['contents']['title']);
 309          $this->assertEquals($body, $result['blocks'][0]['contents']['content']);
 310      }
 311  
 312      /**
 313       * Test user get default dashboard blocks.
 314       */
 315      public function test_get_dashboard_blocks_default_dashboard() {
 316          global $PAGE, $DB;
 317          $this->resetAfterTest(true);
 318  
 319          $user = $this->getDataGenerator()->create_user();
 320          $PAGE->set_url('/my/index.php');    // Need this because some internal API calls require the $PAGE url to be set.
 321  
 322          // Force a setting change to check the returned blocks settings.
 323          set_config('displaycategories', 0, 'block_recentlyaccessedcourses');
 324  
 325          // Get the expected default blocks.
 326          $alldefaultblocksordered = $DB->get_records_menu('block_instances',
 327              array('pagetypepattern' => 'my-index'), 'defaultregion, defaultweight ASC', 'id, blockname');
 328  
 329          $this->setUser($user);
 330  
 331          // Check for the default blocks.
 332          $result = core_block_external::get_dashboard_blocks($user->id);
 333          // We need to execute the return values cleaning process to simulate the web service server.
 334          $result = \external_api::clean_returnvalue(core_block_external::get_dashboard_blocks_returns(), $result);
 335          // Expect all blogs except learning plans one (no learning plans to show).
 336          $this->assertCount(count($alldefaultblocksordered) - 1, $result['blocks']);
 337          $returnedblocks = array();
 338          foreach ($result['blocks'] as $block) {
 339              // Check all the returned blocks are in the expected blocks array.
 340              $this->assertContains($block['name'], $alldefaultblocksordered);
 341              $returnedblocks[] = $block['name'];
 342              // Check the configuration returned for this default block.
 343              if ($block['name'] == 'recentlyaccessedcourses') {
 344                  // Convert config to associative array to avoid DB sorting randomness.
 345                  $config = array_column($block['configs'], null, 'name');
 346                  $this->assertArrayHasKey('displaycategories', $config);
 347                  $this->assertEquals(json_encode('0'), $config['displaycategories']['value']);
 348                  $this->assertEquals('plugin', $config['displaycategories']['type']);
 349              }
 350          }
 351          // Remove lp block.
 352          array_shift($alldefaultblocksordered);
 353          // Check that we received the blocks in the expected order.
 354          $this->assertEquals(array_values($alldefaultblocksordered), $returnedblocks);
 355      }
 356  
 357      /**
 358       * Test user get default dashboard blocks including a sticky block.
 359       */
 360      public function test_get_dashboard_blocks_default_dashboard_including_sticky_block() {
 361          global $PAGE, $DB;
 362          $this->resetAfterTest(true);
 363  
 364          $user = $this->getDataGenerator()->create_user();
 365          $PAGE->set_url('/my/index.php');    // Need this because some internal API calls require the $PAGE url to be set.
 366  
 367          // Get the expected default blocks.
 368          $alldefaultblocks = $DB->get_records_menu('block_instances', array('pagetypepattern' => 'my-index'), '', 'id, blockname');
 369  
 370          // Now, add a sticky block.
 371          $page = new \moodle_page();
 372          $page->set_context(\context_system::instance());
 373          $page->set_pagetype('my-index');
 374          $page->set_url(new \moodle_url('/'));
 375          $page->blocks->add_region('side-pre');
 376          $page->blocks->load_blocks();
 377          $page->blocks->add_block('myprofile', 'side-pre', 0, true, '*');
 378  
 379          $this->setUser($user);
 380  
 381          // Check for the default blocks plus the sticky.
 382          $result = core_block_external::get_dashboard_blocks($user->id);
 383          // We need to execute the return values cleaning process to simulate the web service server.
 384          $result = \external_api::clean_returnvalue(core_block_external::get_dashboard_blocks_returns(), $result);
 385          // Expect all blogs plus sticky one except learning plans one (no learning plans to show).
 386          $this->assertCount(count($alldefaultblocks), $result['blocks']);
 387          $found = false;
 388          foreach ($result['blocks'] as $block) {
 389              if ($block['name'] == 'myprofile') {
 390                  $this->assertEquals('side-pre', $block['region']);
 391                  $found = true;
 392                  continue;
 393              }
 394              // Check that the block is in the expected blocks array.
 395              $this->assertContains($block['name'], $alldefaultblocks);
 396          }
 397          $this->assertTrue($found);
 398      }
 399  
 400      /**
 401       * Test admin get user's custom dashboard blocks.
 402       */
 403      public function test_get_dashboard_blocks_custom_user_dashboard() {
 404          global $PAGE, $DB;
 405          $this->resetAfterTest(true);
 406  
 407          $user = $this->getDataGenerator()->create_user();
 408          $PAGE->set_url('/my/index.php');    // Need this because some internal API calls require the $PAGE url to be set.
 409  
 410          // Get the expected default blocks.
 411          $alldefaultblocks = $DB->get_records_menu('block_instances', array('pagetypepattern' => 'my-index'), '', 'id, blockname');
 412  
 413          // Add a custom block.
 414          $page = new \moodle_page();
 415          $page->set_context(\context_user::instance($user->id));
 416          $page->set_pagelayout('mydashboard');
 417          $page->set_pagetype('my-index');
 418          $page->blocks->add_region('content');
 419          $currentpage = my_get_page($user->id, MY_PAGE_PRIVATE);
 420          $page->set_subpage($currentpage->id);
 421          $page->blocks->load_blocks();
 422          $page->blocks->add_block('myprofile', 'content', 0, false);
 423  
 424          $this->setAdminUser();
 425  
 426          // Check for the new block as admin for a user.
 427          $result = core_block_external::get_dashboard_blocks($user->id);
 428          // We need to execute the return values cleaning process to simulate the web service server.
 429          $result = \external_api::clean_returnvalue(core_block_external::get_dashboard_blocks_returns(), $result);
 430          // Expect all default blogs plys the one we added except learning plans one (no learning plans to show).
 431          $this->assertCount(count($alldefaultblocks), $result['blocks']);
 432          $found = false;
 433          foreach ($result['blocks'] as $block) {
 434              if ($block['name'] == 'myprofile') {
 435                  $this->assertEquals('content', $block['region']);
 436                  $found = true;
 437                  continue;
 438              }
 439              // Check that the block is in the expected blocks array.
 440              $this->assertContains($block['name'], $alldefaultblocks);
 441          }
 442          $this->assertTrue($found);
 443      }
 444  
 445      /**
 446       * Test user tries to get other user blocks not having permission.
 447       */
 448      public function test_get_dashboard_blocks_other_user_missing_permissions() {
 449          $this->resetAfterTest(true);
 450  
 451          $user1 = $this->getDataGenerator()->create_user();
 452          $user2 = $this->getDataGenerator()->create_user();
 453  
 454          $this->setUser($user1);
 455  
 456          $this->expectException('moodle_exception');
 457          core_block_external::get_dashboard_blocks($user2->id);
 458      }
 459  }