Commit f37d3c80 authored by Dries's avatar Dries

Issue #1879774 by damiankloip, dawehner, effulgentsia: catch plugin exceptions...

Issue #1879774 by damiankloip, dawehner, effulgentsia: catch plugin exceptions for invalid views display plugins.
parent 31586f70
......@@ -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;
......@@ -47,43 +46,6 @@ protected function setUp() {
$this->enableViewsTestModule();
}
/**
* 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.
*/
......
......@@ -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);
......
......@@ -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();
}
/**
......
......@@ -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('<front>');
$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('<front>');
$this->assertResponse(200);
$this->assertText(t('The plugin (invalid) did not specify an instance class.'));
$this->assertNoBlockAppears($block);
}
}
......@@ -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.
......@@ -230,6 +231,43 @@ protected function executeView($view, $args = array()) {
$this->verbose('<pre>Executed view: ' . ((string) $view->build_info['query']) . '</pre>');
}
/**
* 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.
*/
......
......@@ -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;
}
}
......
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: ''
......@@ -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;
}
......
......@@ -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.
......
Markdown is supported
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