Issue #2016953 by dawehner, damiankloip, tim.plunkett: Indicate when an optional handler is missing, that it is not 'broken'.

8.0.x
webchick 2013-09-04 15:25:05 -07:00
parent 09fd48a204
commit e235776e24
14 changed files with 513 additions and 28 deletions

View File

@ -279,6 +279,7 @@ display:
text: Translate
optional: '1'
plugin_id: content_translation_link
provider: content_translation
dropbutton:
id: dropbutton
table: views

View File

@ -77,7 +77,7 @@ class ViewsHandlerManager extends PluginManagerBase {
public function getHandler($item, $override = NULL) {
$table = $item['table'];
$field = $item['field'];
$optional = isset($item['optional']) ? $item['optional'] : FALSE;
$optional = !empty($item['optional']);
// Get the plugin manager for this type.
$data = $this->viewsData->get($table);
@ -118,7 +118,7 @@ class ViewsHandlerManager extends PluginManagerBase {
}
// Finally, use the 'broken' handler.
return $this->createInstance('broken');
return $this->createInstance('broken', array('optional' => $optional, 'original_configuration' => $item));
}
}

View File

@ -76,12 +76,20 @@ abstract class HandlerBase extends PluginBase {
*/
public $relationship = NULL;
/**
* Whether or not this handler is optional.
*
* @var bool
*/
protected $optional = FALSE;
/**
* Constructs a Handler object.
*/
public function __construct(array $configuration, $plugin_id, array $plugin_definition) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->is_handler = TRUE;
$this->optional = !empty($configuration['optional']);
}
/**
@ -151,6 +159,15 @@ abstract class HandlerBase extends PluginBase {
return $options;
}
/**
* Returns whether this handler is optional.
*
* @return bool
*/
public function isOptional() {
return $this->optional;
}
/**
* Return a string representing this handler's name in the UI.
*/

View File

@ -8,6 +8,7 @@
namespace Drupal\views\Plugin\views\area;
use Drupal\Component\Annotation\PluginID;
use Drupal\views\ViewExecutable;
/**
* A special handler to take the place of missing or broken handlers.
@ -19,7 +20,10 @@ use Drupal\Component\Annotation\PluginID;
class Broken extends AreaPluginBase {
public function adminLabel($short = FALSE) {
return t('Broken/missing handler');
$args = array(
'@module' => $this->definition['original_configuration']['provider'],
);
return $this->isOptional() ? t('Optional handler is missing (Module: @module) …', $args) : t('Broken/missing handler (Module: @module) …', $args);
}
public function defineOptions() { return array(); }
@ -32,9 +36,41 @@ class Broken extends AreaPluginBase {
// Simply render nothing by returning an empty render array.
return array();
}
/**
* {@inheritdoc}
*/
public function buildOptionsForm(&$form, &$form_state) {
$form['markup'] = array(
'#markup' => '<div class="form-item description">' . t('The handler for this item is broken or missing and cannot be used. If a module provided the handler and was disabled, re-enabling the module may restore it. Otherwise, you should probably delete this item.') . '</div>',
if ($this->isOptional()) {
$description_top = t('The handler for this item is optional. The following details are available:');
}
else {
$description_top = t('The handler for this item is broken or missing. The following details are available:');
}
$items = array(
t('Module: @module', array('@module' => $this->definition['original_configuration']['provider'])),
t('Table: @table', array('@table' => $this->definition['original_configuration']['table'])),
t('Field: @field', array('@field' => $this->definition['original_configuration']['field'])),
);
$description_bottom = t('Enabling the appropriate module will may solve this issue. Otherwise, check to see if there is a module update available.');
$form['description'] = array(
'#type' => 'container',
'#attributes' => array(
'class' => array('form-item', 'description'),
),
'description_top' => array(
'#markup' => '<p>' . $description_top . '</p>',
),
'detail_list' => array(
'#theme' => 'item_list',
'#items' => $items,
),
'description_bottom' => array(
'#markup' => '<p>' . $description_bottom . '</p>',
),
);
}

View File

@ -19,15 +19,50 @@ use Drupal\Component\Annotation\PluginID;
class Broken extends ArgumentPluginBase {
public function adminLabel($short = FALSE) {
return t('Broken/missing handler');
$args = array(
'@module' => $this->definition['original_configuration']['provider'],
);
return $this->isOptional() ? t('Optional handler is missing (Module: @module) …', $args) : t('Broken/missing handler (Module: @module) …', $args);
}
public function defineOptions() { return array(); }
public function ensureMyTable() { /* No table to ensure! */ }
public function query($group_by = FALSE) { /* No query to run */ }
/**
* {@inheritdoc}
*/
public function buildOptionsForm(&$form, &$form_state) {
$form['markup'] = array(
'#markup' => '<div class="form-item description">' . t('The handler for this item is broken or missing and cannot be used. If a module provided the handler and was disabled, re-enabling the module may restore it. Otherwise, you should probably delete this item.') . '</div>',
if ($this->isOptional()) {
$description_top = t('The handler for this item is optional. The following details are available:');
}
else {
$description_top = t('The handler for this item is broken or missing. The following details are available:');
}
$items = array(
t('Module: @module', array('@module' => $this->definition['original_configuration']['provider'])),
t('Table: @table', array('@table' => $this->definition['original_configuration']['table'])),
t('Field: @field', array('@field' => $this->definition['original_configuration']['field'])),
);
$description_bottom = t('Enabling the appropriate module will may solve this issue. Otherwise, check to see if there is a module update available.');
$form['description'] = array(
'#type' => 'container',
'#attributes' => array(
'class' => array('form-item', 'description'),
),
'description_top' => array(
'#markup' => '<p>' . $description_top . '</p>',
),
'detail_list' => array(
'#theme' => 'item_list',
'#items' => $items,
),
'description_bottom' => array(
'#markup' => '<p>' . $description_bottom . '</p>',
),
);
}

View File

@ -19,15 +19,50 @@ use Drupal\Component\Annotation\PluginID;
class Broken extends FieldPluginBase {
public function adminLabel($short = FALSE) {
return t('Broken/missing handler');
$args = array(
'@module' => $this->definition['original_configuration']['provider'],
);
return $this->isOptional() ? t('Optional handler is missing (Module: @module) …', $args) : t('Broken/missing handler (Module: @module) …', $args);
}
public function defineOptions() { return array(); }
public function ensureMyTable() { /* No table to ensure! */ }
public function query($group_by = FALSE) { /* No query to run */ }
/**
* {@inheritdoc}
*/
public function buildOptionsForm(&$form, &$form_state) {
$form['markup'] = array(
'#markup' => '<div class="form-item description">' . t('The handler for this item is broken or missing and cannot be used. If a module provided the handler and was disabled, re-enabling the module may restore it. Otherwise, you should probably delete this item.') . '</div>',
if ($this->isOptional()) {
$description_top = t('The handler for this item is optional. The following details are available:');
}
else {
$description_top = t('The handler for this item is broken or missing. The following details are available:');
}
$items = array(
t('Module: @module', array('@module' => $this->definition['original_configuration']['provider'])),
t('Table: @table', array('@table' => $this->definition['original_configuration']['table'])),
t('Field: @field', array('@field' => $this->definition['original_configuration']['field'])),
);
$description_bottom = t('Enabling the appropriate module will may solve this issue. Otherwise, check to see if there is a module update available.');
$form['description'] = array(
'#type' => 'container',
'#attributes' => array(
'class' => array('form-item', 'description'),
),
'description_top' => array(
'#markup' => '<p>' . $description_top . '</p>',
),
'detail_list' => array(
'#theme' => 'item_list',
'#items' => $items,
),
'description_bottom' => array(
'#markup' => '<p>' . $description_bottom . '</p>',
),
);
}

View File

@ -27,15 +27,50 @@ class Broken extends FilterPluginBase {
}
public function adminLabel($short = FALSE) {
return t('Broken/missing handler');
$args = array(
'@module' => $this->definition['original_configuration']['provider'],
);
return $this->isOptional() ? t('Optional handler is missing (Module: @module) …', $args) : t('Broken/missing handler (Module: @module) …', $args);
}
public function defineOptions() { return array(); }
public function ensureMyTable() { /* No table to ensure! */ }
public function query($group_by = FALSE) { /* No query to run */ }
/**
* {@inheritdoc}
*/
public function buildOptionsForm(&$form, &$form_state) {
$form['markup'] = array(
'#markup' => '<div class="form-item description">' . t('The handler for this item is broken or missing and cannot be used. If a module provided the handler and was disabled, re-enabling the module may restore it. Otherwise, you should probably delete this item.') . '</div>',
if ($this->isOptional()) {
$description_top = t('The handler for this item is optional. The following details are available:');
}
else {
$description_top = t('The handler for this item is broken or missing. The following details are available:');
}
$items = array(
t('Module: @module', array('@module' => $this->definition['original_configuration']['provider'])),
t('Table: @table', array('@table' => $this->definition['original_configuration']['table'])),
t('Field: @field', array('@field' => $this->definition['original_configuration']['field'])),
);
$description_bottom = t('Enabling the appropriate module will may solve this issue. Otherwise, check to see if there is a module update available.');
$form['description'] = array(
'#type' => 'container',
'#attributes' => array(
'class' => array('form-item', 'description'),
),
'description_top' => array(
'#markup' => '<p>' . $description_top . '</p>',
),
'detail_list' => array(
'#theme' => 'item_list',
'#items' => $items,
),
'description_bottom' => array(
'#markup' => '<p>' . $description_bottom . '</p>',
),
);
}

View File

@ -19,15 +19,50 @@ use Drupal\Component\Annotation\PluginID;
class Broken extends RelationshipPluginBase {
public function adminLabel($short = FALSE) {
return t('Broken/missing handler');
$args = array(
'@module' => $this->definition['original_configuration']['provider'],
);
return $this->isOptional() ? t('Optional handler is missing (Module: @module) …', $args) : t('Broken/missing handler (Module: @module) …', $args);
}
public function defineOptions() { return array(); }
public function ensureMyTable() { /* No table to ensure! */ }
public function query() { /* No query to run */ }
/**
* {@inheritdoc}
*/
public function buildOptionsForm(&$form, &$form_state) {
$form['markup'] = array(
'#markup' => '<div class="form-item description">' . t('The handler for this item is broken or missing and cannot be used. If a module provided the handler and was disabled, re-enabling the module may restore it. Otherwise, you should probably delete this item.') . '</div>',
if ($this->isOptional()) {
$description_top = t('The handler for this item is optional. The following details are available:');
}
else {
$description_top = t('The handler for this item is broken or missing. The following details are available:');
}
$items = array(
t('Module: @module', array('@module' => $this->definition['original_configuration']['provider'])),
t('Table: @table', array('@table' => $this->definition['original_configuration']['table'])),
t('Field: @field', array('@field' => $this->definition['original_configuration']['field'])),
);
$description_bottom = t('Enabling the appropriate module will may solve this issue. Otherwise, check to see if there is a module update available.');
$form['description'] = array(
'#type' => 'container',
'#attributes' => array(
'class' => array('form-item', 'description'),
),
'description_top' => array(
'#markup' => '<p>' . $description_top . '</p>',
),
'detail_list' => array(
'#theme' => 'item_list',
'#items' => $items,
),
'description_bottom' => array(
'#markup' => '<p>' . $description_bottom . '</p>',
),
);
}

View File

@ -19,15 +19,50 @@ use Drupal\Component\Annotation\PluginID;
class Broken extends SortPluginBase {
public function adminLabel($short = FALSE) {
return t('Broken/missing handler');
$args = array(
'@module' => $this->definition['original_configuration']['provider'],
);
return $this->isOptional() ? t('Optional handler is missing (Module: @module) …', $args) : t('Broken/missing handler (Module: @module) …', $args);
}
public function defineOptions() { return array(); }
public function ensureMyTable() { /* No table to ensure! */ }
public function query($group_by = FALSE) { /* No query to run */ }
/**
* {@inheritdoc}
*/
public function buildOptionsForm(&$form, &$form_state) {
$form['markup'] = array(
'#markup' => '<div class="form-item description">' . t('The handler for this item is broken or missing and cannot be used. If a module provided the handler and was disabled, re-enabling the module may restore it. Otherwise, you should probably delete this item.') . '</div>',
if ($this->isOptional()) {
$description_top = t('The handler for this item is optional. The following details are available:');
}
else {
$description_top = t('The handler for this item is broken or missing. The following details are available:');
}
$items = array(
t('Module: @module', array('@module' => $this->definition['original_configuration']['provider'])),
t('Table: @table', array('@table' => $this->definition['original_configuration']['table'])),
t('Field: @field', array('@field' => $this->definition['original_configuration']['field'])),
);
$description_bottom = t('Enabling the appropriate module will may solve this issue. Otherwise, check to see if there is a module update available.');
$form['description'] = array(
'#type' => 'container',
'#attributes' => array(
'class' => array('form-item', 'description'),
),
'description_top' => array(
'#markup' => '<p>' . $description_top . '</p>',
),
'detail_list' => array(
'#theme' => 'item_list',
'#items' => $items,
),
'description_bottom' => array(
'#markup' => '<p>' . $description_bottom . '</p>',
),
);
}

View File

@ -0,0 +1,89 @@
base_table: views_test_data
core: '8'
description: ''
status: '1'
display:
default:
display_options:
defaults:
fields: '0'
pager: '0'
pager_options: '0'
sorts: '0'
fields:
id_broken:
field: id_broken
id: id_broken
relationship: none
table: views_test_data
plugin_id: numeric
provider: views
filters:
id_broken:
field: id_broken
id: id_broken
relationship: none
table: views_test_data
plugin_id: numeric
provider: views
arguments:
id_broken:
field: id_broken
id: id_broken
relationship: none
table: views_test_data
plugin_id: numeric
provider: views
sorts:
id_broken:
field: id_broken
id: id_broken
relationship: none
table: views_test_data
plugin_id: numeric
order: ASC
provider: views
relationships:
id_broken:
field: id_broken
id: id_broken
relationship: none
table: views_test_data
plugin_id: numeric
provider: views
header:
id_broken:
field: id_broken
id: id_broken
relationship: none
table: views_test_data
plugin_id: numeric
provider: views
footer:
id_broken:
field: id_broken
id: id_broken
relationship: none
table: views_test_data
plugin_id: numeric
provider: views
empty:
id_broken:
field: id_broken
id: id_broken
relationship: none
table: views_test_data
plugin_id: numeric
provider: views
pager:
options:
offset: '0'
type: none
pager_options: { }
display_plugin: default
display_title: Master
id: default
position: '0'
label: 'Test view'
id: test_view_broken
tag: ''

View File

@ -0,0 +1,97 @@
base_table: views_test_data
core: '8'
description: ''
status: '1'
display:
default:
display_options:
defaults:
fields: '0'
pager: '0'
pager_options: '0'
sorts: '0'
fields:
id_optional:
field: id_optional
id: id_optional
relationship: none
table: views_test_data
plugin_id: numeric
optional: 1
provider: views
filters:
id_optional:
field: id_optional
id: id_optional
relationship: none
table: views_test_data
plugin_id: numeric
optional: 1
provider: views
arguments:
id_optional:
field: id_optional
id: id_optional
relationship: none
table: views_test_data
plugin_id: numeric
optional: 1
provider: views
sorts:
id_optional:
field: id_optional
id: id_optional
relationship: none
table: views_test_data
plugin_id: numeric
order: ASC
optional: 1
provider: views
relationships:
id_optional:
field: id_optional
id: id_optional
relationship: none
table: views_test_data
plugin_id: numeric
optional: 1
provider: views
header:
id_optional:
field: id_optional
id: id_optional
relationship: none
table: views_test_data
plugin_id: numeric
optional: 1
provider: views
footer:
id_optional:
field: id_optional
id: id_optional
relationship: none
table: views_test_data
plugin_id: numeric
optional: 1
provider: views
empty:
id_optional:
field: id_optional
id: id_optional
relationship: none
table: views_test_data
plugin_id: numeric
optional: 1
provider: views
pager:
options:
offset: '0'
type: none
pager_options: { }
display_plugin: default
display_title: Master
id: default
position: '0'
label: 'Test view'
id: test_view_optional
tag: ''

View File

@ -102,11 +102,12 @@ class ConfigItem extends ViewsFormBase {
// If this relationship is valid for this type, add it to the list.
$data = Views::viewsData()->get($relationship['table']);
$base = $data[$relationship['field']]['relationship']['base'];
$base_fields = Views::viewsDataHelper()->fetchFields($base, $form_state['type'], $executable->display_handler->useGroupBy());
if (isset($base_fields[$item['table'] . '.' . $item['field']])) {
$relationship_handler->init($executable, $executable->display_handler, $relationship);
$relationship_options[$relationship['id']] = $relationship_handler->adminLabel();
if (isset($data[$relationship['field']]['relationship']['base']) && $base = $data[$relationship['field']]['relationship']['base']) {
$base_fields = Views::viewsDataHelper()->fetchFields($base, $form_state['type'], $executable->display_handler->useGroupBy());
if (isset($base_fields[$item['table'] . '.' . $item['field']])) {
$relationship_handler->init($executable, $executable->display_handler, $relationship);
$relationship_options[$relationship['id']] = $relationship_handler->adminLabel();
}
}
}

View File

@ -7,6 +7,7 @@
namespace Drupal\views_ui\Tests;
use Drupal\Component\Utility\String;
use Drupal\views\ViewExecutable;
/**
@ -21,7 +22,7 @@ class HandlerTest extends UITestBase {
*
* @var array
*/
public static $testViews = array('test_view_empty');
public static $testViews = array('test_view_empty', 'test_view_broken', 'test_view_optional');
public static function getInfo() {
return array(
@ -143,4 +144,72 @@ class HandlerTest extends UITestBase {
$this->assertTrue(isset($display['display_options'][$type_info['plural']][$id]), 'Ensure the field was added to the view itself.');
}
/**
* Tests broken handlers.
*/
public function testBrokenHandlers() {
$handler_types = ViewExecutable::viewsHandlerTypes();
foreach ($handler_types as $type => $type_info) {
$this->drupalGet('admin/structure/views/view/test_view_broken/edit');
$href = "admin/structure/views/nojs/config-item/test_view_broken/default/$type/id_broken";
$result = $this->xpath('//a[contains(@href, :href)]', array(':href' => $href));
$this->assertEqual(count($result), 1, String::format('Handler (%type) edit link found.', array('%type' => $type)));
$text = t('Broken/missing handler (Module: @module) …', array('@module' => 'views'));
$this->assertIdentical((string) $result[0], $text, 'Ensure the broken handler text was found.');
$this->drupalGet($href);
$result = $this->xpath('//h1');
$this->assertTrue(strpos((string) $result[0], $text) !== FALSE, 'Ensure the broken handler text was found.');
$description_args = array(
'@module' => 'views',
'@table' => 'views_test_data',
'@field' => 'id_broken',
);
foreach ($description_args as $token => $value) {
$this->assertNoText($token, String::format('Raw @token token placeholder not found.', array('@token' => $token)));
$this->assertText($value, String::format('Replaced @token value found.', array('@token' => $token)));
}
}
}
/**
* Tests optional handlers.
*/
public function testOptionalHandlers() {
$handler_types = ViewExecutable::viewsHandlerTypes();
foreach ($handler_types as $type => $type_info) {
$this->drupalGet('admin/structure/views/view/test_view_optional/edit');
$href = "admin/structure/views/nojs/config-item/test_view_optional/default/$type/id_optional";
$result = $this->xpath('//a[contains(@href, :href)]', array(':href' => $href));
$this->assertEqual(count($result), 1, String::format('Handler (%type) edit link found.', array('%type' => $type)));
$text = t('Optional handler is missing (Module: @module) …', array('@module' => 'views'));
$this->assertIdentical((string) $result[0], $text, 'Ensure the optional handler link text was found.');
$this->drupalGet($href);
$result = $this->xpath('//h1');
$this->assertTrue(strpos((string) $result[0], $text) !== FALSE, 'Ensure the optional handler title was found.');
$description_args = array(
'@module' => 'views',
'@table' => 'views_test_data',
'@field' => 'id_optional',
);
foreach ($description_args as $token => $value) {
$this->assertNoText($token, String::format('Raw @token token placeholder not found.', array('@token' => $token)));
$this->assertText($value, String::format('Replaced @token value found.', array('@token' => $token)));
}
}
}
}

View File

@ -1004,9 +1004,9 @@ class ViewEditFormController extends ViewFormControllerBase {
$build['fields'][$id]['#theme'] = 'views_ui_display_tab_setting';
$handler = $executable->display_handler->getHandler($type, $id);
if (empty($handler)) {
if ($handler->broken()) {
$build['fields'][$id]['#class'][] = 'broken';
$field_name = $this->t('Broken/missing handler: @table > @field', array('@table' => $field['table'], '@field' => $field['field']));
$field_name = $handler->adminLabel();
$build['fields'][$id]['#link'] = l($field_name, "admin/structure/views/nojs/config-item/{$view->id()}/{$display['id']}/$type/$id", array('attributes' => array('class' => array('views-ajax-link')), 'html' => TRUE));
continue;
}