Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401]

   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;
  18  
  19  defined('MOODLE_INTERNAL') || die();
  20  
  21  global $CFG;
  22  require_once($CFG->libdir . '/pagelib.php');
  23  require_once($CFG->libdir . '/blocklib.php');
  24  require_once($CFG->dirroot . '/blocks/moodleblock.class.php');
  25  require_once (__DIR__ . '/fixtures/block_ablocktype.php');
  26  require_once (__DIR__ . '/fixtures/testable_block_manager.php');
  27  
  28  /**
  29   * Tests for the block_manager class in ../blocklib.php.
  30   *
  31   * @package   core
  32   * @category  test
  33   * @copyright 2009 Tim Hunt
  34   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  35   */
  36  class blocklib_test extends \advanced_testcase {
  37      protected $testpage;
  38      protected $blockmanager;
  39      protected $isediting = null;
  40  
  41      protected function setUp(): void {
  42          parent::setUp();
  43          $this->testpage = new \moodle_page();
  44          $this->testpage->set_context(\context_system::instance());
  45          $this->testpage->set_pagetype('phpunit-block-test');
  46          $this->blockmanager = new \testable_block_manager($this->testpage);
  47      }
  48  
  49      protected function tearDown(): void {
  50          $this->testpage = null;
  51          $this->blockmanager = null;
  52          parent::tearDown();
  53      }
  54  
  55      protected function purge_blocks() {
  56          global $DB;
  57          $this->resetAfterTest();
  58  
  59          $bis = $DB->get_records('block_instances');
  60          foreach ($bis as $instance) {
  61              blocks_delete_instance($instance);
  62          }
  63      }
  64  
  65      /**
  66       * Gets the last block created.
  67       *
  68       * @return stdClass a record from block_instances
  69       */
  70      protected function get_last_created_block() {
  71          global $DB;
  72          // The newest block should be the record with the highest id.
  73          $records = $DB->get_records('block_instances', [], 'id DESC', '*', 0, 1);
  74          $return = null;
  75          foreach ($records as $record) {
  76              // There should only be one.
  77              $return = $record;
  78          }
  79          return $return;
  80      }
  81  
  82      public function test_no_regions_initially() {
  83          // Exercise SUT & Validate.
  84          $this->assertEquals(array(), $this->blockmanager->get_regions());
  85      }
  86  
  87      public function test_add_region() {
  88          // Exercise SUT.
  89          $this->blockmanager->add_region('a-region-name', false);
  90          // Validate.
  91          $this->assertEquals(array('a-region-name'), $this->blockmanager->get_regions());
  92      }
  93  
  94      public function test_add_regions() {
  95          // Set up fixture.
  96          $regions = array('a-region', 'another-region');
  97          // Exercise SUT.
  98          $this->blockmanager->add_regions($regions, false);
  99          // Validate.
 100          $this->assertEqualsCanonicalizing($regions, $this->blockmanager->get_regions());
 101      }
 102  
 103      public function test_add_region_twice() {
 104          // Exercise SUT.
 105          $this->blockmanager->add_region('a-region-name', false);
 106          $this->blockmanager->add_region('another-region', false);
 107          // Validate.
 108          $this->assertEqualsCanonicalizing(array('a-region-name', 'another-region'), $this->blockmanager->get_regions());
 109      }
 110  
 111      public function test_cannot_add_region_after_loaded() {
 112          // Set up fixture.
 113          $this->blockmanager->mark_loaded();
 114          // Exercise SUT.
 115          $this->expectException(\coding_exception::class);
 116          $this->blockmanager->add_region('too-late', false);
 117      }
 118  
 119      /**
 120       * Testing adding a custom region.
 121       */
 122      public function test_add_custom_region() {
 123          global $SESSION;
 124          // Exercise SUT.
 125          $this->blockmanager->add_region('a-custom-region-name');
 126          // Validate.
 127          $this->assertEquals(array('a-custom-region-name'), $this->blockmanager->get_regions());
 128          $this->assertTrue(isset($SESSION->custom_block_regions));
 129          $this->assertArrayHasKey('phpunit-block-test', $SESSION->custom_block_regions);
 130          $this->assertTrue(in_array('a-custom-region-name', $SESSION->custom_block_regions['phpunit-block-test']));
 131  
 132      }
 133  
 134      /**
 135       * Test adding two custom regions using add_regions method.
 136       */
 137      public function test_add_custom_regions() {
 138          global $SESSION;
 139          // Set up fixture.
 140          $regions = array('a-region', 'another-custom-region');
 141          // Exercise SUT.
 142          $this->blockmanager->add_regions($regions);
 143          // Validate.
 144          $this->assertEqualsCanonicalizing($regions, $this->blockmanager->get_regions());
 145          $this->assertTrue(isset($SESSION->custom_block_regions));
 146          $this->assertArrayHasKey('phpunit-block-test', $SESSION->custom_block_regions);
 147          $this->assertTrue(in_array('another-custom-region', $SESSION->custom_block_regions['phpunit-block-test']));
 148      }
 149  
 150      /**
 151       * Test adding two custom block regions.
 152       */
 153      public function test_add_custom_region_twice() {
 154          // Exercise SUT.
 155          $this->blockmanager->add_region('a-custom-region-name');
 156          $this->blockmanager->add_region('another-custom-region');
 157          // Validate.
 158          $this->assertEqualsCanonicalizing(
 159              array('a-custom-region-name', 'another-custom-region'),
 160              $this->blockmanager->get_regions());
 161      }
 162  
 163      /**
 164       * Test to ensure that we cannot add a region after the blocks have been loaded.
 165       */
 166      public function test_cannot_add_custom_region_after_loaded() {
 167          // Set up fixture.
 168          $this->blockmanager->mark_loaded();
 169          // Exercise SUT.
 170          $this->expectException(\coding_exception::class);
 171          $this->blockmanager->add_region('too-late');
 172      }
 173  
 174      public function test_set_default_region() {
 175          // Set up fixture.
 176          $this->blockmanager->add_region('a-region-name', false);
 177          // Exercise SUT.
 178          $this->blockmanager->set_default_region('a-region-name');
 179          // Validate.
 180          $this->assertEquals('a-region-name', $this->blockmanager->get_default_region());
 181      }
 182  
 183      public function test_cannot_set_unknown_region_as_default() {
 184          // Exercise SUT.
 185          $this->expectException(\coding_exception::class);
 186          $this->blockmanager->set_default_region('a-region-name');
 187      }
 188  
 189      public function test_cannot_change_default_region_after_loaded() {
 190          // Set up fixture.
 191          $this->blockmanager->mark_loaded();
 192          // Exercise SUT.
 193          $this->expectException(\coding_exception::class);
 194          $this->blockmanager->set_default_region('too-late');
 195      }
 196  
 197      public function test_matching_page_type_patterns() {
 198          $this->assertEqualsCanonicalizing(array('site-index', 'site-index-*', 'site-*', '*'),
 199              matching_page_type_patterns('site-index'));
 200  
 201          $this->assertEqualsCanonicalizing(array('mod-quiz-report-overview', 'mod-quiz-report-overview-*', 'mod-quiz-report-*', 'mod-quiz-*', 'mod-*', '*'),
 202              matching_page_type_patterns('mod-quiz-report-overview'));
 203  
 204          $this->assertEqualsCanonicalizing(array('mod-forum-view', 'mod-*-view', 'mod-forum-view-*', 'mod-forum-*', 'mod-*', '*'),
 205              matching_page_type_patterns('mod-forum-view'));
 206  
 207          $this->assertEqualsCanonicalizing(array('mod-forum-index', 'mod-*-index', 'mod-forum-index-*', 'mod-forum-*', 'mod-*', '*'),
 208              matching_page_type_patterns('mod-forum-index'));
 209      }
 210  
 211      protected function get_a_page_and_block_manager($regions, $context, $pagetype, $subpage = '') {
 212          $page = new \moodle_page;
 213          $page->set_context($context);
 214          $page->set_pagetype($pagetype);
 215          $page->set_subpage($subpage);
 216          $page->set_url(new \moodle_url('/'));
 217  
 218          $blockmanager = new \testable_block_manager($page);
 219          $blockmanager->add_regions($regions, false);
 220          $blockmanager->set_default_region($regions[0]);
 221  
 222          return array($page, $blockmanager);
 223      }
 224  
 225      protected function get_a_known_block_type() {
 226          global $DB;
 227          $block = new \stdClass;
 228          $block->name = 'ablocktype';
 229          $DB->insert_record('block', $block);
 230          return $block->name;
 231      }
 232  
 233      protected function assertContainsBlocksOfType($typearray, $blockarray) {
 234          if (!$this->assertEquals(count($typearray), count($blockarray), "Blocks array contains the wrong number of elements %s.")) {
 235              return;
 236          }
 237          $types = array_values($typearray);
 238          $i = 0;
 239          foreach ($blockarray as $block) {
 240              $blocktype = $types[$i];
 241              $this->assertEquals($blocktype, $block->name(), "Block types do not match at postition $i %s.");
 242              $i++;
 243          }
 244      }
 245  
 246      public function test_empty_initially() {
 247          $this->purge_blocks();
 248  
 249          // Set up fixture.
 250          list($page, $blockmanager) = $this->get_a_page_and_block_manager(array('a-region'),
 251              \context_system::instance(), 'page-type');
 252          // Exercise SUT.
 253          $blockmanager->load_blocks();
 254          // Validate.
 255          $blocks = $blockmanager->get_loaded_blocks();
 256          $this->assertEquals(array('a-region' => array()), $blocks);
 257      }
 258  
 259      public function test_adding_and_retrieving_one_block() {
 260          $this->purge_blocks();
 261  
 262          // Set up fixture.
 263          $regionname = 'a-region';
 264          $blockname = $this->get_a_known_block_type();
 265          $context = \context_system::instance();
 266  
 267          list($page, $blockmanager) = $this->get_a_page_and_block_manager(array($regionname),
 268              $context, 'page-type');
 269  
 270          // Exercise SUT.
 271          $blockmanager->add_block($blockname, $regionname, 0, false);
 272          $blockmanager->load_blocks();
 273          // Validate.
 274          $blocks = $blockmanager->get_blocks_for_region($regionname);
 275          $this->assertContainsBlocksOfType(array($blockname), $blocks);
 276      }
 277  
 278      public function test_adding_and_retrieving_two_blocks() {
 279          $this->purge_blocks();
 280  
 281          // Set up fixture.
 282          $regionname = 'a-region';
 283          $blockname = $this->get_a_known_block_type();
 284          $context = \context_system::instance();
 285  
 286          list($page, $blockmanager) = $this->get_a_page_and_block_manager(array($regionname),
 287              $context, 'page-type');
 288  
 289          // Exercise SUT.
 290          $blockmanager->add_block($blockname, $regionname, 0, false);
 291          $blockmanager->add_block($blockname, $regionname, 1, false);
 292          $blockmanager->load_blocks();
 293          // Validate.
 294          $blocks = $blockmanager->get_blocks_for_region($regionname);
 295          $this->assertContainsBlocksOfType(array($blockname, $blockname), $blocks);
 296      }
 297  
 298      public function test_adding_blocks() {
 299          $this->purge_blocks();
 300  
 301          // Set up fixture.
 302          $regionname = 'a-region';
 303          $blockname = $this->get_a_known_block_type();
 304          $context = \context_system::instance();
 305  
 306          list($page, $blockmanager) = $this->get_a_page_and_block_manager(array($regionname),
 307              $context, 'page-type');
 308  
 309          $blockmanager->add_blocks(array($regionname => array($blockname, $blockname)), null, null, false, 3);
 310          $blockmanager->load_blocks();
 311  
 312          $blocks = $blockmanager->get_blocks_for_region($regionname);
 313  
 314          $this->assertEquals('3', $blocks[0]->instance->weight);
 315          $this->assertEquals('4', $blocks[1]->instance->weight);
 316      }
 317  
 318      /**
 319       * Test block instances.
 320       *
 321       * @return null
 322       */
 323      public function test_block_instances() {
 324          $this->purge_blocks();
 325  
 326          // Set up fixture.
 327          $regionname = 'a-region';
 328          $blockname = $this->get_a_known_block_type();
 329          $context = \context_system::instance();
 330  
 331          list($page, $blockmanager) = $this->get_a_page_and_block_manager(array($regionname),
 332              $context, 'page-type');
 333  
 334          $blockmanager->add_blocks(array($regionname => array($blockname, $blockname)), null, null, false, 3);
 335          $blockmanager->load_blocks();
 336  
 337          $blocks = $blockmanager->get_blocks_for_region($regionname);
 338  
 339          $this->assertInstanceOf('\block_base', block_instance($blockname, $blocks[0]->instance));
 340          $this->assertInstanceOf('\block_base', block_instance_by_id($blocks[0]->instance->id));
 341      }
 342  
 343      public function test_block_not_included_in_different_context() {
 344          $this->purge_blocks();
 345  
 346          // Set up fixture.
 347          $syscontext = \context_system::instance();
 348          $cat = $this->getDataGenerator()->create_category(array('name' => 'testcategory'));
 349          $fakecontext = \context_coursecat::instance($cat->id);
 350          $regionname = 'a-region';
 351          $blockname = $this->get_a_known_block_type();
 352  
 353          list($addpage, $addbm) = $this->get_a_page_and_block_manager(array($regionname), $fakecontext, 'page-type');
 354          list($viewpage, $viewbm) = $this->get_a_page_and_block_manager(array($regionname), $syscontext, 'page-type');
 355  
 356          $addbm->add_block($blockname, $regionname, 0, false);
 357  
 358          // Exercise SUT.
 359          $viewbm->load_blocks();
 360          // Validate.
 361          $blocks = $viewbm->get_blocks_for_region($regionname);
 362          $this->assertContainsBlocksOfType(array(), $blocks);
 363      }
 364  
 365      public function test_block_included_in_sub_context() {
 366          $this->purge_blocks();
 367  
 368          // Set up fixture.
 369          $syscontext = \context_system::instance();
 370          $childcontext = \context_coursecat::instance(1);
 371          $regionname = 'a-region';
 372          $blockname = $this->get_a_known_block_type();
 373  
 374          list($addpage, $addbm) = $this->get_a_page_and_block_manager(array($regionname), $syscontext, 'page-type');
 375          list($viewpage, $viewbm) = $this->get_a_page_and_block_manager(array($regionname), $childcontext, 'page-type');
 376  
 377          $addbm->add_block($blockname, $regionname, 0, true);
 378  
 379          // Exercise SUT.
 380          $viewbm->load_blocks();
 381          // Validate.
 382          $blocks = $viewbm->get_blocks_for_region($regionname);
 383          $this->assertContainsBlocksOfType(array($blockname), $blocks);
 384      }
 385  
 386      public function test_block_not_included_on_different_page_type() {
 387          $this->purge_blocks();
 388  
 389          // Set up fixture.
 390          $syscontext = \context_system::instance();
 391          $regionname = 'a-region';
 392          $blockname = $this->get_a_known_block_type();
 393  
 394          list($addpage, $addbm) = $this->get_a_page_and_block_manager(array($regionname), $syscontext, 'page-type');
 395          list($viewpage, $viewbm) = $this->get_a_page_and_block_manager(array($regionname), $syscontext, 'other-page-type');
 396  
 397          $addbm->add_block($blockname, $regionname, 0, true);
 398  
 399          // Exercise SUT.
 400          $viewbm->load_blocks();
 401          // Validate.
 402          $blocks = $viewbm->get_blocks_for_region($regionname);
 403          $this->assertContainsBlocksOfType(array(), $blocks);
 404      }
 405  
 406      public function test_block_not_included_on_different_sub_page() {
 407          $this->purge_blocks();
 408  
 409          // Set up fixture.
 410          $regionname = 'a-region';
 411          $blockname = $this->get_a_known_block_type();
 412          $syscontext = \context_system::instance();
 413  
 414          list($page, $blockmanager) = $this->get_a_page_and_block_manager(array($regionname),
 415              $syscontext, 'page-type', 'sub-page');
 416  
 417          $blockmanager->add_block($blockname, $regionname, 0, true, $page->pagetype, 'other-sub-page');
 418  
 419          // Exercise SUT.
 420          $blockmanager->load_blocks();
 421          // Validate.
 422          $blocks = $blockmanager->get_blocks_for_region($regionname);
 423          $this->assertContainsBlocksOfType(array(), $blocks);
 424      }
 425  
 426      public function test_block_included_with_explicit_sub_page() {
 427          $this->purge_blocks();
 428  
 429          // Set up fixture.
 430          $regionname = 'a-region';
 431          $blockname = $this->get_a_known_block_type();
 432          $syscontext = \context_system::instance();
 433  
 434          list($page, $blockmanager) = $this->get_a_page_and_block_manager(array($regionname),
 435              $syscontext, 'page-type', 'sub-page');
 436  
 437          $blockmanager->add_block($blockname, $regionname, 0, true, $page->pagetype, $page->subpage);
 438  
 439          // Exercise SUT.
 440          $blockmanager->load_blocks();
 441          // Validate.
 442          $blocks = $blockmanager->get_blocks_for_region($regionname);
 443          $this->assertContainsBlocksOfType(array($blockname), $blocks);
 444      }
 445  
 446      public function test_block_included_with_page_type_pattern() {
 447          $this->purge_blocks();
 448  
 449          // Set up fixture.
 450          $regionname = 'a-region';
 451          $blockname = $this->get_a_known_block_type();
 452          $syscontext = \context_system::instance();
 453  
 454          list($page, $blockmanager) = $this->get_a_page_and_block_manager(array($regionname),
 455              $syscontext, 'page-type', 'sub-page');
 456  
 457          $blockmanager->add_block($blockname, $regionname, 0, true, 'page-*', $page->subpage);
 458  
 459          // Exercise SUT.
 460          $blockmanager->load_blocks();
 461          // Validate.
 462          $blocks = $blockmanager->get_blocks_for_region($regionname);
 463          $this->assertContainsBlocksOfType(array($blockname), $blocks);
 464      }
 465  
 466      public function test_matching_page_type_patterns_from_pattern() {
 467          $pattern = '*';
 468          $expected = array('*');
 469          $this->assertEquals($expected, array_values(matching_page_type_patterns_from_pattern($pattern)));
 470  
 471          $pattern = 'admin-*';
 472          $expected = array('admin-*', 'admin', '*');
 473          $this->assertEquals($expected, array_values(matching_page_type_patterns_from_pattern($pattern)));
 474  
 475          $pattern = 'blog-index';
 476          $expected = array('blog-index', 'blog-index-*', 'blog-*', '*');
 477          $this->assertEquals($expected, array_values(matching_page_type_patterns_from_pattern($pattern)));
 478  
 479          $pattern = 'course-index-*';
 480          $expected = array('course-index-*', 'course-index', 'course-*', '*');
 481          $this->assertEquals($expected, array_values(matching_page_type_patterns_from_pattern($pattern)));
 482  
 483          $pattern = 'course-index-category';
 484          $expected = array('course-index-category', 'course-index-category-*', 'course-index-*', 'course-*', '*');
 485          $this->assertEquals($expected, array_values(matching_page_type_patterns_from_pattern($pattern)));
 486  
 487          $pattern = 'mod-assign-view';
 488          $expected = array('mod-assign-view', 'mod-*-view', 'mod-assign-view-*', 'mod-assign-*', 'mod-*', '*');
 489          $this->assertEquals($expected, array_values(matching_page_type_patterns_from_pattern($pattern)));
 490  
 491          $pattern = 'mod-assign-index';
 492          $expected = array('mod-assign-index', 'mod-*-index', 'mod-assign-index-*', 'mod-assign-*', 'mod-*', '*');
 493          $this->assertEquals($expected, array_values(matching_page_type_patterns_from_pattern($pattern)));
 494  
 495          $pattern = 'mod-forum-*';
 496          $expected = array('mod-forum-*', 'mod-forum', 'mod-*', '*');
 497          $this->assertEquals($expected, array_values(matching_page_type_patterns_from_pattern($pattern)));
 498  
 499          $pattern = 'mod-*-view';
 500          $expected = array('mod-*-view', 'mod', 'mod-*', '*');
 501          $this->assertEquals($expected, array_values(matching_page_type_patterns_from_pattern($pattern)));
 502  
 503          $pattern = 'mod-*-index';
 504          $expected = array('mod-*-index', 'mod', 'mod-*', '*');
 505          $this->assertEquals($expected, array_values(matching_page_type_patterns_from_pattern($pattern)));
 506  
 507          $pattern = 'my-index';
 508          $expected = array('my-index', 'my-index-*', 'my-*', '*');
 509          $this->assertEquals($expected, array_values(matching_page_type_patterns_from_pattern($pattern)));
 510  
 511          $pattern = 'user-profile';
 512          $expected = array('user-profile', 'user-profile-*', 'user-*', '*');
 513          $this->assertEquals($expected, array_values(matching_page_type_patterns_from_pattern($pattern)));
 514      }
 515  
 516      public function test_delete_instances() {
 517          global $DB;
 518          $this->purge_blocks();
 519          $this->setAdminUser();
 520  
 521          $regionname = 'a-region';
 522          $blockname = $this->get_a_known_block_type();
 523          $context = \context_system::instance();
 524  
 525          list($page, $blockmanager) = $this->get_a_page_and_block_manager(array($regionname),
 526              $context, 'page-type');
 527  
 528          $blockmanager->add_blocks(array($regionname => array($blockname, $blockname, $blockname)), null, null, false, 3);
 529          $blockmanager->load_blocks();
 530  
 531          $blocks = $blockmanager->get_blocks_for_region($regionname);
 532          $blockids = array();
 533          $preferences = array();
 534  
 535          // Create block related data.
 536          foreach ($blocks as $block) {
 537              $instance = $block->instance;
 538              $pref = 'block' . $instance->id . 'hidden';
 539              set_user_preference($pref, '123', 123);
 540              $preferences[] = $pref;
 541              $pref = 'docked_block_instance_' . $instance->id;
 542              set_user_preference($pref, '123', 123);
 543              $preferences[] = $pref;
 544              blocks_set_visibility($instance, $page, 1);
 545              $blockids[] = $instance->id;
 546          }
 547  
 548          // Confirm what has been set.
 549          $this->assertCount(3, $blockids);
 550          list($insql, $inparams) = $DB->get_in_or_equal($blockids);
 551          $this->assertEquals(3, $DB->count_records_select('block_positions', "blockinstanceid $insql", $inparams));
 552          list($insql, $inparams) = $DB->get_in_or_equal($preferences);
 553          $this->assertEquals(6, $DB->count_records_select('user_preferences', "name $insql", $inparams));
 554  
 555          // Keep a block on the side.
 556          $allblockids = $blockids;
 557          $tokeep = array_pop($blockids);
 558  
 559          // Delete and confirm what should have happened.
 560          blocks_delete_instances($blockids);
 561  
 562          // Reload the manager.
 563          list($page, $blockmanager) = $this->get_a_page_and_block_manager(array($regionname),
 564              $context, 'page-type');
 565          $blockmanager->load_blocks();
 566          $blocks = $blockmanager->get_blocks_for_region($regionname);
 567  
 568          $this->assertCount(1, $blocks);
 569          list($insql, $inparams) = $DB->get_in_or_equal($allblockids);
 570          $this->assertEquals(1, $DB->count_records_select('block_positions', "blockinstanceid $insql", $inparams));
 571          list($insql, $inparams) = $DB->get_in_or_equal($preferences);
 572          $this->assertEquals(2, $DB->count_records_select('user_preferences', "name $insql", $inparams));
 573  
 574          $this->assertFalse(\context_block::instance($blockids[0], IGNORE_MISSING));
 575          $this->assertFalse(\context_block::instance($blockids[1], IGNORE_MISSING));
 576          \context_block::instance($tokeep);   // Would throw an exception if it was deleted.
 577      }
 578  
 579      public function test_create_all_block_instances() {
 580          global $CFG, $PAGE, $DB;
 581  
 582          $this->setAdminUser();
 583          $this->resetAfterTest();
 584          $regionname = 'side-pre';
 585          $context = \context_system::instance();
 586  
 587          $PAGE->reset_theme_and_output();
 588          $CFG->theme = 'boost';
 589  
 590          list($page, $blockmanager) = $this->get_a_page_and_block_manager(array($regionname),
 591              $context, 'page-type');
 592          $blockmanager->load_blocks();
 593          $blockmanager->create_all_block_instances();
 594          $blocks = $blockmanager->get_blocks_for_region($regionname);
 595          // Assert that we no auto created blocks in boost by default.
 596          $this->assertEmpty($blocks);
 597          // There should be no blocks in the DB.
 598  
 599          $PAGE->reset_theme_and_output();
 600          // Change to a theme with undeletable blocks.
 601          $CFG->theme = 'classic';
 602  
 603          list($page, $blockmanager) = $this->get_a_page_and_block_manager(array($regionname),
 604              $context, 'page-type');
 605  
 606          $blockmanager->show_only_fake_blocks(true);
 607          $blockmanager->load_blocks();
 608          $blockmanager->create_all_block_instances();
 609          $blocks = $blockmanager->get_blocks_for_region($regionname);
 610          // Assert that we no auto created blocks when viewing a fake blocks only page.
 611          $this->assertEmpty($blocks);
 612  
 613          $PAGE->reset_theme_and_output();
 614          list($page, $blockmanager) = $this->get_a_page_and_block_manager(array($regionname),
 615              $context, 'page-type');
 616  
 617          $blockmanager->show_only_fake_blocks(false);
 618          $blockmanager->load_blocks();
 619          $blockmanager->create_all_block_instances();
 620          $blocks = $blockmanager->get_blocks_for_region($regionname);
 621          // Assert that we get the required block for this theme auto-created.
 622          $this->assertCount(2, $blocks);
 623  
 624          $PAGE->reset_theme_and_output();
 625          list($page, $blockmanager) = $this->get_a_page_and_block_manager(array($regionname),
 626              $context, 'page-type');
 627  
 628          $blockmanager->protect_block('html');
 629          $blockmanager->load_blocks();
 630          $blockmanager->create_all_block_instances();
 631          $blocks = $blockmanager->get_blocks_for_region($regionname);
 632          // Assert that protecting a block does not make it auto-created.
 633          $this->assertCount(2, $blocks);
 634  
 635          $requiredbytheme = $blockmanager->get_required_by_theme_block_types();
 636          foreach ($requiredbytheme as $blockname) {
 637              $instance = $DB->get_record('block_instances', array('blockname' => $blockname));
 638              $this->assertEquals(1, $instance->requiredbytheme);
 639          }
 640  
 641          // Switch back and those auto blocks should not be returned.
 642          $PAGE->reset_theme_and_output();
 643          $CFG->theme = 'boost';
 644  
 645          list($page, $blockmanager) = $this->get_a_page_and_block_manager(array($regionname),
 646              $context, 'page-type');
 647          $blockmanager->load_blocks();
 648          $blockmanager->create_all_block_instances();
 649          $blocks = $blockmanager->get_blocks_for_region($regionname);
 650          // Assert that we do not return requiredbytheme blocks when they are not required.
 651          $this->assertEmpty($blocks);
 652          // But they should exist in the DB.
 653          foreach ($requiredbytheme as $blockname) {
 654              $count = $DB->count_records('block_instances', array('blockname' => $blockname));
 655              $this->assertEquals(1, $count);
 656          }
 657      }
 658  
 659      /**
 660       * Test the block instance time fields (timecreated, timemodified).
 661       */
 662      public function test_block_instance_times() {
 663          global $DB;
 664  
 665          $this->purge_blocks();
 666  
 667          // Set up fixture.
 668          $regionname = 'a-region';
 669          $blockname = 'html';
 670          $context = \context_system::instance();
 671  
 672          list($page, $blockmanager) = $this->get_a_page_and_block_manager(array($regionname),
 673                  $context, 'page-type');
 674  
 675          // Add block to page.
 676          $before = time();
 677          $blockmanager->add_block($blockname, $regionname, 0, false);
 678          $after = time();
 679  
 680          // Check database table to ensure it contains created/modified times.
 681          $blockdata = $DB->get_record('block_instances', ['blockname' => 'html']);
 682          $this->assertTrue($blockdata->timemodified >= $before && $blockdata->timemodified <= $after);
 683          $this->assertTrue($blockdata->timecreated >= $before && $blockdata->timecreated <= $after);
 684  
 685          // Get block from manager.
 686          $blockmanager->load_blocks();
 687          $blocks = $blockmanager->get_blocks_for_region($regionname);
 688          $block = reset($blocks);
 689  
 690          // Wait until at least the next second.
 691          while (time() === $after) {
 692              usleep(100000);
 693          }
 694  
 695          // Update block settings.
 696          $this->setAdminUser();
 697          $data = (object)['text' => ['text' => 'New text', 'itemid' => 0, 'format' => FORMAT_HTML]];
 698          $before = time();
 699          $block->instance_config_save($data);
 700          $after = time();
 701  
 702          // Check time modified updated, but time created didn't.
 703          $newblockdata = $DB->get_record('block_instances', ['blockname' => 'html']);
 704          $this->assertTrue(
 705                  $newblockdata->timemodified >= $before &&
 706                  $newblockdata->timemodified <= $after &&
 707                  $newblockdata->timemodified > $blockdata->timemodified);
 708          $this->assertEquals($blockdata->timecreated, $newblockdata->timecreated);
 709  
 710          // Also try repositioning the block.
 711          while (time() === $after) {
 712              usleep(100000);
 713          }
 714          $before = time();
 715          $blockmanager->reposition_block($blockdata->id, $regionname, 10);
 716          $after = time();
 717          $blockdata = $newblockdata;
 718          $newblockdata = $DB->get_record('block_instances', ['blockname' => 'html']);
 719          $this->assertTrue(
 720                  $newblockdata->timemodified >= $before &&
 721                  $newblockdata->timemodified <= $after &&
 722                  $newblockdata->timemodified > $blockdata->timemodified);
 723          $this->assertEquals($blockdata->timecreated, $newblockdata->timecreated);
 724      }
 725  
 726      /**
 727       * Tests that dashboard pages get their blocks loaded correctly.
 728       */
 729      public function test_default_dashboard() {
 730          global $CFG, $PAGE, $DB;
 731          $storedpage = $PAGE;
 732          require_once($CFG->dirroot . '/my/lib.php');
 733          $this->purge_blocks();
 734          $regionname = 'a-region';
 735          $blockname = $this->get_a_known_block_type();
 736          $user = self::getDataGenerator()->create_user();
 737          $syscontext = \context_system::instance();
 738          $usercontext = \context_user::instance($user->id);
 739          // Add sitewide 'sticky' blocks. The page is not setup exactly as a site page would be...
 740          // but it does seem to mean that the bloacks are added correctly.
 741          list($sitepage, $sitebm) = $this->get_a_page_and_block_manager(array($regionname), $syscontext, 'site-index');
 742          $sitebm->add_block($blockname, $regionname, 0, true, '*');
 743          $sitestickyblock1 = $this->get_last_created_block();
 744          $sitebm->add_block($blockname, $regionname, 1, true, '*');
 745          $sitestickyblock2 = $this->get_last_created_block();
 746          $sitebm->add_block($blockname, $regionname, 8, true, '*');
 747          $sitestickyblock3 = $this->get_last_created_block();
 748          // Blocks that should not be picked up by any other pages in this unit test.
 749          $sitebm->add_block($blockname, $regionname, -8, true, 'site-index-*');
 750          $sitebm->add_block($blockname, $regionname, -9, true, 'site-index');
 751          $sitebm->load_blocks();
 752          // This repositioning should not be picked up.
 753          $sitebm->reposition_block($sitestickyblock3->id, $regionname, 9);
 754          // Setup the default dashboard page. This adds the blocks with the correct parameters, but seems to not be
 755          // an exact page/blockmanager setup for the default dashboard setup page.
 756          $defaultmy = my_get_page(null, MY_PAGE_PRIVATE);
 757          list($defaultmypage, $defaultmybm) = $this->get_a_page_and_block_manager(array($regionname), null, 'my-index', $defaultmy->id);
 758          $PAGE = $defaultmypage;
 759          $defaultmybm->add_block($blockname, $regionname, -2, false, $defaultmypage->pagetype, $defaultmypage->subpage);
 760          $defaultblock1 = $this->get_last_created_block();
 761          $defaultmybm->add_block($blockname, $regionname, 3, false, $defaultmypage->pagetype, $defaultmypage->subpage);
 762          $defaultblock2 = $this->get_last_created_block();
 763          $defaultmybm->load_blocks();
 764          $defaultmybm->reposition_block($sitestickyblock1->id, $regionname, 4);
 765          // Setup the user's dashboard.
 766          $usermy = my_copy_page($user->id);
 767          list($mypage, $mybm) = $this->get_a_page_and_block_manager(array($regionname), $usercontext, 'my-index', $usermy->id);
 768          $PAGE = $mypage;
 769          $mybm->add_block($blockname, $regionname, 5, false, $mypage->pagetype, $mypage->subpage);
 770          $block1 = $this->get_last_created_block();
 771          $mybm->load_blocks();
 772          $mybm->reposition_block($sitestickyblock2->id, $regionname, -1);
 773          // Reload the blocks in the managers.
 774          \context_helper::reset_caches();
 775          $defaultmybm->reset_caches();
 776          $this->assertNull($defaultmybm->get_loaded_blocks());
 777          $defaultmybm->load_blocks();
 778          $this->assertNotNull($defaultmybm->get_loaded_blocks());
 779          $defaultbr = $defaultmybm->get_blocks_for_region($regionname);
 780          $mybm->reset_caches();
 781          $this->assertNull($mybm->get_loaded_blocks());
 782          $mybm->load_blocks();
 783          $this->assertNotNull($mybm->get_loaded_blocks());
 784          $mybr = $mybm->get_blocks_for_region($regionname);
 785          // Test that a user dashboard when forced to use the default finds the correct blocks.
 786          list($forcedmypage, $forcedmybm) = $this->get_a_page_and_block_manager(array($regionname), $usercontext, 'my-index', $defaultmy->id);
 787          $forcedmybm->load_blocks();
 788          $forcedmybr = $forcedmybm->get_blocks_for_region($regionname);
 789          // Check that the default page is in the expected order.
 790          $this->assertCount(5, $defaultbr);
 791          $this->assertEquals($defaultblock1->id, $defaultbr[0]->instance->id);
 792          $this->assertEquals('-2', $defaultbr[0]->instance->weight);
 793          $this->assertEquals($sitestickyblock2->id, $defaultbr[1]->instance->id);
 794          $this->assertEquals('1', $defaultbr[1]->instance->weight);
 795          $this->assertEquals($defaultblock2->id, $defaultbr[2]->instance->id);
 796          $this->assertEquals('3', $defaultbr[2]->instance->weight);
 797          $this->assertEquals($sitestickyblock1->id, $defaultbr[3]->instance->id);
 798          $this->assertEquals('4', $defaultbr[3]->instance->weight);
 799          $this->assertEquals($sitestickyblock3->id, $defaultbr[4]->instance->id);
 800          $this->assertEquals('8', $defaultbr[4]->instance->weight);
 801          // Check that the correct block are present in the expected order for a.
 802          $this->assertCount(5, $forcedmybr);
 803          $this->assertEquals($defaultblock1->id, $forcedmybr[0]->instance->id);
 804          $this->assertEquals('-2', $forcedmybr[0]->instance->weight);
 805          $this->assertEquals($sitestickyblock2->id, $forcedmybr[1]->instance->id);
 806          $this->assertEquals('1', $forcedmybr[1]->instance->weight);
 807          $this->assertEquals($defaultblock2->id, $forcedmybr[2]->instance->id);
 808          $this->assertEquals('3', $forcedmybr[2]->instance->weight);
 809          $this->assertEquals($sitestickyblock1->id, $forcedmybr[3]->instance->id);
 810          $this->assertEquals('4', $forcedmybr[3]->instance->weight);
 811          $this->assertEquals($sitestickyblock3->id, $forcedmybr[4]->instance->id);
 812          $this->assertEquals('8', $forcedmybr[4]->instance->weight);
 813          // Check that the correct blocks are present in the standard my page.
 814          $this->assertCount(6, $mybr);
 815          $this->assertEquals('-2', $mybr[0]->instance->weight);
 816          $this->assertEquals($sitestickyblock2->id, $mybr[1]->instance->id);
 817          $this->assertEquals('-1', $mybr[1]->instance->weight);
 818          $this->assertEquals('3', $mybr[2]->instance->weight);
 819          // Test the override on the first sticky block was copied and picked up.
 820          $this->assertEquals($sitestickyblock1->id, $mybr[3]->instance->id);
 821          $this->assertEquals('4', $mybr[3]->instance->weight);
 822          $this->assertEquals($block1->id, $mybr[4]->instance->id);
 823          $this->assertEquals('5', $mybr[4]->instance->weight);
 824          $this->assertEquals($sitestickyblock3->id, $mybr[5]->instance->id);
 825          $this->assertEquals('8', $mybr[5]->instance->weight);
 826          $PAGE = $storedpage;
 827      }
 828  
 829      /**
 830       * Test get_unaddable_by_theme_block_types() method to return expected result depending on the theme.
 831       *
 832       * @covers \block_manager::get_unaddable_by_theme_block_types
 833       */
 834      public function test_get_unaddable_by_theme_block_types(): void {
 835          global $CFG, $PAGE;
 836  
 837          $this->setAdminUser();
 838          $this->resetAfterTest();
 839          $regionname = 'side-pre';
 840          $context = \context_system::instance();
 841  
 842          $PAGE->reset_theme_and_output();
 843          $CFG->theme = 'boost';
 844  
 845          list($page, $blockmanager) = $this->get_a_page_and_block_manager([$regionname], $context, 'page-type');
 846          $blockmanager->load_blocks();
 847          $blocks = $blockmanager->get_unaddable_by_theme_block_types();
 848          // Assert that a few blocks are excluded for boost theme.
 849          $this->assertCount(4, $blocks);
 850          $this->assertContains('navigation', $blocks);
 851          $this->assertContains('settings', $blocks);
 852          $this->assertContains('course_list', $blocks);
 853          $this->assertContains('section_links', $blocks);
 854  
 855          // Change to a theme without unaddable blocks.
 856          $PAGE->reset_theme_and_output();
 857          $CFG->theme = 'classic';
 858  
 859          list($page, $blockmanager) = $this->get_a_page_and_block_manager([$regionname], $context, 'page-type');
 860          $blockmanager->load_blocks();
 861          $blocks = $blockmanager->get_unaddable_by_theme_block_types();
 862          // Assert that no blocks are excluded for classic theme.
 863          $this->assertEmpty($blocks);
 864      }
 865  }