Commit f42515e2 authored by webchick's avatar webchick

Issue #1798574 by tim.plunkett: Refactor Views UI to be a form controller.

parent a213f310
...@@ -250,7 +250,7 @@ protected function build_filters(&$form, &$form_state) { ...@@ -250,7 +250,7 @@ protected function build_filters(&$form, &$form_state) {
parent::build_filters($form, $form_state); parent::build_filters($form, $form_state);
$entity_info = $this->entity_info; $entity_info = $this->entity_info;
$selected_bundle = views_ui_get_selected($form_state, array('show', 'type'), 'all', $form['displays']['show']['type']); $selected_bundle = static::getSelected($form_state, array('show', 'type'), 'all', $form['displays']['show']['type']);
// Add the "tagged with" filter to the view. // Add the "tagged with" filter to the view.
......
...@@ -23,6 +23,11 @@ ...@@ -23,6 +23,11 @@
* module = "views", * module = "views",
* controller_class = "Drupal\views\ViewStorageController", * controller_class = "Drupal\views\ViewStorageController",
* list_controller_class = "Drupal\views_ui\ViewListController", * list_controller_class = "Drupal\views_ui\ViewListController",
* form_controller_class = {
* "edit" = "Drupal\views_ui\ViewEditFormController",
* "add" = "Drupal\views_ui\ViewAddFormController",
* "preview" = "Drupal\views_ui\ViewPreviewFormController"
* },
* config_prefix = "views.view", * config_prefix = "views.view",
* fieldable = FALSE, * fieldable = FALSE,
* entity_keys = { * entity_keys = {
...@@ -39,7 +44,7 @@ class View extends ConfigEntityBase implements ViewStorageInterface { ...@@ -39,7 +44,7 @@ class View extends ConfigEntityBase implements ViewStorageInterface {
* *
* @var string * @var string
*/ */
public $base_table = 'node'; protected $base_table = 'node';
/** /**
* The name of the view. * The name of the view.
...@@ -53,7 +58,7 @@ class View extends ConfigEntityBase implements ViewStorageInterface { ...@@ -53,7 +58,7 @@ class View extends ConfigEntityBase implements ViewStorageInterface {
* *
* @var string * @var string
*/ */
public $description = ''; protected $description = '';
/** /**
* The "tags" of a view. * The "tags" of a view.
...@@ -63,7 +68,7 @@ class View extends ConfigEntityBase implements ViewStorageInterface { ...@@ -63,7 +68,7 @@ class View extends ConfigEntityBase implements ViewStorageInterface {
* *
* @var string * @var string
*/ */
public $tag = ''; protected $tag = '';
/** /**
* The human readable name of the view. * The human readable name of the view.
...@@ -77,14 +82,14 @@ class View extends ConfigEntityBase implements ViewStorageInterface { ...@@ -77,14 +82,14 @@ class View extends ConfigEntityBase implements ViewStorageInterface {
* *
* @var int * @var int
*/ */
public $core = DRUPAL_CORE_COMPATIBILITY; protected $core = DRUPAL_CORE_COMPATIBILITY;
/** /**
* The views API version this view was created by. * The views API version this view was created by.
* *
* @var string * @var string
*/ */
public $api_version = VIEWS_API_VERSION; protected $api_version = VIEWS_API_VERSION;
/** /**
* Stores all display handlers of this view. * Stores all display handlers of this view.
...@@ -94,14 +99,14 @@ class View extends ConfigEntityBase implements ViewStorageInterface { ...@@ -94,14 +99,14 @@ class View extends ConfigEntityBase implements ViewStorageInterface {
* *
* @var array * @var array
*/ */
public $display; protected $display;
/** /**
* The name of the base field to use. * The name of the base field to use.
* *
* @var string * @var string
*/ */
public $base_field = 'nid'; protected $base_field = 'nid';
/** /**
* Returns whether the view's status is disabled or not. * Returns whether the view's status is disabled or not.
...@@ -111,7 +116,7 @@ class View extends ConfigEntityBase implements ViewStorageInterface { ...@@ -111,7 +116,7 @@ class View extends ConfigEntityBase implements ViewStorageInterface {
* *
* @var bool * @var bool
*/ */
public $disabled = FALSE; protected $disabled = FALSE;
/** /**
* The UUID for this entity. * The UUID for this entity.
...@@ -132,58 +137,18 @@ class View extends ConfigEntityBase implements ViewStorageInterface { ...@@ -132,58 +137,18 @@ class View extends ConfigEntityBase implements ViewStorageInterface {
* *
* @var string * @var string
*/ */
public $module = 'views'; protected $module = 'views';
/** /**
* Stores the executable version of this view. * Overrides Drupal\Core\Entity\EntityInterface::get().
*
* @param Drupal\views\ViewExecutable $executable
* The executable version of this view.
*/
public function setExecutable(ViewExecutable $executable) {
$this->executable = $executable;
}
/**
* Retrieves the executable version of this view.
*
* @param bool $reset
* Get a new Drupal\views\ViewExecutable instance.
* @param bool $ui
* If this should return Drupal\views_ui\ViewUI instead.
*
* @return Drupal\views\ViewExecutable
* The executable version of this view.
*/ */
public function getExecutable($reset = FALSE, $ui = FALSE) { public function get($property_name, $langcode = NULL) {
if (!isset($this->executable) || $reset) { // Ensure that an executable View is available.
// @todo Remove this approach and use proper dependency injection. if ($property_name == 'executable' && !isset($this->{$property_name})) {
if ($ui) { $this->set('executable', new ViewExecutable($this));
$executable = new ViewUI($this);
}
else {
$executable = new ViewExecutable($this);
}
$this->setExecutable($executable);
} }
return $this->executable;
}
/**
* Initializes the display.
*
* @todo Inspect calls to this and attempt to clean up.
* @see Drupal\views\ViewExecutable::initDisplay()
*/
public function initDisplay() {
$this->getExecutable()->initDisplay();
}
/** return parent::get($property_name, $langcode);
* Returns the name of the module implementing this view.
*/
public function getModule() {
return $this->module;
} }
/** /**
...@@ -199,7 +164,7 @@ public function uri() { ...@@ -199,7 +164,7 @@ public function uri() {
* Overrides Drupal\Core\Entity\EntityInterface::id(). * Overrides Drupal\Core\Entity\EntityInterface::id().
*/ */
public function id() { public function id() {
return $this->name; return $this->get('name');
} }
/** /**
...@@ -231,11 +196,8 @@ public function isEnabled() { ...@@ -231,11 +196,8 @@ public function isEnabled() {
* When a certain view doesn't have a human readable name return the machine readable name. * When a certain view doesn't have a human readable name return the machine readable name.
*/ */
public function getHumanName() { public function getHumanName() {
if (!empty($this->human_name)) { if (!$human_name = $this->get('human_name')) {
$human_name = $this->human_name; $human_name = $this->get('name');
}
else {
$human_name = $this->name;
} }
return $human_name; return $human_name;
} }
...@@ -344,7 +306,7 @@ protected function generateDisplayId($plugin_id) { ...@@ -344,7 +306,7 @@ protected function generateDisplayId($plugin_id) {
*/ */
public function &newDisplay($plugin_id = 'page', $title = NULL, $id = NULL) { public function &newDisplay($plugin_id = 'page', $title = NULL, $id = NULL) {
$id = $this->addDisplay($plugin_id, $title, $id); $id = $this->addDisplay($plugin_id, $title, $id);
return $this->getExecutable()->newDisplay($id); return $this->get('executable')->newDisplay($id);
} }
/** /**
......
...@@ -2156,7 +2156,7 @@ public function submitOptionsForm(&$form, &$form_state) { ...@@ -2156,7 +2156,7 @@ public function submitOptionsForm(&$form, &$form_state) {
$access = array('type' => $form_state['values']['access']['type']); $access = array('type' => $form_state['values']['access']['type']);
$this->setOption('access', $access); $this->setOption('access', $access);
if ($plugin->usesOptions()) { if ($plugin->usesOptions()) {
$this->view->addFormToStack('display', $this->display['id'], array('access_options')); $form_state['view']->addFormToStack('display', $this->display['id'], array('access_options'));
} }
} }
} }
...@@ -2179,7 +2179,7 @@ public function submitOptionsForm(&$form, &$form_state) { ...@@ -2179,7 +2179,7 @@ public function submitOptionsForm(&$form, &$form_state) {
$cache = array('type' => $form_state['values']['cache']['type']); $cache = array('type' => $form_state['values']['cache']['type']);
$this->setOption('cache', $cache); $this->setOption('cache', $cache);
if ($plugin->usesOptions()) { if ($plugin->usesOptions()) {
$this->view->addFormToStack('display', $this->display['id'], array('cache_options')); $form_state['view']->addFormToStack('display', $this->display['id'], array('cache_options'));
} }
} }
} }
...@@ -2240,7 +2240,7 @@ public function submitOptionsForm(&$form, &$form_state) { ...@@ -2240,7 +2240,7 @@ public function submitOptionsForm(&$form, &$form_state) {
// send ajax form to options page if we use it. // send ajax form to options page if we use it.
if ($plugin->usesOptions()) { if ($plugin->usesOptions()) {
$this->view->addFormToStack('display', $this->display['id'], array('row_options')); $form_state['view']->addFormToStack('display', $this->display['id'], array('row_options'));
} }
} }
} }
...@@ -2256,7 +2256,7 @@ public function submitOptionsForm(&$form, &$form_state) { ...@@ -2256,7 +2256,7 @@ public function submitOptionsForm(&$form, &$form_state) {
$this->setOption($section, $row); $this->setOption($section, $row);
// send ajax form to options page if we use it. // send ajax form to options page if we use it.
if ($plugin->usesOptions()) { if ($plugin->usesOptions()) {
$this->view->addFormToStack('display', $this->display['id'], array('style_options')); $form_state['view']->addFormToStack('display', $this->display['id'], array('style_options'));
} }
} }
} }
...@@ -2290,7 +2290,7 @@ public function submitOptionsForm(&$form, &$form_state) { ...@@ -2290,7 +2290,7 @@ public function submitOptionsForm(&$form, &$form_state) {
$exposed_form = array('type' => $form_state['values']['exposed_form']['type'], 'options' => array()); $exposed_form = array('type' => $form_state['values']['exposed_form']['type'], 'options' => array());
$this->setOption('exposed_form', $exposed_form); $this->setOption('exposed_form', $exposed_form);
if ($plugin->usesOptions()) { if ($plugin->usesOptions()) {
$this->view->addFormToStack('display', $this->display['id'], array('exposed_form_options')); $form_state['view']->addFormToStack('display', $this->display['id'], array('exposed_form_options'));
} }
} }
} }
...@@ -2317,7 +2317,7 @@ public function submitOptionsForm(&$form, &$form_state) { ...@@ -2317,7 +2317,7 @@ public function submitOptionsForm(&$form, &$form_state) {
$pager = array('type' => $form_state['values']['pager']['type'], 'options' => $plugin->options); $pager = array('type' => $form_state['values']['pager']['type'], 'options' => $plugin->options);
$this->setOption('pager', $pager); $this->setOption('pager', $pager);
if ($plugin->usesOptions()) { if ($plugin->usesOptions()) {
$this->view->addFormToStack('display', $this->display['id'], array('pager_options')); $form_state['view']->addFormToStack('display', $this->display['id'], array('pager_options'));
} }
} }
} }
......
...@@ -400,7 +400,7 @@ public function submitOptionsForm(&$form, &$form_state) { ...@@ -400,7 +400,7 @@ public function submitOptionsForm(&$form, &$form_state) {
$this->setOption('menu', $form_state['values']['menu']); $this->setOption('menu', $form_state['values']['menu']);
// send ajax form to options page if we use it. // send ajax form to options page if we use it.
if ($form_state['values']['menu']['type'] == 'default tab') { if ($form_state['values']['menu']['type'] == 'default tab') {
$this->view->addFormToStack('display', $this->display['id'], array('tab_options')); $form_state['view']->addFormToStack('display', $this->display['id'], array('tab_options'));
} }
break; break;
case 'tab_options': case 'tab_options':
......
...@@ -164,7 +164,7 @@ public function buildOptionsForm(&$form, &$form_state) { ...@@ -164,7 +164,7 @@ public function buildOptionsForm(&$form, &$form_state) {
function get_temporary_view() { function get_temporary_view() {
$view = entity_create('view', array('base_table' => $this->definition['base'])); $view = entity_create('view', array('base_table' => $this->definition['base']));
$view->addDisplay('default'); $view->addDisplay('default');
return $view->getExecutable(); return $view->get('executable');
} }
/** /**
......
...@@ -74,11 +74,11 @@ protected function defineOptions() { ...@@ -74,11 +74,11 @@ protected function defineOptions() {
public function buildOptionsForm(&$form, &$form_state) { public function buildOptionsForm(&$form, &$form_state) {
parent::buildOptionsForm($form, $form_state); parent::buildOptionsForm($form, $form_state);
if (isset($this->base_table)) { if (isset($this->base_table)) {
$view = &$form_state['view']; $executable = $form_state['view']->get('executable');
// A whole bunch of code to figure out what relationships are valid for // A whole bunch of code to figure out what relationships are valid for
// this item. // this item.
$relationships = $view->display_handler->getOption('relationships'); $relationships = $executable->display_handler->getOption('relationships');
$relationship_options = array(); $relationship_options = array();
foreach ($relationships as $relationship) { foreach ($relationships as $relationship) {
...@@ -88,7 +88,7 @@ public function buildOptionsForm(&$form, &$form_state) { ...@@ -88,7 +88,7 @@ public function buildOptionsForm(&$form, &$form_state) {
$data = views_fetch_data($relationship['table']); $data = views_fetch_data($relationship['table']);
$base = $data[$relationship['field']]['relationship']['base']; $base = $data[$relationship['field']]['relationship']['base'];
if ($base == $this->base_table) { if ($base == $this->base_table) {
$relationship_handler->init($view, $relationship); $relationship_handler->init($executable, $relationship);
$relationship_options[$relationship['id']] = $relationship_handler->label(); $relationship_options[$relationship['id']] = $relationship_handler->label();
} }
} }
......
...@@ -257,7 +257,7 @@ function build_form(array $form, array &$form_state) { ...@@ -257,7 +257,7 @@ function build_form(array $form, array &$form_state) {
'#options' => $style_options, '#options' => $style_options,
); );
$style_form = &$form['displays']['page']['options']['style']; $style_form = &$form['displays']['page']['options']['style'];
$style_form['style_plugin']['#default_value'] = views_ui_get_selected($form_state, array('page', 'style', 'style_plugin'), 'default', $style_form['style_plugin']); $style_form['style_plugin']['#default_value'] = static::getSelected($form_state, array('page', 'style', 'style_plugin'), 'default', $style_form['style_plugin']);
// Changing this dropdown updates $form['displays']['page']['options'] via // Changing this dropdown updates $form['displays']['page']['options'] via
// AJAX. // AJAX.
views_ui_add_ajax_trigger($style_form, 'style_plugin', array('displays', 'page', 'options')); views_ui_add_ajax_trigger($style_form, 'style_plugin', array('displays', 'page', 'options'));
...@@ -394,7 +394,7 @@ function build_form(array $form, array &$form_state) { ...@@ -394,7 +394,7 @@ function build_form(array $form, array &$form_state) {
'#options' => $style_options, '#options' => $style_options,
); );
$style_form = &$form['displays']['block']['options']['style']; $style_form = &$form['displays']['block']['options']['style'];
$style_form['style_plugin']['#default_value'] = views_ui_get_selected($form_state, array('block', 'style', 'style_plugin'), 'default', $style_form['style_plugin']); $style_form['style_plugin']['#default_value'] = static::getSelected($form_state, array('block', 'style', 'style_plugin'), 'default', $style_form['style_plugin']);
// Changing this dropdown updates $form['displays']['block']['options'] via // Changing this dropdown updates $form['displays']['block']['options'] via
// AJAX. // AJAX.
views_ui_add_ajax_trigger($style_form, 'style_plugin', array('displays', 'block', 'options')); views_ui_add_ajax_trigger($style_form, 'style_plugin', array('displays', 'block', 'options'));
...@@ -415,6 +415,85 @@ function build_form(array $form, array &$form_state) { ...@@ -415,6 +415,85 @@ function build_form(array $form, array &$form_state) {
return $form; return $form;
} }
/**
* Gets the current value of a #select element, from within a form constructor function.
*
* This function is intended for use in highly dynamic forms (in particular the
* add view wizard) which are rebuilt in different ways depending on which
* triggering element (AJAX or otherwise) was most recently fired. For example,
* sometimes it is necessary to decide how to build one dynamic form element
* based on the value of a different dynamic form element that may not have
* even been present on the form the last time it was submitted. This function
* takes care of resolving those conflicts and gives you the proper current
* value of the requested #select element.
*
* By necessity, this function sometimes uses non-validated user input from
* $form_state['input'] in making its determination. Although it performs some
* minor validation of its own, it is not complete. The intention is that the
* return value of this function should only be used to help decide how to
* build the current form the next time it is reloaded, not to be saved as if
* it had gone through the normal, final form validation process. Do NOT use
* the results of this function for any other purpose besides deciding how to
* build the next version of the form.
*
* @param $form_state
* The standard associative array containing the current state of the form.
* @param $parents
* An array of parent keys that point to the part of the submitted form
* values that are expected to contain the element's value (in the case where
* this form element was actually submitted). In a simple case (assuming
* #tree is TRUE throughout the form), if the select element is located in
* $form['wrapper']['select'], so that the submitted form values would
* normally be found in $form_state['values']['wrapper']['select'], you would
* pass array('wrapper', 'select') for this parameter.
* @param $default_value
* The default value to return if the #select element does not currently have
* a proper value set based on the submitted input.
* @param $element
* An array representing the current version of the #select element within
* the form.
*
* @return
* The current value of the #select element. A common use for this is to feed
* it back into $element['#default_value'] so that the form will be rendered
* with the correct value selected.
*/
public static function getSelected($form_state, $parents, $default_value, $element) {
// For now, don't trust this to work on anything but a #select element.
if (!isset($element['#type']) || $element['#type'] != 'select' || !isset($element['#options'])) {
return $default_value;
}
// If there is a user-submitted value for this element that matches one of
// the currently available options attached to it, use that. We need to check
// $form_state['input'] rather than $form_state['values'] here because the
// triggering element often has the #limit_validation_errors property set to
// prevent unwanted errors elsewhere on the form. This means that the
// $form_state['values'] array won't be complete. We could make it complete
// by adding each required part of the form to the #limit_validation_errors
// property individually as the form is being built, but this is difficult to
// do for a highly dynamic and extensible form. This method is much simpler.
if (!empty($form_state['input'])) {
$key_exists = NULL;
$submitted = drupal_array_get_nested_value($form_state['input'], $parents, $key_exists);
// Check that the user-submitted value is one of the allowed options before
// returning it. This is not a substitute for actual form validation;
// rather it is necessary because, for example, the same select element
// might have #options A, B, and C under one set of conditions but #options
// D, E, F under a different set of conditions. So the form submission
// might have occurred with option A selected, but when the form is rebuilt
// option A is no longer one of the choices. In that case, we don't want to
// use the value that was submitted anymore but rather fall back to the
// default value.
if ($key_exists && in_array($submitted, array_keys($element['#options']))) {
return $submitted;
}
}
// Fall back on returning the default value if nothing was returned above.
return $default_value;
}
/** /**
* Adds the style options to the wizard form. * Adds the style options to the wizard form.
* *
...@@ -443,7 +522,7 @@ protected function build_form_style(array &$form, array &$form_state, $type) { ...@@ -443,7 +522,7 @@ protected function build_form_style(array &$form, array &$form_state, $type) {
// if it's available (since that's the most common use case). // if it's available (since that's the most common use case).
$block_with_linked_titles_available = ($type == 'block' && isset($options['titles_linked'])); $block_with_linked_titles_available = ($type == 'block' && isset($options['titles_linked']));
$default_value = $block_with_linked_titles_available ? 'titles_linked' : key($options); $default_value = $block_with_linked_titles_available ? 'titles_linked' : key($options);
$style_form['row_plugin']['#default_value'] = views_ui_get_selected($form_state, array($type, 'style', 'row_plugin'), $default_value, $style_form['row_plugin']); $style_form['row_plugin']['#default_value'] = static::getSelected($form_state, array($type, 'style', 'row_plugin'), $default_value, $style_form['row_plugin']);
// Changing this dropdown updates the individual row options via AJAX. // Changing this dropdown updates the individual row options via AJAX.
views_ui_add_ajax_trigger($style_form, 'row_plugin', array('displays', $type, 'options', 'style', 'row_options')); views_ui_add_ajax_trigger($style_form, 'row_plugin', array('displays', $type, 'options', 'style', 'row_options'));
...@@ -493,7 +572,7 @@ protected function build_filters(&$form, &$form_state) { ...@@ -493,7 +572,7 @@ protected function build_filters(&$form, &$form_state) {
'#title' => t('of type'), '#title' => t('of type'),
'#options' => $options, '#options' => $options,
); );
$selected_bundle = views_ui_get_selected($form_state, array('show', 'type'), 'all', $form['displays']['show']['type']); $selected_bundle = static::getSelected($form_state, array('show', 'type'), 'all', $form['displays']['show']['type']);
$form['displays']['show']['type']['#default_value'] = $selected_bundle; $form['displays']['show']['type']['#default_value'] = $selected_bundle;
// Changing this dropdown updates the entire content of $form['displays'] // Changing this dropdown updates the entire content of $form['displays']
// via AJAX, since each bundle might have entirely different fields // via AJAX, since each bundle might have entirely different fields
...@@ -1066,7 +1145,7 @@ protected function set_validated_view(array $form, array &$form_state, ViewUI $v ...@@ -1066,7 +1145,7 @@ protected function set_validated_view(array $form, array &$form_state, ViewUI $v
*/ */
public function validateView(array $form, array &$form_state) { public function validateView(array $form, array &$form_state) {
$view = $this->instantiate_view($form, $form_state); $view = $this->instantiate_view($form, $form_state);
$errors = $view->validate(); $errors = $view->get('executable')->validate();
if (!is_array($errors) || empty($errors)) { if (!is_array($errors) || empty($errors)) {
$this->set_validated_view($form, $form_state, $view); $this->set_validated_view($form, $form_state, $view);
return array(); return array();
......
...@@ -59,7 +59,7 @@ public function testHandlers() { ...@@ -59,7 +59,7 @@ public function testHandlers() {
} }
$view = views_new_view(); $view = views_new_view();
$view->base_table = $base_table; $view->set('base_table', $base_table);
$view = new ViewExecutable($view); $view = new ViewExecutable($view);
// @todo The groupwise relationship is currently broken. // @todo The groupwise relationship is currently broken.
......
...@@ -163,12 +163,12 @@ public function testLoadFunctions() { ...@@ -163,12 +163,12 @@ public function testLoadFunctions() {
// Test $exclude_view parameter. // Test $exclude_view parameter.
$this->assertFalse(array_key_exists('archive', views_get_views_as_options(TRUE, 'all', 'archive')), 'View excluded from options based on name'); $this->assertFalse(array_key_exists('archive', views_get_views_as_options(TRUE, 'all', 'archive')), 'View excluded from options based on name');
$this->assertFalse(array_key_exists('archive:default', views_get_views_as_options(FALSE, 'all', 'archive:default')), 'View display excluded from options based on name'); $this->assertFalse(array_key_exists('archive:default', views_get_views_as_options(FALSE, 'all', 'archive:default')), 'View display excluded from options based on name');
$this->assertFalse(array_key_exists('archive', views_get_views_as_options(TRUE, 'all', $archive->getExecutable())), 'View excluded from options based on object'); $this->assertFalse(array_key_exists('archive', views_get_views_as_options(TRUE, 'all', $archive->get('executable'))), 'View excluded from options based on object');
// Test the $opt_group parameter. // Test the $opt_group parameter.
$expected_opt_groups = array(); $expected_opt_groups = array();
foreach ($all_views as $id => $view) { foreach ($all_views as $id => $view) {
foreach ($view->display as $display_id => $display) { foreach ($view->get('display') as $display_id => $display) {
$expected_opt_groups[$view->id()][$view->id() . ':' . $display['id']] = t('@view : @display', array('@view' => $view->id(), '@display' => $display['id'])); $expected_opt_groups[$view->id()][$view->id() . ':' . $display['id']] = t('@view : @display', array('@view' => $view->id(), '@display' => $display['id']));
} }
} }
...@@ -205,7 +205,7 @@ function testStatusFunctions() { ...@@ -205,7 +205,7 @@ function testStatusFunctions() {
protected function formatViewOptions(array $views = array()) {