diff --git a/core/modules/block/lib/Drupal/block/Tests/Views/DisplayBlockTest.php b/core/modules/block/lib/Drupal/block/Tests/Views/DisplayBlockTest.php index d88894d..a84549c 100644 --- a/core/modules/block/lib/Drupal/block/Tests/Views/DisplayBlockTest.php +++ b/core/modules/block/lib/Drupal/block/Tests/Views/DisplayBlockTest.php @@ -7,7 +7,6 @@ namespace Drupal\block\Tests\Views; -use Drupal\block\Plugin\Core\Entity\Block; use Drupal\views\Tests\ViewTestBase; use Drupal\views\Tests\ViewTestData; @@ -48,43 +47,6 @@ protected function setUp() { } /** - * Checks to see whether a block appears on the page. - * - * @param \Drupal\block\Plugin\Core\Entity\Block $block - * The block entity to find on the page. - */ - protected function assertBlockAppears(Block $block) { - $result = $this->findBlockInstance($block); - $this->assertTrue(!empty($result), format_string('Ensure the block @id appears on the page', array('@id' => $block->id()))); - } - - /** - * Checks to see whether a block does not appears on the page. - * - * @param \Drupal\block\Plugin\Core\Entity\Block $block - * The block entity to find on the page. - */ - protected function assertNoBlockAppears(Block $block) { - $result = $this->findBlockInstance($block); - $this->assertFalse(!empty($result), format_string('Ensure the block @id does not appear on the page', array('@id' => $block->id()))); - } - - /** - * Find a block instance on the page. - * - * @param \Drupal\block\Plugin\Core\Entity\Block $block - * The block entity to find on the page. - * - * @return array - * The result from the xpath query. - */ - protected function findBlockInstance(Block $block) { - $config_id = explode('.', $block->id()); - $machine_name = array_pop($config_id); - return $this->xpath('//div[@id = :id]', array(':id' => 'block-' . $machine_name)); - } - - /** * Tests removing a block display. */ protected function testDeleteBlockDisplay() { diff --git a/core/modules/views/lib/Drupal/views/DisplayBag.php b/core/modules/views/lib/Drupal/views/DisplayBag.php index 72023f3..fccdf72 100644 --- a/core/modules/views/lib/Drupal/views/DisplayBag.php +++ b/core/modules/views/lib/Drupal/views/DisplayBag.php @@ -7,6 +7,7 @@ namespace Drupal\views; +use Drupal\Component\Plugin\Exception\PluginException; use Drupal\Component\Plugin\PluginBag; use Drupal\Component\Plugin\PluginManagerInterface; @@ -59,7 +60,7 @@ public function __destruct() { * Overrides \Drupal\Component\Plugin\PluginBag::clear(). */ public function clear() { - foreach ($this->pluginInstances as $display_id => $display) { + foreach (array_filter($this->pluginInstances) as $display_id => $display) { $display->destroy(); } @@ -77,11 +78,20 @@ protected function initializePlugin($display_id) { // Retrieve and initialize the new display handler with data. $display = &$this->view->storage->getDisplay($display_id); - $this->pluginInstances[$display_id] = $this->manager->createInstance($display['display_plugin']); + + try { + $this->pluginInstances[$display_id] = $this->manager->createInstance($display['display_plugin']); + } + // Catch any plugin exceptions that are thrown. So we can fail nicely if a + // display plugin isn't found. + catch (PluginException $e) { + $message = $e->getMessage(); + drupal_set_message(t('!message', array('!message' => $message)), 'warning'); + } + + // If no plugin instance has been created, return NULL. if (empty($this->pluginInstances[$display_id])) { - // Provide a 'default' handler as an emergency. This won't work well but - // it will keep things from crashing. - $this->pluginInstances[$display_id] = $this->manager->createInstance('default'); + return NULL; } $this->pluginInstances[$display_id]->initDisplay($this->view, $display); diff --git a/core/modules/views/lib/Drupal/views/Plugin/Block/ViewsBlock.php b/core/modules/views/lib/Drupal/views/Plugin/Block/ViewsBlock.php index 1e2bfd5..5b82a7b 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/Block/ViewsBlock.php +++ b/core/modules/views/lib/Drupal/views/Plugin/Block/ViewsBlock.php @@ -47,7 +47,6 @@ public function __construct(array $configuration, $plugin_id, array $plugin_defi list($name, $this->displayID) = explode('-', $delta, 2); // Load the view. $this->view = views_get_view($name); - $this->view->setDisplay($this->displayID); } /** @@ -73,15 +72,19 @@ public function form($form, &$form_state) { * Implements \Drupal\block\BlockBase::blockBuild(). */ protected function blockBuild() { - $output = $this->view->executeDisplay($this->displayID); - // Set the label to the title configured in the view. - $this->configuration['label'] = filter_xss_admin($this->view->getTitle()); - // Before returning the block output, convert it to a renderable array - // with contextual links. - $this->addContextualLinks($output); - - $this->view->destroy(); - return $output; + if ($output = $this->view->executeDisplay($this->displayID)) { + $output = $this->view->executeDisplay($this->displayID); + // Set the label to the title configured in the view. + $this->configuration['label'] = filter_xss_admin($this->view->getTitle()); + // Before returning the block output, convert it to a renderable array + // with contextual links. + $this->addContextualLinks($output); + + $this->view->destroy(); + return $output; + } + + return array(); } /** diff --git a/core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayTest.php b/core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayTest.php index 2c7230d..f53946e 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayTest.php @@ -19,14 +19,14 @@ class DisplayTest extends PluginTestBase { * * @var array */ - public static $testViews = array('test_filter_groups', 'test_get_attach_displays', 'test_view', 'test_display_more'); + public static $testViews = array('test_filter_groups', 'test_get_attach_displays', 'test_view', 'test_display_more', 'test_display_invalid'); /** * Modules to enable. * * @var array */ - public static $modules = array('views_ui'); + public static $modules = array('views_ui', 'node', 'block'); public static function getInfo() { return array( @@ -197,4 +197,52 @@ public function testReadMore() { $this->assertEqual($more_text, $expected_more_text, 'The right more text is chosen.'); } + /** + * Tests invalid display plugins. + */ + public function testInvalidDisplayPlugins() { + $this->drupalGet('test_display_invalid'); + $this->assertResponse(200); + + // Change the page plugin id to an invalid one. Bypass the entity system + // so no menu rebuild was executed (so the path is still available). + $config = config('views.view.test_display_invalid'); + $config->set('display.page_1.display_plugin', 'invalid'); + $config->save(); + + $this->drupalGet('test_display_invalid'); + $this->assertResponse(200); + $this->assertText(t('The plugin (invalid) did not specify an instance class.')); + + // Rebuild the menu, and ensure that the path is not accessible anymore. + menu_router_rebuild(); + + $this->drupalGet('test_display_invalid'); + $this->assertResponse(404); + + // Change the display plugin ID back to the correct ID. + $config = config('views.view.test_display_invalid'); + $config->set('display.page_1.display_plugin', 'page'); + $config->save(); + + // Place the block display. + $block = $this->drupalPlaceBlock('views_block:test_display_invalid-block_1', array(), array('title' => 'Invalid display')); + + $this->drupalGet(''); + $this->assertResponse(200); + $this->assertBlockAppears($block); + + // Change the block plugin ID to an invalid one. + $config = config('views.view.test_display_invalid'); + $config->set('display.block_1.display_plugin', 'invalid'); + $config->save(); + + // Test the page is still displayed, the block not present, and has the + // plugin warning message. + $this->drupalGet(''); + $this->assertResponse(200); + $this->assertText(t('The plugin (invalid) did not specify an instance class.')); + $this->assertNoBlockAppears($block); + } + } diff --git a/core/modules/views/lib/Drupal/views/Tests/ViewTestBase.php b/core/modules/views/lib/Drupal/views/Tests/ViewTestBase.php index 51a0c51..9041742 100644 --- a/core/modules/views/lib/Drupal/views/Tests/ViewTestBase.php +++ b/core/modules/views/lib/Drupal/views/Tests/ViewTestBase.php @@ -9,6 +9,7 @@ use Drupal\simpletest\WebTestBase; use Drupal\views\ViewExecutable; +use Drupal\block\Plugin\Core\Entity\Block; /** * Defines a base class for Views testing in the full web test environment. @@ -231,6 +232,43 @@ protected function executeView($view, $args = array()) { } /** + * Checks to see whether a block appears on the page. + * + * @param \Drupal\block\Plugin\Core\Entity\Block $block + * The block entity to find on the page. + */ + protected function assertBlockAppears(Block $block) { + $result = $this->findBlockInstance($block); + $this->assertTrue(!empty($result), format_string('Ensure the block @id appears on the page', array('@id' => $block->id()))); + } + + /** + * Checks to see whether a block does not appears on the page. + * + * @param \Drupal\block\Plugin\Core\Entity\Block $block + * The block entity to find on the page. + */ + protected function assertNoBlockAppears(Block $block) { + $result = $this->findBlockInstance($block); + $this->assertFalse(!empty($result), format_string('Ensure the block @id does not appear on the page', array('@id' => $block->id()))); + } + + /** + * Find a block instance on the page. + * + * @param \Drupal\block\Plugin\Core\Entity\Block $block + * The block entity to find on the page. + * + * @return array + * The result from the xpath query. + */ + protected function findBlockInstance(Block $block) { + $config_id = explode('.', $block->id()); + $machine_name = array_pop($config_id); + return $this->xpath('//div[@id = :id]', array(':id' => 'block-' . $machine_name)); + } + + /** * Returns the schema definition. */ protected function schemaDefinition() { diff --git a/core/modules/views/lib/Drupal/views/ViewExecutable.php b/core/modules/views/lib/Drupal/views/ViewExecutable.php index b4f76c2..89573da 100644 --- a/core/modules/views/lib/Drupal/views/ViewExecutable.php +++ b/core/modules/views/lib/Drupal/views/ViewExecutable.php @@ -677,10 +677,13 @@ public function setDisplay($display_id = NULL) { $this->rowPlugin = NULL; } - // Set a shortcut. - $this->display_handler = $this->displayHandlers->get($display_id); + if ($display = $this->displayHandlers->get($display_id)) { + // Set a shortcut. + $this->display_handler = $display; + return TRUE; + } - return TRUE; + return FALSE; } /** @@ -1476,7 +1479,7 @@ public function access($displays = NULL, $account = NULL) { $displays = (array)$displays; foreach ($displays as $display_id) { if ($this->displayHandlers->has($display_id)) { - if ($this->displayHandlers->get($display_id)->access($account)) { + if (($display = $this->displayHandlers->get($display_id)) && $display->access($account)) { return TRUE; } } diff --git a/core/modules/views/tests/views_test_config/test_views/views.view.test_display_invalid.yml b/core/modules/views/tests/views_test_config/test_views/views.view.test_display_invalid.yml new file mode 100644 index 0000000..5c0c76d --- /dev/null +++ b/core/modules/views/tests/views_test_config/test_views/views.view.test_display_invalid.yml @@ -0,0 +1,35 @@ +api_version: '3.0' +base_table: node +core: '8' +description: '' +status: '1' +display: + default: + display_options: + row: + type: fields + fields: + id: + id: nid + table: node + field: nid + plugin_id: numeric + display_plugin: default + display_title: Master + id: default + position: '0' + page_1: + display_options: + path: test_display_invalid + display_plugin: page + display_title: Page + id: page_1 + position: '0' + block_1: + display_plugin: block + id: block_1 + display_title: Block + position: '1' +label: '' +id: test_display_invalid +tag: '' diff --git a/core/modules/views/views.module b/core/modules/views/views.module index cf577bf..8acc4ff 100644 --- a/core/modules/views/views.module +++ b/core/modules/views/views.module @@ -435,10 +435,15 @@ function views_page($name, $display_id) { // Load the view and render it. if ($view = views_get_view($name)) { - return $view->executeDisplay($display_id, $args); + if ($output = $view->executeDisplay($display_id, $args)) { + return $output; + } + else { + return array(); + } } - // Fallback; if we get here no view was found or handler was not valid. + // Fallback if we get here no view was found. return MENU_NOT_FOUND; } diff --git a/core/modules/views_ui/lib/Drupal/views_ui/ViewFormControllerBase.php b/core/modules/views_ui/lib/Drupal/views_ui/ViewFormControllerBase.php index bc9c50f..c0fe439 100644 --- a/core/modules/views_ui/lib/Drupal/views_ui/ViewFormControllerBase.php +++ b/core/modules/views_ui/lib/Drupal/views_ui/ViewFormControllerBase.php @@ -101,12 +101,20 @@ public static function getAdminCSS() { * The display_id which is edited on the current request. */ public function getDisplayTabs(ViewUI $view) { + $executable = $view->get('executable'); + $executable->initDisplay(); $display_id = $this->displayID; $tabs = array(); // Create a tab for each display. - $displays = $view->get('display'); - foreach ($displays as $id => $display) { + foreach ($view->get('display') as $id => $display) { + // Get an instance of the display plugin, to make sure it will work in the + // UI. + $display_plugin = $executable->displayHandlers->get($id); + if (empty($display_plugin)) { + continue; + } + $tabs[$id] = array( '#theme' => 'menu_local_task', '#weight' => $display['position'], @@ -130,7 +138,7 @@ public function getDisplayTabs(ViewUI $view) { } // Mark the display tab as red to show validation errors. - $errors = $view->get('executable')->validate(); + $errors = $executable->validate(); foreach ($view->get('display') as $id => $display) { if (!empty($errors[$id])) { // Always show the tab.