diff --git a/core/modules/aggregator/aggregator.module b/core/modules/aggregator/aggregator.module index 047026a..59aa356 100644 --- a/core/modules/aggregator/aggregator.module +++ b/core/modules/aggregator/aggregator.module @@ -459,13 +459,6 @@ function aggregator_save_category($edit) { db_delete('aggregator_category') ->condition('cid', $edit['cid']) ->execute(); - // Make sure there is no active block for this category. - if (module_exists('block')) { - db_delete('block') - ->condition('module', 'aggregator') - ->condition('delta', 'category-' . $edit['cid']) - ->execute(); - } $edit['title'] = ''; $op = 'delete'; } @@ -526,13 +519,6 @@ function aggregator_save_feed($edit) { db_delete('aggregator_item') ->condition('fid', $edit['fid']) ->execute(); - // Make sure there is no active block for this feed. - if (module_exists('block')) { - db_delete('block') - ->condition('module', 'aggregator') - ->condition('delta', 'feed-' . $edit['fid']) - ->execute(); - } } elseif (!empty($edit['title'])) { $edit['fid'] = db_insert('aggregator_feed') diff --git a/core/modules/block/block.admin.inc b/core/modules/block/block.admin.inc index f9fa425..759c51c 100644 --- a/core/modules/block/block.admin.inc +++ b/core/modules/block/block.admin.inc @@ -751,20 +751,17 @@ function block_custom_block_delete_submit($form, &$form_state) { db_delete('block_custom') ->condition('bid', $form_state['values']['bid']) ->execute(); - db_delete('block') - ->condition('module', 'block') - ->condition('delta', $form_state['values']['bid']) - ->execute(); - db_delete('block_role') - ->condition('module', 'block') - ->condition('delta', $form_state['values']['bid']) - ->execute(); db_delete('block_language') ->condition('module', 'block') ->condition('delta', $form_state['values']['bid']) ->execute(); drupal_set_message(t('The block %name has been removed.', array('%name' => $form_state['values']['info']))); + + // Invoke _block_rehash() for all enabled themes to remove all references to + // this custom block instance and flush block and page caches afterwards. + block_rebuild(); + cache_invalidate_tags(array('content' => TRUE)); $form_state['redirect'] = 'admin/structure/block'; return; diff --git a/core/modules/block/block.module b/core/modules/block/block.module index 26bcb25..77c2e08 100644 --- a/core/modules/block/block.module +++ b/core/modules/block/block.module @@ -253,8 +253,10 @@ function block_block_save($delta = 0, $edit = array()) { */ function block_block_view($delta = '') { $block = db_query('SELECT body, format FROM {block_custom} WHERE bid = :bid', array(':bid' => $delta))->fetchObject(); - $data['subject'] = NULL; - $data['content'] = check_markup($block->body, $block->format, '', TRUE); + if ($block) { + $data['subject'] = NULL; + $data['content'] = check_markup($block->body, $block->format, '', TRUE); + } return $data; } @@ -452,8 +454,6 @@ function _block_rehash($theme = NULL) { } // Blocks stored in the database override the blocks defined in code. $current_blocks[$block->module][$block->delta] = get_object_vars($block); - // Preserve this block. - $bids[$block->bid] = $block->bid; } drupal_alter('block_info', $current_blocks, $theme, $code_blocks); foreach ($current_blocks as $module => $module_blocks) { @@ -479,31 +479,64 @@ function _block_rehash($theme = NULL) { $block['status'] = 0; $block['region'] = BLOCK_REGION_NONE; } - // There is no point saving disabled blocks. Still, we need to save them - // because the 'title' attribute is saved to the {blocks} table. if (isset($block['bid'])) { // If the block has a bid property, it comes from the database and // the record needs to be updated, so set the primary key to 'bid' // before passing to drupal_write_record(). $primary_keys = array('bid'); - // Remove a block from the list of blocks to keep if it became disabled. - unset($bids[$block['bid']]); } else { $primary_keys = array(); } drupal_write_record('block', $block, $primary_keys); + // Now that the block has been saved, it is guaranteed to have an ID. + // Mark it to be preserved. + $bids[$block['bid']] = $block['bid']; // Add to the list of blocks we return. $blocks[] = $block; } } if ($bids) { - // Remove disabled that are no longer defined by the code from the - // database. - db_delete('block') + // Remove blocks that are no longer defined in code from the database. + // However, keep blocks belonging to a disabled module, to retain their + // settings in case the module is re-enabled. + // Blocks of uninstalled modules are deleted separately. + // @see block_modules_uninstalled() + $blocks_to_delete = db_select('block', 'b') + ->fields('b', array('bid', 'module', 'delta')) ->condition('bid', $bids, 'NOT IN') + ->condition('module', module_list(), 'IN') ->condition('theme', $theme) - ->execute(); + ->execute() + ->fetchAllAssoc('bid'); + if (!empty($blocks_to_delete)) { + $bids_to_delete = array_keys($blocks_to_delete); + // Delete the block instance records from the {block} table. + db_delete('block') + ->condition('bid', $bids_to_delete, 'IN') + ->execute(); + // Determine which corresponding {block_role} records can be deleted. + // {block_role} records are shared across themes, so they must be + // retained if other instances of the block exist. + $or = db_or(); + foreach ($blocks_to_delete as $block) { + $count = db_query_range('SELECT 1 FROM {block} WHERE module = :module AND delta = :delta', 0, 1, array( + ':module' => $block->module, + ':delta' => $block->delta, + )); + if ($count) { + $or->condition(db_and() + ->condition('module', $block->module) + ->condition('delta', $block->delta) + ); + } + } + if ($or->count()) { + db_delete('block_role') + ->condition($or) + ->execute(); + } + } } return $blocks; } @@ -604,7 +637,7 @@ function block_form_user_profile_form_alter(&$form, &$form_state) { $blocks = array(); foreach ($result as $block) { $data = module_invoke($block->module, 'block_info'); - if ($data[$block->delta]['info']) { + if (!empty($data[$block->delta]['info'])) { $blocks[$block->module][$block->delta] = array( '#type' => 'checkbox', '#title' => check_plain($data[$block->delta]['info']), @@ -1034,20 +1067,6 @@ function block_user_role_delete($role) { } /** - * Implements hook_menu_delete(). - */ -function block_menu_delete($menu) { - db_delete('block') - ->condition('module', 'menu') - ->condition('delta', $menu['menu_name']) - ->execute(); - db_delete('block_role') - ->condition('module', 'menu') - ->condition('delta', $menu['menu_name']) - ->execute(); -} - -/** * Implements hook_admin_paths(). */ function block_admin_paths() { diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockTest.php index 7446052..d238640 100644 --- a/core/modules/block/lib/Drupal/block/Tests/BlockTest.php +++ b/core/modules/block/lib/Drupal/block/Tests/BlockTest.php @@ -420,5 +420,31 @@ function testBlockRehash() { // Verify that the database is updated with the new caching mode. $current_caching = db_query("SELECT cache FROM {block} WHERE module = 'block_test' AND delta = 'test_cache'")->fetchField(); $this->assertEqual($current_caching, DRUPAL_NO_CACHE, "Test block's database entry updated to DRUPAL_NO_CACHE."); + + // Make sure the dynamic block is stored in the database. + $block = db_query("SELECT bid FROM {block} WHERE module = 'block_test' AND delta = 'test_dynamic_block'")->fetchField(); + $this->assertTrue(!empty($block), t('Test block is in the database.')); + + // Disable the module, and flush all caches to trigger _block_rehash(). The + // block should still be in the database. + module_disable(array('block_test')); + drupal_flush_all_caches(); + $block = db_query("SELECT bid FROM {block} WHERE module = 'block_test' AND delta = 'test_dynamic_block'")->fetchField(); + $this->assertTrue(!empty($block), t('Test block is still in the database after the module is disabled.')); + + // Re-enable the module, and trigger _block_rehash() again. The block + // should still be in the database. + module_enable(array('block_test')); + drupal_flush_all_caches(); + $block = db_query("SELECT bid FROM {block} WHERE module = 'block_test' AND delta = 'test_dynamic_block'")->fetchField(); + $this->assertTrue(!empty($block), t('Test block is still in the database after the module is re-enabled.')); + + // Set a variable that triggers hook_block_info() to no longer return the + // block, and trigger _block_rehash() again. The block should no longer be + // in the database. + variable_set('block_test_dynamic_block', FALSE); + drupal_flush_all_caches(); + $block = db_query("SELECT bid FROM {block} WHERE module = 'block_test' AND delta = 'test_dynamic_block'")->fetchField(); + $this->assertTrue(empty($block), t('Test block is no longer in the database when it is no longer defined by hook_block_info().')); } } diff --git a/core/modules/block/tests/block_test.module b/core/modules/block/tests/block_test.module index 4105294..774f46f 100644 --- a/core/modules/block/tests/block_test.module +++ b/core/modules/block/tests/block_test.module @@ -25,6 +25,12 @@ function block_test_block_info() { $blocks['test_html_id'] = array( 'info' => t('Test block html id'), ); + + if (variable_get('block_test_dynamic_block', TRUE)) { + $blocks['test_dynamic_block'] = array( + 'info' => t('Test dynamic block'), + ); + } return $blocks; } diff --git a/core/modules/menu/menu.module b/core/modules/menu/menu.module index e9080fd..7d017b6 100644 --- a/core/modules/menu/menu.module +++ b/core/modules/menu/menu.module @@ -489,13 +489,15 @@ function menu_block_info() { */ function menu_block_view($delta = '') { $menus = menu_get_menus(FALSE); - $data['subject'] = check_plain($menus[$delta]); - $data['content'] = menu_tree($delta); - // Add contextual links for this block. - if (!empty($data['content'])) { - $data['content']['#contextual_links']['menu'] = array('admin/structure/menu/manage', array($delta)); + if (isset($menus[$delta])) { + $data['subject'] = check_plain($menus[$delta]); + $data['content'] = menu_tree($delta); + // Add contextual links for this block. + if (!empty($data['content'])) { + $data['content']['#contextual_links']['menu'] = array('admin/structure/menu/manage', array($delta)); + } + return $data; } - return $data; } /**