From 9d1402abd8aba96cb5ad18a76c13146deeace2a6 Mon Sep 17 00:00:00 2001 From: Damyon Wiese Date: Fri, 2 Dec 2016 10:00:43 +0800 Subject: [PATCH] MDL-57232 blocks: Split undeletableblocktypes There are 2 kinds of blocks that are undeletable. Those required by the theme and those protected via the admin page ui. We only want the theme ones to be auto created site wide if they don't exist - so we need to rename the theme ones and split the logic. --- admin/blocks.php | 27 ++------- lib/blocklib.php | 116 +++++++++++++++++++++++++++++------- lib/outputlib.php | 6 +- lib/tests/blocklib_test.php | 22 ++++++- theme/boost/config.php | 2 +- theme/upgrade.txt | 2 +- 6 files changed, 124 insertions(+), 51 deletions(-) diff --git a/admin/blocks.php b/admin/blocks.php index dcf00f6001e..b31da9ea004 100644 --- a/admin/blocks.php +++ b/admin/blocks.php @@ -4,6 +4,7 @@ require_once('../config.php'); require_once($CFG->libdir.'/adminlib.php'); + require_once($CFG->libdir.'/blocklib.php'); require_once($CFG->libdir.'/tablelib.php'); admin_externalpage_setup('manageblocks'); @@ -49,36 +50,18 @@ admin_get_root(true, false); // settings not required - only pages } - if (!isset($CFG->undeletableblocktypes) || (!is_array($CFG->undeletableblocktypes) && !is_string($CFG->undeletableblocktypes))) { - $undeletableblocktypes = array('navigation', 'settings'); - } else if (is_string($CFG->undeletableblocktypes)) { - $undeletableblocktypes = explode(',', $CFG->undeletableblocktypes); - } else { - $undeletableblocktypes = $CFG->undeletableblocktypes; - } - if (!empty($protect) && confirm_sesskey()) { - if (!$block = $DB->get_record('block', array('id'=>$protect))) { - print_error('blockdoesnotexist', 'error'); - } - if (!in_array($block->name, $undeletableblocktypes)) { - $undeletableblocktypes[] = $block->name; - set_config('undeletableblocktypes', implode(',', $undeletableblocktypes)); - } + block_manager::protect_block((int)$protect); admin_get_root(true, false); // settings not required - only pages } if (!empty($unprotect) && confirm_sesskey()) { - if (!$block = $DB->get_record('block', array('id'=>$unprotect))) { - print_error('blockdoesnotexist', 'error'); - } - if (in_array($block->name, $undeletableblocktypes)) { - $undeletableblocktypes = array_diff($undeletableblocktypes, array($block->name)); - set_config('undeletableblocktypes', implode(',', $undeletableblocktypes)); - } + block_manager::unprotect_block((int)$unprotect); admin_get_root(true, false); // settings not required - only pages } + $undeletableblocktypes = block_manager::get_undeletable_block_types(); + echo $OUTPUT->header(); echo $OUTPUT->heading($strmanageblocks); diff --git a/lib/blocklib.php b/lib/blocklib.php index 4990a2e5aa0..e3a386883ea 100644 --- a/lib/blocklib.php +++ b/lib/blocklib.php @@ -215,12 +215,14 @@ class block_manager { } $unaddableblocks = self::get_undeletable_block_types(); + $requiredbythemeblocks = self::get_required_by_theme_block_types(); $pageformat = $this->page->pagetype; foreach($allblocks as $block) { if (!$bi = block_instance($block->name)) { continue; } if ($block->visible && !in_array($block->name, $unaddableblocks) && + !in_array($block->name, $requiredbythemeblocks) && ($bi->instance_allow_multiple() || !$this->is_block_present($block->name)) && blocks_name_allowed_in_format($block->name, $pageformat) && $bi->user_can_addto($this->page)) { @@ -376,6 +378,80 @@ class block_manager { } /** + * @return array names of block types that must exist on every page with this theme. + */ + public static function get_required_by_theme_block_types() { + global $CFG, $PAGE; + $requiredbythemeblocks = false; + if (isset($PAGE->theme->requiredblocks)) { + $requiredbythemeblocks = $PAGE->theme->requiredblocks; + } + + if ($requiredbythemeblocks === false) { + return array('navigation', 'settings'); + } else if ($requiredbythemeblocks === '') { + return array(); + } else if (is_string($requiredbythemeblocks)) { + return explode(',', $requiredbythemeblocks); + } else { + return $requiredbythemeblocks; + } + } + + /** + * Make this block type undeletable and unaddable. + * + * @param mixed $blockidorname string or int + */ + public static function protect_block($blockidorname) { + global $DB; + + $syscontext = context_system::instance(); + + require_capability('moodle/site:config', $syscontext); + + $block = false; + if (is_int($blockidorname)) { + $block = $DB->get_record('block', array('id' => $blockidorname), 'id, name', MUST_EXIST); + } else { + $block = $DB->get_record('block', array('name' => $blockidorname), 'id, name', MUST_EXIST); + } + $undeletableblocktypes = self::get_undeletable_block_types(); + if (!in_array($block->name, $undeletableblocktypes)) { + $undeletableblocktypes[] = $block->name; + set_config('undeletableblocktypes', implode(',', $undeletableblocktypes)); + } + } + + /** + * Make this block type deletable and addable. + * + * @param mixed $blockidorname string or int + */ + public static function unprotect_block($blockidorname) { + global $DB; + + $syscontext = context_system::instance(); + + require_capability('moodle/site:config', $syscontext); + + $block = false; + if (is_int($blockidorname)) { + $block = $DB->get_record('block', array('id' => $blockidorname), 'id, name', MUST_EXIST); + } else { + $block = $DB->get_record('block', array('name' => $blockidorname), 'id, name', MUST_EXIST); + } + $undeletableblocktypes = self::get_undeletable_block_types(); + if (in_array($block->name, $undeletableblocktypes)) { + $undeletableblocktypes = array_diff($undeletableblocktypes, array($block->name)); + set_config('undeletableblocktypes', implode(',', $undeletableblocktypes)); + } + + } + + /** + * Get the list of "protected" blocks via admin block manager ui. + * * @return array names of block types that cannot be added or deleted. E.g. array('navigation','settings'). */ public static function get_undeletable_block_types() { @@ -383,13 +459,9 @@ class block_manager { $undeletableblocks = false; if (isset($CFG->undeletableblocktypes)) { $undeletableblocks = $CFG->undeletableblocktypes; - } else if (isset($PAGE->theme->undeletableblocktypes)) { - $undeletableblocks = $PAGE->theme->undeletableblocktypes; } - if ($undeletableblocks === false) { - return array('navigation','settings'); - } else if ($undeletableblocks === '') { + if (empty($undeletableblocks)) { return array(); } else if (is_string($undeletableblocks)) { return explode(',', $undeletableblocks); @@ -596,17 +668,18 @@ class block_manager { } // 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 ' . + $requiredbytheme = $this->get_required_by_theme_block_types(); + $requiredbythemecheck = ''; + $requiredbythemeparams = array(); + $requiredbythemenotparams = array(); + if (!empty($requiredbytheme)) { + list($testsql, $requiredbythemeparams) = $DB->get_in_or_equal($requiredbytheme, SQL_PARAMS_NAMED, 'requiredbytheme'); + list($testnotsql, $requiredbythemenotparams) = $DB->get_in_or_equal($requiredbytheme, SQL_PARAMS_NAMED, + 'notrequiredbytheme', false); + $requiredbythemecheck = 'AND ((bi.blockname ' . $testsql . ' AND bi.requiredbytheme = 1) OR ' . ' (bi.blockname ' . $testnotsql . ' AND bi.requiredbytheme = 0))'; } else { - $undeletablecheck = 'AND (bi.requiredbytheme = 0)'; + $requiredbythemecheck = 'AND (bi.requiredbytheme = 0)'; } if (is_null($includeinvisible)) { @@ -680,14 +753,14 @@ class block_manager { AND (bi.subpagepattern IS NULL OR bi.subpagepattern = :subpage2) $visiblecheck AND b.visible = 1 - $undeletablecheck + $requiredbythemecheck ORDER BY COALESCE(bp.region, bi.defaultregion), COALESCE(bp.weight, bi.defaultweight), bi.id"; - $allparams = $params + $parentcontextparams + $pagetypepatternparams + $undeletableparams + $undeletablenotparams; + $allparams = $params + $parentcontextparams + $pagetypepatternparams + $requiredbythemeparams + $requiredbythemenotparams; $blockinstances = $DB->get_recordset_sql($sql, $allparams); $this->birecordsbyregion = $this->prepare_per_region_arrays(); @@ -992,7 +1065,7 @@ class block_manager { * 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. + * It is also used to create any blocks that are "requiredbytheme" 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. */ @@ -1001,9 +1074,9 @@ class block_manager { $missing = false; // If there are any un-removable blocks that were not created - force them. - $undeletable = $this->get_undeletable_block_types(); + $requiredbytheme = $this->get_required_by_theme_block_types(); if (!$this->fakeblocksonly) { - foreach ($undeletable as $forced) { + foreach ($requiredbytheme as $forced) { if (empty($forced)) { continue; } @@ -1283,7 +1356,8 @@ class block_manager { protected function user_can_delete_block($block) { return $this->page->user_can_edit_blocks() && $block->user_can_edit() && $block->user_can_addto($this->page) && - !in_array($block->instance->blockname, self::get_undeletable_block_types()); + !in_array($block->instance->blockname, self::get_undeletable_block_types()) && + !in_array($block->instance->blockname, self::get_required_by_theme_block_types()); } /** @@ -2453,7 +2527,7 @@ function blocks_add_default_system_blocks() { $page = new moodle_page(); $page->set_context(context_system::instance()); - $page->blocks->add_blocks(array(BLOCK_POS_LEFT => block_manager::get_undeletable_block_types()), '*', null, true); + // We don't add blocks required by the theme, they will be auto-created. $page->blocks->add_blocks(array(BLOCK_POS_LEFT => array('admin_bookmarks')), 'admin-*', null, null, 2); if ($defaultmypage = $DB->get_record('my_pages', array('userid' => null, 'name' => '__default', 'private' => 1))) { diff --git a/lib/outputlib.php b/lib/outputlib.php index 1f93bd253db..03f9068662f 100644 --- a/lib/outputlib.php +++ b/lib/outputlib.php @@ -349,10 +349,10 @@ class theme_config { public $doctype = 'html5'; /** - * @var string undeletableblocktypes If set to a string, will list the block types that cannot be deleted. Defaults to + * @var string requiredblocks If set to a string, will list the block types that cannot be deleted. Defaults to * navigation and settings. */ - public $undeletableblocktypes = false; + public $requiredblocks = false; //==Following properties are not configurable from theme config.php== @@ -542,7 +542,7 @@ class theme_config { $configurable = array( 'parents', 'sheets', 'parents_exclude_sheets', 'plugins_exclude_sheets', 'javascripts', 'javascripts_footer', 'parents_exclude_javascripts', - 'layouts', 'enable_dock', 'enablecourseajax', 'undeletableblocktypes', + 'layouts', 'enable_dock', 'enablecourseajax', 'requiredblocks', 'rendererfactory', 'csspostprocess', 'editor_sheets', 'rarrow', 'larrow', 'uarrow', 'darrow', 'hidefromselector', 'doctype', 'yuicssmodules', 'blockrtlmanipulations', 'lessfile', 'extralesscallback', 'lessvariablescallback', 'blockrendermethod', diff --git a/lib/tests/blocklib_test.php b/lib/tests/blocklib_test.php index b396373ee77..b5b5454584e 100644 --- a/lib/tests/blocklib_test.php +++ b/lib/tests/blocklib_test.php @@ -545,6 +545,7 @@ class core_blocklib_testcase extends advanced_testcase { public function test_create_all_block_instances() { global $CFG, $PAGE, $DB; + $this->setAdminUser(); $this->resetAfterTest(); $regionname = 'side-pre'; $context = context_system::instance(); @@ -557,6 +558,7 @@ class core_blocklib_testcase extends advanced_testcase { $blockmanager->load_blocks(); $blockmanager->create_all_block_instances(); $blocks = $blockmanager->get_blocks_for_region($regionname); + // Assert that we no auto created blocks in boost by default. $this->assertEmpty($blocks); // There should be no blocks in the DB. @@ -571,6 +573,7 @@ class core_blocklib_testcase extends advanced_testcase { $blockmanager->load_blocks(); $blockmanager->create_all_block_instances(); $blocks = $blockmanager->get_blocks_for_region($regionname); + // Assert that we no auto created blocks when viewing a fake blocks only page. $this->assertEmpty($blocks); $PAGE->reset_theme_and_output(); @@ -581,10 +584,22 @@ class core_blocklib_testcase extends advanced_testcase { $blockmanager->load_blocks(); $blockmanager->create_all_block_instances(); $blocks = $blockmanager->get_blocks_for_region($regionname); + // Assert that we get the required block for this theme auto-created. $this->assertCount(2, $blocks); - $undeletable = block_manager::get_undeletable_block_types(); - foreach ($undeletable as $blockname) { + $PAGE->reset_theme_and_output(); + list($page, $blockmanager) = $this->get_a_page_and_block_manager(array($regionname), + $context, 'page-type'); + + $blockmanager->protect_block('html'); + $blockmanager->load_blocks(); + $blockmanager->create_all_block_instances(); + $blocks = $blockmanager->get_blocks_for_region($regionname); + // Assert that protecting a block does not make it auto-created. + $this->assertCount(2, $blocks); + + $requiredbytheme = block_manager::get_required_by_theme_block_types(); + foreach ($requiredbytheme as $blockname) { $instance = $DB->get_record('block_instances', array('blockname' => $blockname)); $this->assertEquals(1, $instance->requiredbytheme); } @@ -598,9 +613,10 @@ class core_blocklib_testcase extends advanced_testcase { $blockmanager->load_blocks(); $blockmanager->create_all_block_instances(); $blocks = $blockmanager->get_blocks_for_region($regionname); + // Assert that we do not return requiredbytheme blocks when they are not required. $this->assertEmpty($blocks); // But they should exist in the DB. - foreach ($undeletable as $blockname) { + foreach ($requiredbytheme as $blockname) { $count = $DB->count_records('block_instances', array('blockname' => $blockname)); $this->assertEquals(1, $count); } diff --git a/theme/boost/config.php b/theme/boost/config.php index b04956b4e4b..f7ea46b5af6 100644 --- a/theme/boost/config.php +++ b/theme/boost/config.php @@ -151,5 +151,5 @@ $THEME->extrascsscallback = 'theme_boost_get_extra_scss'; $THEME->prescsscallback = 'theme_boost_get_pre_scss'; $THEME->yuicssmodules = array(); $THEME->rendererfactory = 'theme_overridden_renderer_factory'; -$THEME->undeletableblocktypes = ''; +$THEME->requiredblocks = ''; $THEME->addblockposition = BLOCK_ADDBLOCK_POSITION_FLATNAV; diff --git a/theme/upgrade.txt b/theme/upgrade.txt index 6387ad8a199..5e0f854ebe9 100644 --- a/theme/upgrade.txt +++ b/theme/upgrade.txt @@ -35,7 +35,7 @@ information provided here is intended especially for theme designer. * A new class .text-ltr may be used to force the direction to LTR. This is especially useful for forms fields (numbers, emails, URLs must not be RTL'd), and for displaying code snippets or configuration samples. -* A new theme config 'undeletableblocktypes' allows a theme to define which blocks are deletable. +* A new theme config 'requiredblocks' allows a theme to define which blocks are deletable. * You may no longer override the following core_course_renderer methods. See course/upgrade.txt for more information: - course_modchooser_module_types