Commit 51718b75 authored by Damyon Wiese's avatar Damyon Wiese
Browse files

MDL-56902 blocks: Prevent blocks returning when switching themes.

Now if the current theme requires some blocks that don't exist, they
will be created - but with a new flag that indicates this block should
only be returned if the current theme requires it. This allows flipping back
and forth between boost and clean without restoring the nav and settings blocks
in boost.
parent cd4a6b8b
......@@ -234,12 +234,18 @@ class block_manager {
return false;
}
$undeletableblocks = self::get_undeletable_block_types();
foreach ($this->blockinstances as $region) {
foreach ($region as $instance) {
if (empty($instance->instance->blockname)) {
continue;
}
if ($instance->instance->blockname == $blockname) {
if ($instance->instance->requiredbytheme) {
if (!in_array($block->name, $undeletableblocks)) {
continue;
}
}
return true;
}
}
......@@ -579,6 +585,20 @@ class block_manager {
return;
}
// Exclude auto created blocks if they are not undeletable in this theme.
$undeletable = $this->get_undeletable_block_types();
$undeletablecheck = '';
$undeletableparams = array();
$undeletablenotparams = array();
if (!empty($undeletable)) {
list($testsql, $undeletableparams) = $DB->get_in_or_equal($undeletable, SQL_PARAMS_NAMED, 'undeletable');
list($testnotsql, $undeletablenotparams) = $DB->get_in_or_equal($undeletable, SQL_PARAMS_NAMED, 'deletable', false);
$undeletablecheck = 'AND ((bi.blockname ' . $testsql . ' AND bi.requiredbytheme = 1) OR ' .
' (bi.blockname ' . $testnotsql . ' AND bi.requiredbytheme = 0))';
} else {
$undeletablecheck = 'AND (bi.requiredbytheme = 0)';
}
if (is_null($includeinvisible)) {
$includeinvisible = $this->page->user_is_editing();
}
......@@ -626,6 +646,7 @@ class block_manager {
bi.parentcontextid,
bi.showinsubcontexts,
bi.pagetypepattern,
bi.requiredbytheme,
bi.subpagepattern,
bi.defaultregion,
bi.defaultweight,
......@@ -649,12 +670,15 @@ class block_manager {
AND (bi.subpagepattern IS NULL OR bi.subpagepattern = :subpage2)
$visiblecheck
AND b.visible = 1
$undeletablecheck
ORDER BY
COALESCE(bp.region, bi.defaultregion),
COALESCE(bp.weight, bi.defaultweight),
bi.id";
$blockinstances = $DB->get_recordset_sql($sql, $params + $parentcontextparams + $pagetypepatternparams);
$allparams = $params + $parentcontextparams + $pagetypepatternparams + $undeletableparams + $undeletablenotparams;
$blockinstances = $DB->get_recordset_sql($sql, $allparams);
$this->birecordsbyregion = $this->prepare_per_region_arrays();
$unknown = array();
......@@ -957,6 +981,10 @@ class block_manager {
* load_blocks. This is used, for example, to ensure that all blocks get a
* chance to initialise themselves via the {@link block_base::specialize()}
* method, before any output is done.
*
* It is also used to create any blocks that are "undeletable" by the current theme.
* These blocks that are auto-created have requiredbytheme set on the block instance
* so they are only visible on themes that require them.
*/
public function create_all_block_instances() {
global $PAGE;
......@@ -977,7 +1005,7 @@ class block_manager {
}
}
if (!$found) {
$this->add_block_at_end_of_default_region($forced);
$this->add_block_required_by_theme($forced);
$missing = true;
}
}
......@@ -993,6 +1021,45 @@ class block_manager {
}
/**
* Add a block that is required by the current theme but has not been
* created yet. This is a special type of block that only shows in themes that
* require it (by listing it in undeletable_block_types).
*
* @param string $blockname the name of the block type.
*/
protected function add_block_required_by_theme($blockname) {
global $DB;
if (empty($this->birecordsbyregion)) {
// No blocks or block regions exist yet.
return;
}
$systemcontext = context_system::instance();
$defaultregion = $this->get_default_region();
// Add a special system wide block instance only for themes that require it.
$blockinstance = new stdClass;
$blockinstance->blockname = $blockname;
$blockinstance->parentcontextid = $systemcontext->id;
$blockinstance->showinsubcontexts = true;
$blockinstance->requiredbytheme = true;
$blockinstance->pagetypepattern = '*';
$blockinstance->subpagepattern = null;
$blockinstance->defaultregion = $defaultregion;
$blockinstance->defaultweight = 0;
$blockinstance->configdata = '';
$blockinstance->id = $DB->insert_record('block_instances', $blockinstance);
// Ensure the block context is created.
context_block::instance($blockinstance->id);
// If the new instance was created, allow it to do additional setup.
if ($block = block_instance($blockname, $blockinstance)) {
$block->instance_create();
}
}
/**
* Return an array of content objects from a set of block instances
*
......
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="lib/db" VERSION="20160804" COMMENT="XMLDB file for core Moodle tables"
<XMLDB PATH="lib/db" VERSION="20161119" COMMENT="XMLDB file for core Moodle tables"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../lib/xmldb/xmldb.xsd"
>
......@@ -2459,6 +2459,7 @@
<FIELD NAME="blockname" TYPE="char" LENGTH="40" NOTNULL="true" SEQUENCE="false" COMMENT="The type of block this is. Foreign key, references block.name."/>
<FIELD NAME="parentcontextid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="The context within which this block appears. Foreign key, references context.id."/>
<FIELD NAME="showinsubcontexts" TYPE="int" LENGTH="4" NOTNULL="true" SEQUENCE="false" COMMENT="If 1, this block appears on all matching pages in subcontexts of parentcontextid, as well in pages belonging to parentcontextid."/>
<FIELD NAME="requiredbytheme" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="If 1, this block was created because it was required by the theme and did not exist."/>
<FIELD NAME="pagetypepattern" TYPE="char" LENGTH="64" NOTNULL="true" SEQUENCE="false" COMMENT="The types of page this block appears on. Either an exact page type like mod-quiz-view, or a pattern like mod-quiz-* or course-view-*. Note that course-view-* will match course-view."/>
<FIELD NAME="subpagepattern" TYPE="char" LENGTH="16" NOTNULL="false" SEQUENCE="false" COMMENT="Further restrictions on where this block appears. In some places, e.g. during a quiz or lesson attempt, different pages have different subpage ids. If this field is not null, the block only appears on that particular subpage."/>
<FIELD NAME="defaultregion" TYPE="char" LENGTH="16" NOTNULL="true" SEQUENCE="false" COMMENT="Which block region this block should appear in on each page, in the absence of a specific position in the block_positions table."/>
......
......@@ -2406,5 +2406,20 @@ function xmldb_main_upgrade($oldversion) {
upgrade_main_savepoint(true, 2016110600.00);
}
if ($oldversion < 2016111900.00) {
// Define field requiredbytheme to be added to block_instances.
$table = new xmldb_table('block_instances');
$field = new xmldb_field('requiredbytheme', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, '0', 'showinsubcontexts');
// Conditionally launch add field requiredbytheme.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
// Main savepoint reached.
upgrade_main_savepoint(true, 2016111900.00);
}
return true;
}
......@@ -542,6 +542,58 @@ class core_blocklib_testcase extends advanced_testcase {
context_block::instance($tokeep); // Would throw an exception if it was deleted.
}
public function test_create_all_block_instances() {
global $CFG, $PAGE, $DB;
$this->resetAfterTest();
$regionname = 'side-pre';
$context = context_system::instance();
$PAGE->reset_theme_and_output();
$CFG->theme = 'boost';
list($page, $blockmanager) = $this->get_a_page_and_block_manager(array($regionname),
$context, 'page-type');
$blockmanager->load_blocks();
$blockmanager->create_all_block_instances();
$blocks = $blockmanager->get_blocks_for_region($regionname);
$this->assertEmpty($blocks);
// There should be no blocks in the DB.
$PAGE->reset_theme_and_output();
// Change to a theme with undeletable blocks.
$CFG->theme = 'clean';
list($page, $blockmanager) = $this->get_a_page_and_block_manager(array($regionname),
$context, 'page-type');
$blockmanager->load_blocks();
$blockmanager->create_all_block_instances();
$blocks = $blockmanager->get_blocks_for_region($regionname);
$this->assertCount(2, $blocks);
$undeletable = block_manager::get_undeletable_block_types();
foreach ($undeletable as $blockname) {
$instance = $DB->get_record('block_instances', array('blockname' => $blockname));
$this->assertEquals(1, $instance->requiredbytheme);
}
// Switch back and those auto blocks should not be returned.
$PAGE->reset_theme_and_output();
$CFG->theme = 'boost';
list($page, $blockmanager) = $this->get_a_page_and_block_manager(array($regionname),
$context, 'page-type');
$blockmanager->load_blocks();
$blockmanager->create_all_block_instances();
$blocks = $blockmanager->get_blocks_for_region($regionname);
$this->assertEmpty($blocks);
// But they should exist in the DB.
foreach ($undeletable as $blockname) {
$count = $DB->count_records('block_instances', array('blockname' => $blockname));
$this->assertEquals(1, $count);
}
}
}
/**
......
......@@ -29,7 +29,7 @@
defined('MOODLE_INTERNAL') || die();
$version = 2016111800.00; // YYYYMMDD = weekly release date of this DEV branch.
$version = 2016111900.00; // YYYYMMDD = weekly release date of this DEV branch.
// RR = release increments - 00 in DEV branches.
// .XX = incremental changes.
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment