diff --git a/core/modules/node/config/views.view.content.yml b/core/modules/node/config/views.view.content.yml index 39f6d60..487b04b 100644 --- a/core/modules/node/config/views.view.content.yml +++ b/core/modules/node/config/views.view.content.yml @@ -279,6 +279,7 @@ display: text: Translate optional: '1' plugin_id: content_translation_link + provider: content_translation dropbutton: id: dropbutton table: views diff --git a/core/modules/views/lib/Drupal/views/Plugin/ViewsHandlerManager.php b/core/modules/views/lib/Drupal/views/Plugin/ViewsHandlerManager.php index 4ac2fac..0a1fb76 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/ViewsHandlerManager.php +++ b/core/modules/views/lib/Drupal/views/Plugin/ViewsHandlerManager.php @@ -77,7 +77,7 @@ public function __construct($handler_type, \Traversable $namespaces, ViewsData $ 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 @@ public function getHandler($item, $override = NULL) { } // Finally, use the 'broken' handler. - return $this->createInstance('broken'); + return $this->createInstance('broken', array('optional' => $optional, 'original_configuration' => $item)); } } diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php index 51d3a99..5912c76 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php +++ b/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php @@ -77,11 +77,19 @@ 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']); } /** @@ -152,6 +160,15 @@ protected function defineOptions() { } /** + * 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. */ public function adminLabel($short = FALSE) { diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/area/Broken.php b/core/modules/views/lib/Drupal/views/Plugin/views/area/Broken.php index 64a63a0..50cfb87 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/views/area/Broken.php +++ b/core/modules/views/lib/Drupal/views/Plugin/views/area/Broken.php @@ -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 @@ 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(); } @@ -33,9 +37,32 @@ public function render($empty = FALSE) { return array(); } public function buildOptionsForm(&$form, &$form_state) { - $form['markup'] = array( - '#markup' => '
' . 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.') . '
', + $args = array( + '@module' => $this->definition['original_configuration']['provider'], + '@table' => $this->definition['original_configuration']['table'], + '@field' => $this->definition['original_configuration']['field'], ); + + if ($this->isOptional()) { + $form['markup'] = array( + '#markup' => '
' . t('The handler for this item is optional. The following handler is optional: +- Module: @module +- Table: @table +- Field: @field + +Enabling the appropriate module will may solve this issue. Otherwise try to look whether there is a module update available.', $args) . '
', + ); + } + else { + $form['markup'] = array( + '#markup' => '
' . t('The handler for this item is broken or missing. The following handler is missing: +- Module: @module +- Table: @table +- Field: @field + +Enabling the appropriate module may solve this issue. Otherwise try to look whether there is a module update available.', $args) . '
', + ); + } } /** diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/argument/Broken.php b/core/modules/views/lib/Drupal/views/Plugin/views/argument/Broken.php index d74ab95..55c9c53 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/views/argument/Broken.php +++ b/core/modules/views/lib/Drupal/views/Plugin/views/argument/Broken.php @@ -19,16 +19,42 @@ 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 */ } public function buildOptionsForm(&$form, &$form_state) { - $form['markup'] = array( - '#markup' => '
' . 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.') . '
', + $args = array( + '@module' => $this->definition['original_configuration']['provider'], + '@table' => $this->definition['original_configuration']['table'], + '@field' => $this->definition['original_configuration']['field'], ); + + if ($this->isOptional()) { + $form['markup'] = array( + '#markup' => '
' . t('The handler for this item is optional. The following handler is optional: +- Module: @module +- Table: @table +- Field: @field + +Enabling the appropriate module will may solve this issue. Otherwise try to look whether there is a module update available.', $args) . '
', + ); + } + else { + $form['markup'] = array( + '#markup' => '
' . t('The handler for this item is broken or missing. The following handler is missing: +- Module: @module +- Table: @table +- Field: @field + +Enabling the appropriate module may solve this issue. Otherwise try to look whether there is a module update available.', $args) . '
', + ); + } } /** diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/field/Broken.php b/core/modules/views/lib/Drupal/views/Plugin/views/field/Broken.php index 6310248..9afa63b 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/views/field/Broken.php +++ b/core/modules/views/lib/Drupal/views/Plugin/views/field/Broken.php @@ -19,16 +19,42 @@ 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 */ } public function buildOptionsForm(&$form, &$form_state) { - $form['markup'] = array( - '#markup' => '
' . 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.') . '
', + $args = array( + '@module' => $this->definition['original_configuration']['provider'], + '@table' => $this->definition['original_configuration']['table'], + '@field' => $this->definition['original_configuration']['field'], ); + + if ($this->isOptional()) { + $form['markup'] = array( + '#markup' => '
' . t('The handler for this item is optional. The following handler is optional: +- Module: @module +- Table: @table +- Field: @field + +Enabling the appropriate module will may solve this issue. Otherwise try to look whether there is a module update available.', $args) . '
', + ); + } + else { + $form['markup'] = array( + '#markup' => '
' . t('The handler for this item is broken or missing. The following handler is missing: +- Module: @module +- Table: @table +- Field: @field + +Enabling the appropriate module may solve this issue. Otherwise try to look whether there is a module update available.', $args) . '
', + ); + } } /** diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/filter/Broken.php b/core/modules/views/lib/Drupal/views/Plugin/views/filter/Broken.php index 701cabb..23b9dab 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/views/filter/Broken.php +++ b/core/modules/views/lib/Drupal/views/Plugin/views/filter/Broken.php @@ -27,16 +27,42 @@ public function init(ViewExecutable $view, DisplayPluginBase $display, array &$o } 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 */ } public function buildOptionsForm(&$form, &$form_state) { - $form['markup'] = array( - '#markup' => '
' . 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.') . '
', + $args = array( + '@module' => $this->definition['original_configuration']['provider'], + '@table' => $this->definition['original_configuration']['table'], + '@field' => $this->definition['original_configuration']['field'], ); + + if ($this->isOptional()) { + $form['markup'] = array( + '#markup' => '
' . t('The handler for this item is optional. The following handler is optional: +- Module: @module +- Table: @table +- Field: @field + +Enabling the appropriate module will may solve this issue. Otherwise try to look whether there is a module update available.', $args) . '
', + ); + } + else { + $form['markup'] = array( + '#markup' => '
' . t('The handler for this item is broken or missing. The following handler is missing: +- Module: @module +- Table: @table +- Field: @field + +Enabling the appropriate module may solve this issue. Otherwise try to look whether there is a module update available.', $args) . '
', + ); + } } /** diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/relationship/Broken.php b/core/modules/views/lib/Drupal/views/Plugin/views/relationship/Broken.php index c891f00..6312fec 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/views/relationship/Broken.php +++ b/core/modules/views/lib/Drupal/views/Plugin/views/relationship/Broken.php @@ -19,16 +19,42 @@ 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 */ } public function buildOptionsForm(&$form, &$form_state) { - $form['markup'] = array( - '#markup' => '
' . 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.') . '
', + $args = array( + '@module' => $this->definition['original_configuration']['provider'], + '@table' => $this->definition['original_configuration']['table'], + '@field' => $this->definition['original_configuration']['field'], ); + + if ($this->isOptional()) { + $form['markup'] = array( + '#markup' => '
' . t('The handler for this item is optional. The following handler is optional: +- Module: @module +- Table: @table +- Field: @field + +Enabling the appropriate module will may solve this issue. Otherwise try to look whether there is a module update available.', $args) . '
', + ); + } + else { + $form['markup'] = array( + '#markup' => '
' . t('The handler for this item is broken or missing. The following handler is missing: +- Module: @module +- Table: @table +- Field: @field + +Enabling the appropriate module may solve this issue. Otherwise try to look whether there is a module update available.', $args) . '
', + ); + } } /** diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/sort/Broken.php b/core/modules/views/lib/Drupal/views/Plugin/views/sort/Broken.php index 341bc8b..4bd460d 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/views/sort/Broken.php +++ b/core/modules/views/lib/Drupal/views/Plugin/views/sort/Broken.php @@ -19,16 +19,42 @@ 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 */ } public function buildOptionsForm(&$form, &$form_state) { - $form['markup'] = array( - '#markup' => '
' . 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.') . '
', + $args = array( + '@module' => $this->definition['original_configuration']['provider'], + '@table' => $this->definition['original_configuration']['table'], + '@field' => $this->definition['original_configuration']['field'], ); + + if ($this->isOptional()) { + $form['markup'] = array( + '#markup' => '
' . t('The handler for this item is optional. The following handler is optional: +- Module: @module +- Table: @table +- Field: @field + +Enabling the appropriate module will may solve this issue. Otherwise try to look whether there is a module update available.', $args) . '
', + ); + } + else { + $form['markup'] = array( + '#markup' => '
' . t('The handler for this item is broken or missing. The following handler is missing: +- Module: @module +- Table: @table +- Field: @field + +Enabling the appropriate module may solve this issue. Otherwise try to look whether there is a module update available.', $args) . '
', + ); + } } /** diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_view_broken.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_view_broken.yml new file mode 100644 index 0000000..e5870e5 --- /dev/null +++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_view_broken.yml @@ -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: '' diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_view_optional.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_view_optional.yml new file mode 100644 index 0000000..36bf93e --- /dev/null +++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_view_optional.yml @@ -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: '' diff --git a/core/modules/views_ui/lib/Drupal/views_ui/Form/Ajax/ConfigItem.php b/core/modules/views_ui/lib/Drupal/views_ui/Form/Ajax/ConfigItem.php index 2be9fbc..26ad723 100644 --- a/core/modules/views_ui/lib/Drupal/views_ui/Form/Ajax/ConfigItem.php +++ b/core/modules/views_ui/lib/Drupal/views_ui/Form/Ajax/ConfigItem.php @@ -102,11 +102,12 @@ public function buildForm(array $form, array &$form_state) { // 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(); + } } } diff --git a/core/modules/views_ui/lib/Drupal/views_ui/Tests/HandlerTest.php b/core/modules/views_ui/lib/Drupal/views_ui/Tests/HandlerTest.php index 79b02bf..f9badc5 100644 --- a/core/modules/views_ui/lib/Drupal/views_ui/Tests/HandlerTest.php +++ b/core/modules/views_ui/lib/Drupal/views_ui/Tests/HandlerTest.php @@ -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( @@ -73,7 +74,7 @@ protected function viewsData() { /** * Tests UI CRUD. */ - public function testUICRUD() { + public function _testUICRUD() { $handler_types = ViewExecutable::viewsHandlerTypes(); foreach ($handler_types as $type => $type_info) { // Test adding handlers. @@ -143,4 +144,60 @@ public function testUICRUD() { $this->assertTrue(isset($display['display_options'][$type_info['plural']][$id]), 'Ensure the field was added to the view itself.'); } + /** + * Test 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))); + $args = array( + '@module' => 'views', + //'@table' => 'views_test_data', + //'@field' => 'id_broken', + ); + + $text = t('Broken/missing handler (Module: @module) …', $args); + + $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.'); + } + } + + /** + * Test 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))); + $args = array( + '@module' => 'views', + //'@table' => 'views_test_data', + //'@field' => 'id_broken', + ); + + $text = t('Optional handler is missing (Module: @module) …', $args); + + $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.'); + } + } + } diff --git a/core/modules/views_ui/lib/Drupal/views_ui/ViewEditFormController.php b/core/modules/views_ui/lib/Drupal/views_ui/ViewEditFormController.php index f926d8d..2d81547 100644 --- a/core/modules/views_ui/lib/Drupal/views_ui/ViewEditFormController.php +++ b/core/modules/views_ui/lib/Drupal/views_ui/ViewEditFormController.php @@ -1004,9 +1004,9 @@ public function getFormBucket(ViewUI $view, $type, $display) { $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; }