Issue #1919142 by tim.plunkett, dawehner: Convert Views UI AJAX forms to use FormInterface.

8.0.x
webchick 2013-03-03 17:01:21 -05:00
parent e357af4a14
commit 057dd5110f
28 changed files with 2169 additions and 1626 deletions

View File

@ -365,7 +365,7 @@ class Field extends FieldPluginBase {
'#ajax' => array(
'path' => views_ui_build_form_url($form_state),
),
'#submit' => array('views_ui_config_item_form_submit_temporary'),
'#submit' => array(array($this, 'submitTemporaryForm')),
'#executes_submit_callback' => TRUE,
);

View File

@ -94,7 +94,7 @@ class View extends ConfigEntityBase implements ViewStorageInterface {
*
* @var array
*/
protected $display;
protected $display = array();
/**
* The name of the base field to use.

View File

@ -937,4 +937,86 @@ abstract class HandlerBase extends PluginBase {
return $handler;
}
/**
* Displays the Expose form.
*/
public function displayExposedForm($form, &$form_state) {
$item = &$this->options;
// flip
$item['exposed'] = empty($item['exposed']);
// If necessary, set new defaults:
if ($item['exposed']) {
$this->defaultExposeOptions();
}
$form_state['view']->get('executable')->setItem($form_state['display_id'], $form_state['type'], $form_state['id'], $item);
$form_state['view']->addFormToStack($form_state['form_key'], $form_state['display_id'], $form_state['type'], $form_state['id'], TRUE, TRUE);
views_ui_cache_set($form_state['view']);
$form_state['rerender'] = TRUE;
$form_state['rebuild'] = TRUE;
$form_state['force_expose_options'] = TRUE;
}
/**
* A submit handler that is used for storing temporary items when using
* multi-step changes, such as ajax requests.
*/
public function submitTemporaryForm($form, &$form_state) {
// Run it through the handler's submit function.
$this->submitOptionsForm($form['options'], $form_state);
$item = $this->options;
$types = ViewExecutable::viewsHandlerTypes();
// For footer/header $handler_type is area but $type is footer/header.
// For all other handle types it's the same.
$handler_type = $type = $form_state['type'];
if (!empty($types[$type]['type'])) {
$handler_type = $types[$type]['type'];
}
$override = NULL;
$executable = $form_state['view']->get('executable');
if ($executable->display_handler->useGroupBy() && !empty($item['group_type'])) {
if (empty($executable->query)) {
$executable->initQuery();
}
$aggregate = $executable->query->get_aggregation_info();
if (!empty($aggregate[$item['group_type']]['handler'][$type])) {
$override = $aggregate[$item['group_type']]['handler'][$type];
}
}
// Create a new handler and unpack the options from the form onto it. We
// can use that for storage.
$handler = views_get_handler($item['table'], $item['field'], $handler_type, $override);
$handler->init($executable, $executable->display_handler, $item);
// Add the incoming options to existing options because items using
// the extra form may not have everything in the form here.
$options = $form_state['values']['options'] + $this->options;
// This unpacks only options that are in the definition, ensuring random
// extra stuff on the form is not sent through.
$handler->unpackOptions($handler->options, $options, NULL, FALSE);
// Store the item back on the view.
$executable = $form_state['view']->get('executable');
$executable->temporary_options[$type][$form_state['id']] = $handler->options;
// @todo Decide if \Drupal\views_ui\Form\Ajax\ViewsFormBase::getForm() is
// perhaps the better place to fix the issue.
// \Drupal\views_ui\Form\Ajax\ViewsFormBase::getForm() drops the current
// form from the stack, even if it's an #ajax. So add the item back to the top
// of the stack.
$form_state['view']->addFormToStack($form_state['form_key'], $form_state['display_id'], $type, $item['id'], TRUE);
$form_state['rerender'] = TRUE;
$form_state['rebuild'] = TRUE;
// Write to cache
views_ui_cache_set($form_state['view']);
}
}

View File

@ -1837,7 +1837,7 @@ abstract class DisplayPluginBase extends PluginBase {
$form['box']['change'] = array(
'#type' => 'submit',
'#value' => t('Change theme'),
'#submit' => array('views_ui_edit_display_form_change_theme'),
'#submit' => array(array($this, 'changeThemeForm')),
);
$form['analysis'] = array(
@ -1851,7 +1851,7 @@ abstract class DisplayPluginBase extends PluginBase {
$form['rescan_button']['button'] = array(
'#type' => 'submit',
'#value' => t('Rescan template files'),
'#submit' => array('views_ui_config_item_form_rescan'),
'#submit' => array(array($this, 'rescanThemes')),
);
$form['rescan_button']['markup'] = array(
'#markup' => '<div class="description">' . t("<strong>Important!</strong> When adding, removing, or renaming template files, it is necessary to make Drupal aware of the changes by making it rescan the files on your system. By clicking this button you clear Drupal's theme registry and thereby trigger this rescanning process. The highlighted templates above will then reflect the new state of your system.") . '</div>',
@ -2020,6 +2020,38 @@ abstract class DisplayPluginBase extends PluginBase {
}
}
/**
* Submit hook to clear Drupal's theme registry (thereby triggering
* a templates rescan).
*/
public function rescanThemes($form, &$form_state) {
drupal_theme_rebuild();
// The 'Theme: Information' page is about to be shown again. That page
// analyzes the output of theme_get_registry(). However, this latter
// function uses an internal cache (which was initialized before we
// called drupal_theme_rebuild()) so it won't reflect the
// current state of our theme registry. The only way to clear that cache
// is to re-initialize the theme system:
unset($GLOBALS['theme']);
drupal_theme_initialize();
$form_state['rerender'] = TRUE;
$form_state['rebuild'] = TRUE;
}
/**
* Displays the Change Theme form.
*/
public function changeThemeForm($form, &$form_state) {
// This is just a temporary variable.
$form_state['view']->theme = $form_state['values']['theme'];
views_ui_cache_set($form_state['view']);
$form_state['rerender'] = TRUE;
$form_state['rebuild'] = TRUE;
}
/**
* Format a list of theme templates for output by the theme info helper.
*/
@ -2129,7 +2161,7 @@ abstract class DisplayPluginBase extends PluginBase {
$access = array('type' => $form_state['values']['access']['type']);
$this->setOption('access', $access);
if ($plugin->usesOptions()) {
$form_state['view']->addFormToStack('display', $this->display['id'], array('access_options'));
$form_state['view']->addFormToStack('display', $this->display['id'], 'access_options');
}
}
}
@ -2152,7 +2184,7 @@ abstract class DisplayPluginBase extends PluginBase {
$cache = array('type' => $form_state['values']['cache']['type']);
$this->setOption('cache', $cache);
if ($plugin->usesOptions()) {
$form_state['view']->addFormToStack('display', $this->display['id'], array('cache_options'));
$form_state['view']->addFormToStack('display', $this->display['id'], 'cache_options');
}
}
}
@ -2213,7 +2245,7 @@ abstract class DisplayPluginBase extends PluginBase {
// send ajax form to options page if we use it.
if ($plugin->usesOptions()) {
$form_state['view']->addFormToStack('display', $this->display['id'], array('row_options'));
$form_state['view']->addFormToStack('display', $this->display['id'], 'row_options');
}
}
}
@ -2229,7 +2261,7 @@ abstract class DisplayPluginBase extends PluginBase {
$this->setOption($section, $row);
// send ajax form to options page if we use it.
if ($plugin->usesOptions()) {
$form_state['view']->addFormToStack('display', $this->display['id'], array('style_options'));
$form_state['view']->addFormToStack('display', $this->display['id'], 'style_options');
}
}
}
@ -2263,7 +2295,7 @@ abstract class DisplayPluginBase extends PluginBase {
$exposed_form = array('type' => $form_state['values']['exposed_form']['type'], 'options' => array());
$this->setOption('exposed_form', $exposed_form);
if ($plugin->usesOptions()) {
$form_state['view']->addFormToStack('display', $this->display['id'], array('exposed_form_options'));
$form_state['view']->addFormToStack('display', $this->display['id'], 'exposed_form_options');
}
}
}
@ -2290,7 +2322,7 @@ abstract class DisplayPluginBase extends PluginBase {
$pager = array('type' => $form_state['values']['pager']['type'], 'options' => $plugin->options);
$this->setOption('pager', $pager);
if ($plugin->usesOptions()) {
$form_state['view']->addFormToStack('display', $this->display['id'], array('pager_options'));
$form_state['view']->addFormToStack('display', $this->display['id'], 'pager_options');
}
}
}

View File

@ -399,7 +399,7 @@ class Page extends PathPluginBase {
$this->setOption('menu', $form_state['values']['menu']);
// send ajax form to options page if we use it.
if ($form_state['values']['menu']['type'] == 'default tab') {
$form_state['view']->addFormToStack('display', $this->display['id'], array('tab_options'));
$form_state['view']->addFormToStack('display', $this->display['id'], 'tab_options');
}
break;
case 'tab_options':

View File

@ -398,7 +398,7 @@ abstract class FilterPluginBase extends HandlerBase {
'#limit_validation_errors' => array(),
'#type' => 'submit',
'#value' => t('Grouped filters'),
'#submit' => array('views_ui_config_item_form_build_group'),
'#submit' => array(array($this, 'buildGroupForm')),
);
$form['group_button']['radios']['radios']['#default_value'] = 0;
}
@ -407,11 +407,35 @@ abstract class FilterPluginBase extends HandlerBase {
'#limit_validation_errors' => array(),
'#type' => 'submit',
'#value' => t('Single filter'),
'#submit' => array('views_ui_config_item_form_build_group'),
'#submit' => array(array($this, 'buildGroupForm')),
);
$form['group_button']['radios']['radios']['#default_value'] = 1;
}
}
/**
* Displays the Build Group form.
*/
public function buildGroupForm($form, &$form_state) {
$item = &$this->options;
// flip. If the filter was a group, set back to a standard filter.
$item['is_grouped'] = empty($item['is_grouped']);
// If necessary, set new defaults:
if ($item['is_grouped']) {
$this->build_group_options();
}
$form_state['view']->get('executable')->setItem($form_state['display_id'], $form_state['type'], $form_state['id'], $item);
$form_state['view']->addFormToStack($form_state['form_key'], $form_state['display_id'], $form_state['type'], $form_state['id'], TRUE, TRUE);
views_ui_cache_set($form_state['view']);
$form_state['rerender'] = TRUE;
$form_state['rebuild'] = TRUE;
$form_state['force_build_group_options'] = TRUE;
}
/**
* Shortcut to display the expose/hide button.
*/
@ -443,7 +467,7 @@ abstract class FilterPluginBase extends HandlerBase {
'#limit_validation_errors' => array(),
'#type' => 'submit',
'#value' => t('Expose filter'),
'#submit' => array('views_ui_config_item_form_expose'),
'#submit' => array(array($this, 'displayExposedForm')),
);
$form['expose_button']['checkbox']['checkbox']['#default_value'] = 0;
}
@ -455,7 +479,7 @@ abstract class FilterPluginBase extends HandlerBase {
'#limit_validation_errors' => array(),
'#type' => 'submit',
'#value' => t('Hide filter'),
'#submit' => array('views_ui_config_item_form_expose'),
'#submit' => array(array($this, 'displayExposedForm')),
);
$form['expose_button']['checkbox']['checkbox']['#default_value'] = 1;
}
@ -1038,7 +1062,7 @@ abstract class FilterPluginBase extends HandlerBase {
'#suffix' => '</div>',
'#type' => 'submit',
'#value' => t('Add another item'),
'#submit' => array('views_ui_config_item_form_add_group'),
'#submit' => array(array($this, 'addGroupForm')),
);
$js = array();
@ -1058,6 +1082,23 @@ abstract class FilterPluginBase extends HandlerBase {
}
}
/**
* Add a new group to the exposed filter groups.
*/
public function addGroupForm($form, &$form_state) {
$item = &$this->options;
// Add a new row.
$item['group_info']['group_items'][] = array();
$form_state['view']->get('executable')->setItem($form_state['display_id'], $form_state['type'], $form_state['id'], $item);
views_ui_cache_set($form_state['view']);
$form_state['rerender'] = TRUE;
$form_state['rebuild'] = TRUE;
$form_state['force_build_group_options'] = TRUE;
}
/**
* Make some translations to a form item to make it more suitable to

View File

@ -117,7 +117,7 @@ abstract class SortPluginBase extends HandlerBase {
'#limit_validation_errors' => array(),
'#type' => 'submit',
'#value' => t('Expose sort'),
'#submit' => array('views_ui_config_item_form_expose'),
'#submit' => array(array($this, 'displayExposedForm')),
);
$form['expose_button']['checkbox']['checkbox']['#default_value'] = 0;
}
@ -129,7 +129,7 @@ abstract class SortPluginBase extends HandlerBase {
'#limit_validation_errors' => array(),
'#type' => 'submit',
'#value' => t('Hide sort'),
'#submit' => array('views_ui_config_item_form_expose'),
'#submit' => array(array($this, 'displayExposedForm')),
);
$form['expose_button']['checkbox']['checkbox']['#default_value'] = 1;
}

View File

@ -103,7 +103,9 @@ class PagerTest extends PluginTestBase {
'pager_options[items_per_page]' => 10,
);
$this->drupalPost('admin/structure/views/nojs/display/test_store_pager_settings/default/pager_options', $edit, t('Apply'));
$this->assertText('20 items');
$this->assertText('10 items', 'The default value has been changed.');
$this->drupalGet('admin/structure/views/view/test_store_pager_settings/edit/page_1');
$this->assertText('20 items', 'The original value remains unchanged.');
}

View File

@ -99,7 +99,7 @@ class HandlerTest extends UITestBase {
$this->assertUrl($edit_handler_url, array(), 'The user got redirected to the handler edit form.');
$this->drupalPost(NULL, array(), t('Apply'));
$this->assertUrl('admin/structure/views/view/test_view_empty/edit', array(), 'The user got redirected to the views edit form.');
$this->assertUrl('admin/structure/views/view/test_view_empty/edit/default', array(), 'The user got redirected to the views edit form.');
$this->assertLinkByHref($edit_handler_url, 0, 'The handler edit link appears in the UI.');

View File

@ -81,23 +81,26 @@ class ViewStorageController extends ConfigStorageController {
* The view entity to attach default displays options.
*/
protected function mergeDefaultDisplaysOptions(EntityInterface $entity) {
if (isset($entity->display) && is_array($entity->display)) {
$displays = array();
foreach ($entity->get('display') as $key => $options) {
$options += array(
'display_options' => array(),
'display_plugin' => NULL,
'id' => NULL,
'display_title' => '',
'position' => NULL,
);
// Add the defaults for the display.
$displays[$key] = $options;
}
$entity->set('display', $displays);
$displays = array();
foreach ($entity->get('display') as $key => $options) {
$options += array(
'display_options' => array(),
'display_plugin' => NULL,
'id' => NULL,
'display_title' => '',
'position' => NULL,
);
// Add the defaults for the display.
$displays[$key] = $options;
}
// Sort the displays.
uasort($displays, function ($display1, $display2) {
if ($display1['position'] != $display2['position']) {
return $display1['position'] < $display2['position'] ? -1 : 1;
}
return 0;
});
$entity->set('display', $displays);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,187 @@
<?php
/**
* @file
* Contains \Drupal\views_ui\Form\Ajax\AddItem.
*/
namespace Drupal\views_ui\Form\Ajax;
use Drupal\views\ViewExecutable;
use Drupal\views\ViewStorageInterface;
/**
* Provides a form for adding an item in the Views UI.
*/
class AddItem extends ViewsFormBase {
/**
* Constucts a new AddItem object.
*/
public function __construct($type = NULL) {
$this->setType($type);
}
/**
* Implements \Drupal\views_ui\Form\Ajax\ViewsFormInterface::getFormKey().
*/
public function getFormKey() {
return 'add-item';
}
/**
* Overrides \Drupal\views_ui\Form\Ajax\ViewsFormBase::getForm().
*/
public function getForm(ViewStorageInterface $view, $display_id, $js, $type = NULL) {
$this->setType($type);
return parent::getForm($view, $display_id, $js);
}
/**
* Implements \Drupal\Core\Form\FormInterface::getFormID().
*/
public function getFormID() {
return 'views_ui_add_item_form';
}
/**
* Implements \Drupal\Core\Form\FormInterface::buildForm().
*/
public function buildForm(array $form, array &$form_state) {
$view = &$form_state['view'];
$display_id = $form_state['display_id'];
$type = $form_state['type'];
$form = array(
'options' => array(
'#theme_wrappers' => array('container'),
'#attributes' => array('class' => array('scroll')),
),
);
$executable = $view->get('executable');
if (!$executable->setDisplay($display_id)) {
views_ajax_error(t('Invalid display id @display', array('@display' => $display_id)));
}
$display = &$executable->displayHandlers->get($display_id);
$types = ViewExecutable::viewsHandlerTypes();
$ltitle = $types[$type]['ltitle'];
$section = $types[$type]['plural'];
if (!empty($types[$type]['type'])) {
$type = $types[$type]['type'];
}
$form['#title'] = t('Add @type', array('@type' => $ltitle));
$form['#section'] = $display_id . 'add-item';
// Add the display override dropdown.
views_ui_standard_display_dropdown($form, $form_state, $section);
// Figure out all the base tables allowed based upon what the relationships provide.
$base_tables = $executable->getBaseTables();
$options = views_fetch_fields(array_keys($base_tables), $type, $display->useGroupBy(), $form_state['type']);
if (!empty($options)) {
$form['override']['controls'] = array(
'#theme_wrappers' => array('container'),
'#id' => 'views-filterable-options-controls',
'#attributes' => array('class' => array('container-inline')),
);
$form['override']['controls']['options_search'] = array(
'#type' => 'textfield',
'#title' => t('Search'),
);
$groups = array('all' => t('- All -'));
$form['override']['controls']['group'] = array(
'#type' => 'select',
'#title' => t('Type'),
'#options' => array(),
);
$form['options']['name'] = array(
'#prefix' => '<div class="views-radio-box form-checkboxes views-filterable-options">',
'#suffix' => '</div>',
'#tree' => TRUE,
'#default_value' => 'all',
);
// Group options first to simplify the usage of #states.
$grouped_options = array();
foreach ($options as $key => $option) {
$group = preg_replace('/[^a-z0-9]/', '-', strtolower($option['group']));
$groups[$group] = $option['group'];
$grouped_options[$group][$key] = $option;
if (!empty($option['aliases']) && is_array($option['aliases'])) {
foreach ($option['aliases'] as $id => $alias) {
if (empty($alias['base']) || !empty($base_tables[$alias['base']])) {
$copy = $option;
$copy['group'] = $alias['group'];
$copy['title'] = $alias['title'];
if (isset($alias['help'])) {
$copy['help'] = $alias['help'];
}
$group = preg_replace('/[^a-z0-9]/', '-', strtolower($copy['group']));
$groups[$group] = $copy['group'];
$grouped_options[$group][$key . '$' . $id] = $copy;
}
}
}
}
foreach ($grouped_options as $group => $group_options) {
$zebra = 0;
foreach ($group_options as $key => $option) {
$zebra_class = ($zebra % 2) ? 'odd' : 'even';
$form['options']['name'][$key] = array(
'#type' => 'checkbox',
'#title' => t('!group: !field', array('!group' => $option['group'], '!field' => $option['title'])),
'#description' => $option['help'],
'#return_value' => $key,
'#prefix' => "<div class='$zebra_class filterable-option'>",
'#suffix' => '</div>',
'#states' => array(
'visible' => array(
array(
':input[name="override[controls][group]"]' => array('value' => 'all'),
),
array(
':input[name="override[controls][group]"]' => array('value' => $group),
),
)
)
);
$zebra++;
}
}
$form['override']['controls']['group']['#options'] = $groups;
}
else {
$form['options']['markup'] = array(
'#markup' => '<div class="form-item">' . t('There are no @types available to add.', array('@types' => $ltitle)) . '</div>',
);
}
// Add a div to show the selected items
$form['selected'] = array(
'#type' => 'item',
'#markup' => '<div class="views-selected-options"></div>',
'#title' => t('Selected') . ':',
'#theme_wrappers' => array('form_element', 'views_ui_container'),
'#attributes' => array('class' => array('container-inline', 'views-add-form-selected')),
);
$view->getStandardButtons($form, $form_state, 'views_ui_add_item_form', t('Add and configure @types', array('@types' => $ltitle)));
// Remove the default submit function.
$form['buttons']['submit']['#submit'] = array_filter($form['buttons']['submit']['#submit'], function($var) {
return !(is_array($var) && isset($var[1]) && $var[1] == 'standardSubmit');
});
$form['buttons']['submit']['#submit'][] = array($view, 'submitItemAdd');
return $form;
}
}

View File

@ -0,0 +1,63 @@
<?php
/**
* @file
* Contains \Drupal\views_ui\Form\Ajax\Analyze.
*/
namespace Drupal\views_ui\Form\Ajax;
use Drupal\views_ui\ViewUI;
use Drupal\views\Analyzer;
/**
* Displays analysis information for a view.
*/
class Analyze extends ViewsFormBase {
/**
* Implements \Drupal\views_ui\Form\Ajax\ViewsFormInterface::getFormKey().
*/
public function getFormKey() {
return 'analyze';
}
/**
* Implements \Drupal\Core\Form\FormInterface::getFormID().
*/
public function getFormID() {
return 'views_ui_analyze_view_form';
}
/**
* Implements \Drupal\Core\Form\FormInterface::buildForm().
*/
public function buildForm(array $form, array &$form_state) {
$view = &$form_state['view'];
$form['#title'] = t('View analysis');
$form['#section'] = 'analyze';
$analyzer = drupal_container()->get('views.analyzer');
$messages = $analyzer->getMessages($view->get('executable'));
$form['analysis'] = array(
'#prefix' => '<div class="form-item">',
'#suffix' => '</div>',
'#markup' => $analyzer->formatMessages($messages),
);
// Inform the standard button function that we want an OK button.
$form_state['ok_button'] = TRUE;
$view->getStandardButtons($form, $form_state, 'views_ui_analyze_view_form');
return $form;
}
/**
* Overrides \Drupal\views_ui\Form\Ajax\ViewsFormBase::submitForm().
*/
public function submitForm(array &$form, array &$form_state) {
$form_state['redirect'] = 'admin/structure/views/view/' . $form_state['view']->id() . '/edit';
}
}

View File

@ -0,0 +1,268 @@
<?php
/**
* @file
* Contains \Drupal\views_ui\Form\Ajax\ConfigItem.
*/
namespace Drupal\views_ui\Form\Ajax;
use Drupal\views\ViewStorageInterface;
use Drupal\views\ViewExecutable;
/**
* Provides a form for configuring an item in the Views UI.
*/
class ConfigItem extends ViewsFormBase {
/**
* Constucts a new ConfigItem object.
*/
public function __construct($type = NULL, $id = NULL) {
$this->setType($type);
$this->setID($id);
}
/**
* Implements \Drupal\views_ui\Form\Ajax\ViewsFormInterface::getFormKey().
*/
public function getFormKey() {
return 'config-item';
}
/**
* Overrides \Drupal\views_ui\Form\Ajax\ViewsFormBase::getForm().
*/
public function getForm(ViewStorageInterface $view, $display_id, $js, $type = NULL, $id = NULL) {
$this->setType($type);
$this->setID($id);
return parent::getForm($view, $display_id, $js);
}
/**
* Implements \Drupal\Core\Form\FormInterface::getFormID().
*/
public function getFormID() {
return 'views_ui_config_item_form';
}
/**
* Implements \Drupal\Core\Form\FormInterface::buildForm().
*/
public function buildForm(array $form, array &$form_state) {
$view = &$form_state['view'];
$display_id = $form_state['display_id'];
$type = $form_state['type'];
$id = $form_state['id'];
$form = array(
'options' => array(
'#tree' => TRUE,
'#theme_wrappers' => array('container'),
'#attributes' => array('class' => array('scroll')),
),
);
$executable = $view->get('executable');
$save_ui_cache = FALSE;
if (!$executable->setDisplay($display_id)) {
views_ajax_error(t('Invalid display id @display', array('@display' => $display_id)));
}
$item = $executable->getItem($display_id, $type, $id);
if ($item) {
$handler = $executable->display_handler->getHandler($type, $id);
if (empty($handler)) {
$form['markup'] = array('#markup' => t("Error: handler for @table > @field doesn't exist!", array('@table' => $item['table'], '@field' => $item['field'])));
}
else {
$types = ViewExecutable::viewsHandlerTypes();
// If this item can come from the default display, show a dropdown
// that lets the user choose which display the changes should apply to.
if ($executable->display_handler->defaultableSections($types[$type]['plural'])) {
$form_state['section'] = $types[$type]['plural'];
views_ui_standard_display_dropdown($form, $form_state, $form_state['section']);
}
// A whole bunch of code to figure out what relationships are valid for
// this item.
$relationships = $executable->display_handler->getOption('relationships');
$relationship_options = array();
foreach ($relationships as $relationship) {
// relationships can't link back to self. But also, due to ordering,
// relationships can only link to prior relationships.
if ($type == 'relationship' && $id == $relationship['id']) {
break;
}
$relationship_handler = views_get_handler($relationship['table'], $relationship['field'], 'relationship');
// ignore invalid/broken relationships.
if (empty($relationship_handler)) {
continue;
}
// If this relationship is valid for this type, add it to the list.
$data = drupal_container()->get('views.views_data')->get($relationship['table']);
$base = $data[$relationship['field']]['relationship']['base'];
$base_fields = views_fetch_fields($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->label();
}
}
if (!empty($relationship_options)) {
// Make sure the existing relationship is even valid. If not, force
// it to none.
$base_fields = views_fetch_fields($view->get('base_table'), $form_state['type'], $executable->display_handler->useGroupBy());
if (isset($base_fields[$item['table'] . '.' . $item['field']])) {
$relationship_options = array_merge(array('none' => t('Do not use a relationship')), $relationship_options);
}
$rel = empty($item['relationship']) ? 'none' : $item['relationship'];
if (empty($relationship_options[$rel])) {
// Pick the first relationship.
$rel = key($relationship_options);
// We want this relationship option to get saved even if the user
// skips submitting the form.
$executable->setItemOption($display_id, $type, $id, 'relationship', $rel);
$save_ui_cache = TRUE;
}
$form['options']['relationship'] = array(
'#type' => 'select',
'#title' => t('Relationship'),
'#options' => $relationship_options,
'#default_value' => $rel,
'#weight' => -500,
);
}
else {
$form['options']['relationship'] = array(
'#type' => 'value',
'#value' => 'none',
);
}
$form['#title'] = t('Configure @type: @item', array('@type' => $types[$type]['lstitle'], '@item' => $handler->adminLabel()));
if (!empty($handler->definition['help'])) {
$form['options']['form_description'] = array(
'#markup' => $handler->definition['help'],
'#theme_wrappers' => array('container'),
'#attributes' => array('class' => array('form-item description')),
'#weight' => -1000,
);
}
$form['#section'] = $display_id . '-' . $type . '-' . $id;
// Get form from the handler.
$handler->buildOptionsForm($form['options'], $form_state);
$form_state['handler'] = &$handler;
}
$name = NULL;
if (isset($form_state['update_name'])) {
$name = $form_state['update_name'];
}
$view->getStandardButtons($form, $form_state, 'views_ui_config_item_form', $name);
// Add a 'remove' button.
$form['buttons']['remove'] = array(
'#type' => 'submit',
'#value' => t('Remove'),
'#submit' => array(array($this, 'remove')),
'#limit_validation_errors' => array(array('override')),
);
}
if ($save_ui_cache) {
views_ui_cache_set($view);
}
return $form;
}
/**
* Overrides \Drupal\views_ui\Form\Ajax\ViewsFormBase::validateForm().
*/
public function validateForm(array &$form, array &$form_state) {
$form_state['handler']->validateOptionsForm($form['options'], $form_state);
if (form_get_errors()) {
$form_state['rerender'] = TRUE;
}
}
/**
* Overrides \Drupal\views_ui\Form\Ajax\ViewsFormBase::submitForm().
*/
public function submitForm(array &$form, array &$form_state) {
// Run it through the handler's submit function.
$form_state['handler']->submitOptionsForm($form['options'], $form_state);
$item = $form_state['handler']->options;
$types = ViewExecutable::viewsHandlerTypes();
// For footer/header $handler_type is area but $type is footer/header.
// For all other handle types it's the same.
$handler_type = $type = $form_state['type'];
if (!empty($types[$type]['type'])) {
$handler_type = $types[$type]['type'];
}
$override = NULL;
$executable = $form_state['view']->get('executable');
if ($executable->display_handler->useGroupBy() && !empty($item['group_type'])) {
if (empty($executable->query)) {
$executable->initQuery();
}
$aggregate = $executable->query->get_aggregation_info();
if (!empty($aggregate[$item['group_type']]['handler'][$type])) {
$override = $aggregate[$item['group_type']]['handler'][$type];
}
}
// Create a new handler and unpack the options from the form onto it. We
// can use that for storage.
$handler = views_get_handler($item['table'], $item['field'], $handler_type, $override);
$handler->init($executable, $executable->display_handler, $item);
// Add the incoming options to existing options because items using
// the extra form may not have everything in the form here.
$options = $form_state['values']['options'] + $form_state['handler']->options;
// This unpacks only options that are in the definition, ensuring random
// extra stuff on the form is not sent through.
$handler->unpackOptions($handler->options, $options, NULL, FALSE);
// Store the item back on the view
$executable->setItem($form_state['display_id'], $form_state['type'], $form_state['id'], $handler->options);
// Ensure any temporary options are removed.
if (isset($form_state['view']->temporary_options[$type][$form_state['id']])) {
unset($form_state['view']->temporary_options[$type][$form_state['id']]);
}
// Write to cache
views_ui_cache_set($form_state['view']);
}
/**
* Submit handler for removing an item from a view
*/
public function remove(&$form, &$form_state) {
// Store the item back on the view
list($was_defaulted, $is_defaulted) = $form_state['view']->getOverrideValues($form, $form_state);
$executable = $form_state['view']->get('executable');
// If the display selection was changed toggle the override value.
if ($was_defaulted != $is_defaulted) {
$display = &$executable->displayHandlers->get($form_state['display_id']);
$display->optionsOverride($form, $form_state);
}
$executable->removeItem($form_state['display_id'], $form_state['type'], $form_state['id']);
// Write to cache
views_ui_cache_set($form_state['view']);
}
}

View File

@ -0,0 +1,121 @@
<?php
/**
* @file
* Contains \Drupal\views_ui\Form\Ajax\ConfigItemExtra.
*/
namespace Drupal\views_ui\Form\Ajax;
use Drupal\views\ViewStorageInterface;
use Drupal\views\ViewExecutable;
/**
* Provides a form for configuring extra information for a Views UI item.
*/
class ConfigItemExtra extends ViewsFormBase {
/**
* Constucts a new ConfigItemExtra object.
*/
public function __construct($type = NULL, $id = NULL) {
$this->setType($type);
$this->setID($id);
}
/**
* Implements \Drupal\views_ui\Form\Ajax\ViewsFormInterface::getFormKey().
*/
public function getFormKey() {
return 'config-item-extra';
}
/**
* Overrides \Drupal\views_ui\Form\Ajax\ViewsFormBase::getForm().
*/
public function getForm(ViewStorageInterface $view, $display_id, $js, $type = NULL, $id = NULL) {
$this->setType($type);
$this->setID($id);
return parent::getForm($view, $display_id, $js);
}
/**
* Implements \Drupal\Core\Form\FormInterface::getFormID().
*/
public function getFormID() {
return 'views_ui_config_item_extra_form';
}
/**
* Implements \Drupal\Core\Form\FormInterface::buildForm().
*/
public function buildForm(array $form, array &$form_state) {
$view = &$form_state['view'];
$display_id = $form_state['display_id'];
$type = $form_state['type'];
$id = $form_state['id'];
$form = array(
'options' => array(
'#tree' => true,
'#theme_wrappers' => array('container'),
'#attributes' => array('class' => array('scroll')),
),
);
$executable = $view->get('executable');
if (!$executable->setDisplay($display_id)) {
views_ajax_error(t('invalid display id @display', array('@display' => $display_id)));
}
$item = $executable->getItem($display_id, $type, $id);
if ($item) {
$handler = $executable->display_handler->getHandler($type, $id);
if (empty($handler)) {
$form['markup'] = array('#markup' => t("Error: handler for @table > @field doesn't exist!", array('@table' => $item['table'], '@field' => $item['field'])));
}
else {
$handler->init($executable, $executable->display_handler, $item);
$types = ViewExecutable::viewsHandlerTypes();
$form['#title'] = t('Configure extra settings for @type %item', array('@type' => $types[$type]['lstitle'], '%item' => $handler->adminLabel()));
$form['#section'] = $display_id . '-' . $type . '-' . $id;
// Get form from the handler.
$handler->buildExtraOptionsForm($form['options'], $form_state);
$form_state['handler'] = &$handler;
}
$view->getStandardButtons($form, $form_state, 'views_ui_config_item_extra_form');
}
return $form;
}
/**
* Overrides \Drupal\views_ui\Form\Ajax\ViewsFormBase::validateForm().
*/
public function validateForm(array &$form, array &$form_state) {
$form_state['handler']->validateExtraOptionsForm($form['options'], $form_state);
}
/**
* Overrides \Drupal\views_ui\Form\Ajax\ViewsFormBase::submitForm().
*/
public function submitForm(array &$form, array &$form_state) {
// Run it through the handler's submit function.
$form_state['handler']->submitExtraOptionsForm($form['options'], $form_state);
$item = $form_state['handler']->options;
// Store the data we're given.
foreach ($form_state['values']['options'] as $key => $value) {
$item[$key] = $value;
}
// Store the item back on the view
$form_state['view']->get('executable')->setItem($form_state['display_id'], $form_state['type'], $form_state['id'], $item);
// Write to cache
views_ui_cache_set($form_state['view']);
}
}

View File

@ -0,0 +1,115 @@
<?php
/**
* @file
* Contains \Drupal\views_ui\Form\Ajax\ConfigItemGroup.
*/
namespace Drupal\views_ui\Form\Ajax;
use Drupal\views\ViewStorageInterface;
use Drupal\views\ViewExecutable;
/**
* Provides a form for configuring grouping information for a Views UI item.
*/
class ConfigItemGroup extends ViewsFormBase {
/**
* Constucts a new ConfigItemGroup object.
*/
public function __construct($type = NULL, $id = NULL) {
$this->setType($type);
$this->setID($id);
}
/**
* Implements \Drupal\views_ui\Form\Ajax\ViewsFormInterface::getFormKey().
*/
public function getFormKey() {
return 'config-item-group';
}
/**
* Overrides \Drupal\views_ui\Form\Ajax\ViewsFormBase::getForm().
*/
public function getForm(ViewStorageInterface $view, $display_id, $js, $type = NULL, $id = NULL) {
$this->setType($type);
$this->setID($id);
return parent::getForm($view, $display_id, $js);
}
/**
* Implements \Drupal\Core\Form\FormInterface::getFormID().
*/
public function getFormID() {
return 'views_ui_config_item_group_form';
}
/**
* Implements \Drupal\Core\Form\FormInterface::buildForm().
*/
public function buildForm(array $form, array &$form_state) {
$view = &$form_state['view'];
$display_id = $form_state['display_id'];
$type = $form_state['type'];
$id = $form_state['id'];
$form = array(
'options' => array(
'#tree' => TRUE,
'#theme_wrappers' => array('container'),
'#attributes' => array('class' => array('scroll')),
),
);
$executable = $view->get('executable');
if (!$executable->setDisplay($display_id)) {
views_ajax_render(t('Invalid display id @display', array('@display' => $display_id)));
}
$executable->initQuery();
$item = $executable->getItem($display_id, $type, $id);
if ($item) {
$handler = $executable->display_handler->getHandler($type, $id);
if (empty($handler)) {
$form['markup'] = array('#markup' => t("Error: handler for @table > @field doesn't exist!", array('@table' => $item['table'], '@field' => $item['field'])));
}
else {
$handler->init($executable, $executable->display_handler, $item);
$types = ViewExecutable::viewsHandlerTypes();
$form['#title'] = t('Configure aggregation settings for @type %item', array('@type' => $types[$type]['lstitle'], '%item' => $handler->adminLabel()));
$handler->buildGroupByForm($form['options'], $form_state);
$form_state['handler'] = &$handler;
}
$view->getStandardButtons($form, $form_state, 'views_ui_config_item_group_form');
}
return $form;
}
/**
* Overrides \Drupal\views_ui\Form\Ajax\ViewsFormBase::submitForm().
*/
public function submitForm(array &$form, array &$form_state) {
$item = &$form_state['handler']->options;
$type = $form_state['type'];
$id = $form_state['id'];
$handler = views_get_handler($item['table'], $item['field'], $type);
$executable = $form_state['view']->get('executable');
$handler->init($executable, $executable->display_handler, $item);
$handler->submitGroupByForm($form, $form_state);
// Store the item back on the view
$executable->setItem($form_state['display_id'], $form_state['type'], $form_state['id'], $item);
// Write to cache
views_ui_cache_set($form_state['view']);
}
}

View File

@ -0,0 +1,119 @@
<?php
/**
* @file
* Contains \Drupal\views_ui\Form\Ajax\Display.
*/
namespace Drupal\views_ui\Form\Ajax;
use Drupal\views\ViewStorageInterface;
/**
* Provides a form for editing the Views display.
*/
class Display extends ViewsFormBase {
/**
* Constucts a new Display object.
*/
public function __construct($type = NULL) {
$this->setType($type);
}
/**
* Implements \Drupal\views_ui\Form\Ajax\ViewsFormInterface::getFormKey().
*/
public function getFormKey() {
return 'display';
}
/**
* Overrides \Drupal\views_ui\Form\Ajax\ViewsFormBase::getFormState().
*
* @todo Remove this and switch all usage of $form_state['section'] to
* $form_state['type'].
*/
public function getFormState(ViewStorageInterface $view, $display_id, $js) {
return array(
'section' => $this->type,
) + parent::getFormState($view, $display_id, $js);
}
/**
* Overrides \Drupal\views_ui\Form\Ajax\ViewsFormBase::getForm().
*/
public function getForm(ViewStorageInterface $view, $display_id, $js, $type = NULL) {
$this->setType($type);
return parent::getForm($view, $display_id, $js);
}
/**
* Implements \Drupal\Core\Form\FormInterface::getFormID().
*/
public function getFormID() {
return 'views_ui_edit_display_form';
}
/**
* Implements \Drupal\Core\Form\FormInterface::buildForm().
*/
public function buildForm(array $form, array &$form_state) {
$view = &$form_state['view'];
$display_id = $form_state['display_id'];
$section = $form_state['section'];
$executable = $view->get('executable');
if (!$executable->setDisplay($display_id)) {
views_ajax_error(t('Invalid display id @display', array('@display' => $display_id)));
}
$display = &$executable->display[$display_id];
// Get form from the handler.
$form['options'] = array(
'#theme_wrappers' => array('container'),
'#attributes' => array('class' => array('scroll')),
);
$executable->display_handler->buildOptionsForm($form['options'], $form_state);
// The handler options form sets $form['#title'], which we need on the entire
// $form instead of just the ['options'] section.
$form['#title'] = $form['options']['#title'];
unset($form['options']['#title']);
// Move the override dropdown out of the scrollable section of the form.
if (isset($form['options']['override'])) {
$form['override'] = $form['options']['override'];
unset($form['options']['override']);
}
$name = NULL;
if (isset($form_state['update_name'])) {
$name = $form_state['update_name'];
}
$view->getStandardButtons($form, $form_state, 'views_ui_edit_display_form', $name);
return $form;
}
/**
* Overrides \Drupal\views_ui\Form\Ajax\ViewsFormBase::validateForm().
*/
public function validateForm(array &$form, array &$form_state) {
$form_state['view']->get('executable')->displayHandlers->get($form_state['display_id'])->validateOptionsForm($form['options'], $form_state);
if (form_get_errors()) {
$form_state['rerender'] = TRUE;
}
}
/**
* Overrides \Drupal\views_ui\Form\Ajax\ViewsFormBase::submitForm().
*/
public function submitForm(array &$form, array &$form_state) {
$form_state['view']->get('executable')->displayHandlers->get($form_state['display_id'])->submitOptionsForm($form['options'], $form_state);
views_ui_cache_set($form_state['view']);
}
}

View File

@ -0,0 +1,88 @@
<?php
/**
* @file
* Contains \Drupal\views_ui\Form\Ajax\EditDetails.
*/
namespace Drupal\views_ui\Form\Ajax;
use Drupal\views_ui\ViewUI;
/**
* Provides a form for editing the details of a View.
*/
class EditDetails extends ViewsFormBase {
/**
* Implements \Drupal\views_ui\Form\Ajax\ViewsFormInterface::getFormKey().
*/
public function getFormKey() {
return 'edit-details';
}
/**
* Implements \Drupal\Core\Form\FormInterface::getFormID().
*/
public function getFormID() {
return 'views_ui_edit_details_form';
}
/**
* Implements \Drupal\Core\Form\FormInterface::buildForm().
*/
public function buildForm(array $form, array &$form_state) {
$view = &$form_state['view'];
$form['#title'] = t('View name and description');
$form['#section'] = 'details';
$form['details'] = array(
'#theme_wrappers' => array('container'),
'#attributes' => array('class' => array('scroll')),
);
$form['details']['human_name'] = array(
'#type' => 'textfield',
'#title' => t('Human-readable name'),
'#description' => t('A descriptive human-readable name for this view. Spaces are allowed'),
'#default_value' => $view->getHumanName(),
);
$form['details']['tag'] = array(
'#type' => 'textfield',
'#title' => t('View tag'),
'#description' => t('Optionally, enter a comma delimited list of tags for this view to use in filtering and sorting views on the administrative page.'),
'#default_value' => $view->get('tag'),
'#autocomplete_path' => 'admin/views/ajax/autocomplete/tag',
);
$form['details']['description'] = array(
'#type' => 'textfield',
'#title' => t('View description'),
'#description' => t('This description will appear on the Views administrative UI to tell you what the view is about.'),
'#default_value' => $view->get('description'),
);
$view->getStandardButtons($form, $form_state, 'views_ui_edit_details_form');
return $form;
}
/**
* Overrides \Drupal\views_ui\Form\Ajax\ViewsFormBase::submitForm().
*/
public function submitForm(array &$form, array &$form_state) {
$view = $form_state['view'];
foreach ($form_state['values'] as $key => $value) {
// Only save values onto the view if they're actual view properties
// (as opposed to 'op' or 'form_build_id').
if (isset($form['details'][$key])) {
$view->set($key, $value);
}
}
$bases = drupal_container()->get('views.views_data')->fetchBaseTables();
$form_state['#page_title'] = $view->getHumanName();
if (isset($bases[$view->get('base_table')])) {
$form_state['#page_title'] .= ' (' . $bases[$view->get('base_table')]['title'] . ')';
}
views_ui_cache_set($view);
}
}

View File

@ -0,0 +1,167 @@
<?php
/**
* @file
* Contains \Drupal\views_ui\Form\Ajax\Rearrange.
*/
namespace Drupal\views_ui\Form\Ajax;
use Drupal\views\ViewStorageInterface;
use Drupal\views\ViewExecutable;
/**
* Provides a rearrange form for Views handlers.
*/
class Rearrange extends ViewsFormBase {
/**
* Constucts a new Rearrange object.
*/
public function __construct($type = NULL) {
$this->setType($type);
}
/**
* Implements \Drupal\views_ui\Form\Ajax\ViewsFormInterface::getFormKey().
*/
public function getFormKey() {
return 'rearrange';
}
/**
* Overrides \Drupal\views_ui\Form\Ajax\ViewsFormBase::getForm().
*/
public function getForm(ViewStorageInterface $view, $display_id, $js, $type = NULL) {
$this->setType($type);
return parent::getForm($view, $display_id, $js);
}
/**
* Implements \Drupal\Core\Form\FormInterface::getFormID().
*/
public function getFormID() {
return 'views_ui_rearrange_form';
}
/**
* Implements \Drupal\Core\Form\FormInterface::buildForm().
*/
public function buildForm(array $form, array &$form_state) {
$view = &$form_state['view'];
$display_id = $form_state['display_id'];
$type = $form_state['type'];
$types = ViewExecutable::viewsHandlerTypes();
$executable = $view->get('executable');
if (!$executable->setDisplay($display_id)) {
views_ajax_error(t('Invalid display id @display', array('@display' => $display_id)));
}
$display = &$executable->displayHandlers->get($display_id);
$form['#title'] = t('Rearrange @type', array('@type' => $types[$type]['ltitle']));
$form['#section'] = $display_id . 'rearrange-item';
if ($display->defaultableSections($types[$type]['plural'])) {
$form_state['section'] = $types[$type]['plural'];
views_ui_standard_display_dropdown($form, $form_state, $form_state['section']);
}
$count = 0;
// Get relationship labels
$relationships = array();
foreach ($display->getHandlers('relationship') as $id => $handler) {
$relationships[$id] = $handler->label();
}
// Filters can now be grouped so we do a little bit extra:
$groups = array();
$grouping = FALSE;
if ($type == 'filter') {
$group_info = $executable->display_handler->getOption('filter_groups');
if (!empty($group_info['groups']) && count($group_info['groups']) > 1) {
$grouping = TRUE;
$groups = array(0 => array());
}
}
foreach ($display->getOption($types[$type]['plural']) as $id => $field) {
$form['fields'][$id] = array('#tree' => TRUE);
$form['fields'][$id]['weight'] = array(
'#type' => 'textfield',
'#default_value' => ++$count,
);
$handler = $display->getHandler($type, $id);
if ($handler) {
$name = $handler->adminLabel() . ' ' . $handler->adminSummary();
if (!empty($field['relationship']) && !empty($relationships[$field['relationship']])) {
$name = '(' . $relationships[$field['relationship']] . ') ' . $name;
}
$form['fields'][$id]['name'] = array(
'#markup' => $name,
);
}
else {
$form['fields'][$id]['name'] = array('#markup' => t('Broken field @id', array('@id' => $id)));
}
$form['fields'][$id]['removed'] = array(
'#type' => 'checkbox',
'#id' => 'views-removed-' . $id,
'#attributes' => array('class' => array('views-remove-checkbox')),
'#default_value' => 0,
);
}
// Add javascript settings that will be added via $.extend for tabledragging
$form['#js']['tableDrag']['arrange']['weight'][0] = array(
'target' => 'weight',
'source' => NULL,
'relationship' => 'sibling',
'action' => 'order',
'hidden' => TRUE,
'limit' => 0,
);
$name = NULL;
if (isset($form_state['update_name'])) {
$name = $form_state['update_name'];
}
$view->getStandardButtons($form, $form_state, 'views_ui_rearrange_form');
return $form;
}
/**
* Overrides \Drupal\views_ui\Form\Ajax\ViewsFormBase::submitForm().
*/
public function submitForm(array &$form, array &$form_state) {
$types = ViewExecutable::viewsHandlerTypes();
$display = &$form_state['view']->get('executable')->displayHandlers->get($form_state['display_id']);
$old_fields = $display->getOption($types[$form_state['type']]['plural']);
$new_fields = $order = array();
// Make an array with the weights
foreach ($form_state['values'] as $field => $info) {
// add each value that is a field with a weight to our list, but only if
// it has had its 'removed' checkbox checked.
if (is_array($info) && isset($info['weight']) && empty($info['removed'])) {
$order[$field] = $info['weight'];
}
}
// Sort the array
asort($order);
// Create a new list of fields in the new order.
foreach (array_keys($order) as $field) {
$new_fields[$field] = $old_fields[$field];
}
$display->setOption($types[$form_state['type']]['plural'], $new_fields);
// Store in cache
views_ui_cache_set($form_state['view']);
}
}

View File

@ -0,0 +1,318 @@
<?php
/**
* @file
* Contains \Drupal\views_ui\Form\Ajax\RearrangeFilter.
*/
namespace Drupal\views_ui\Form\Ajax;
use Drupal\views_ui\ViewUI;
use Drupal\views\ViewExecutable;
/**
* Provides a rearrange form for Views filters.
*/
class RearrangeFilter extends ViewsFormBase {
/**
* Implements \Drupal\views_ui\Form\Ajax\ViewsFormInterface::getFormKey().
*/
public function getFormKey() {
return 'rearrange-filter';
}
/**
* Implements \Drupal\Core\Form\FormInterface::getFormID().
*/
public function getFormID() {
return 'views_ui_rearrange_filter_form';
}
/**
* Implements \Drupal\Core\Form\FormInterface::buildForm().
*/
public function buildForm(array $form, array &$form_state) {
$view = &$form_state['view'];
$display_id = $form_state['display_id'];
$type = 'filter';
$types = ViewExecutable::viewsHandlerTypes();
$executable = $view->get('executable');
if (!$executable->setDisplay($display_id)) {
views_ajax_render(t('Invalid display id @display', array('@display' => $display_id)));
}
$display = $executable->displayHandlers->get($display_id);
$form['#title'] = check_plain($display->display['display_title']) . ': ';
$form['#title'] .= t('Rearrange @type', array('@type' => $types[$type]['ltitle']));
$form['#section'] = $display_id . 'rearrange-item';
if ($display->defaultableSections($types[$type]['plural'])) {
$form_state['section'] = $types[$type]['plural'];
views_ui_standard_display_dropdown($form, $form_state, $form_state['section']);
}
if (!empty($view->form_cache)) {
$groups = $view->form_cache['groups'];
$handlers = $view->form_cache['handlers'];
}
else {
$groups = $display->getOption('filter_groups');
$handlers = $display->getOption($types[$type]['plural']);
}
$count = 0;
// Get relationship labels
$relationships = array();
foreach ($display->getHandlers('relationship') as $id => $handler) {
$relationships[$id] = $handler->label();
}
$group_options = array();
/**
* Filter groups is an array that contains:
* array(
* 'operator' => 'and' || 'or',
* 'groups' => array(
* $group_id => 'and' || 'or',
* ),
* );
*/
$grouping = count(array_keys($groups['groups'])) > 1;
$form['filter_groups']['#tree'] = TRUE;
$form['filter_groups']['operator'] = array(
'#type' => 'select',
'#options' => array(
'AND' => t('And'),
'OR' => t('Or'),
),
'#default_value' => $groups['operator'],
'#attributes' => array(
'class' => array('warning-on-change'),
),
'#title' => t('Operator to use on all groups'),
'#description' => t('Either "group 0 AND group 1 AND group 2" or "group 0 OR group 1 OR group 2", etc'),
'#access' => $grouping,
);
$form['remove_groups']['#tree'] = TRUE;
foreach ($groups['groups'] as $id => $group) {
$form['filter_groups']['groups'][$id] = array(
'#title' => t('Operator'),
'#type' => 'select',
'#options' => array(
'AND' => t('And'),
'OR' => t('Or'),
),
'#default_value' => $group,
'#attributes' => array(
'class' => array('warning-on-change'),
),
);
$form['remove_groups'][$id] = array(); // to prevent a notice
if ($id != 1) {
$form['remove_groups'][$id] = array(
'#type' => 'submit',
'#value' => t('Remove group @group', array('@group' => $id)),
'#id' => "views-remove-group-$id",
'#attributes' => array(
'class' => array('views-remove-group'),
),
'#group' => $id,
);
}
$group_options[$id] = $id == 1 ? t('Default group') : t('Group @group', array('@group' => $id));
$form['#group_renders'][$id] = array();
}
$form['#group_options'] = $group_options;
$form['#groups'] = $groups;
// We don't use getHandlers() because we want items without handlers to
// appear and show up as 'broken' so that the user can see them.
$form['filters'] = array('#tree' => TRUE);
foreach ($handlers as $id => $field) {
// If the group does not exist, move the filters to the default group.
if (empty($field['group']) || empty($groups['groups'][$field['group']])) {
$field['group'] = 1;
}
$handler = $display->getHandler($type, $id);
if ($grouping && $handler && !$handler->can_group()) {
$field['group'] = 'ungroupable';
}
// If not grouping and the handler is set ungroupable, move it back to
// the default group to prevent weird errors from having it be in its
// own group:
if (!$grouping && $field['group'] == 'ungroupable') {
$field['group'] = 1;
}
// Place this item into the proper group for rendering.
$form['#group_renders'][$field['group']][] = $id;
$form['filters'][$id]['weight'] = array(
'#type' => 'textfield',
'#default_value' => ++$count,
'#size' => 8,
);
$form['filters'][$id]['group'] = array(
'#type' => 'select',
'#options' => $group_options,
'#default_value' => $field['group'],
'#attributes' => array(
'class' => array('views-region-select', 'views-region-' . $id),
),
'#access' => $field['group'] !== 'ungroupable',
);
if ($handler) {
$name = $handler->adminLabel() . ' ' . $handler->adminSummary();
if (!empty($field['relationship']) && !empty($relationships[$field['relationship']])) {
$name = '(' . $relationships[$field['relationship']] . ') ' . $name;
}
$form['filters'][$id]['name'] = array(
'#markup' => $name,
);
}
else {
$form['filters'][$id]['name'] = array('#markup' => t('Broken field @id', array('@id' => $id)));
}
$form['filters'][$id]['removed'] = array(
'#type' => 'checkbox',
'#id' => 'views-removed-' . $id,
'#attributes' => array('class' => array('views-remove-checkbox')),
'#default_value' => 0,
);
}
if (isset($form_state['update_name'])) {
$name = $form_state['update_name'];
}
$view->getStandardButtons($form, $form_state, 'views_ui_rearrange_filter_form');
$form['buttons']['add_group'] = array(
'#type' => 'submit',
'#value' => t('Create new filter group'),
'#id' => 'views-add-group',
'#group' => 'add',
);
return $form;
}
/**
* Overrides \Drupal\views_ui\Form\Ajax\ViewsFormBase::submitForm().
*/
public function submitForm(array &$form, array &$form_state) {
$types = ViewExecutable::viewsHandlerTypes();
$display = &$form_state['view']->get('executable')->displayHandlers->get($form_state['display_id']);
$remember_groups = array();
if (!empty($form_state['view']->form_cache)) {
$old_fields = $form_state['view']->form_cache['handlers'];
}
else {
$old_fields = $display->getOption($types['filter']['plural']);
}
$count = 0;
$groups = $form_state['values']['filter_groups'];
// Whatever button was clicked, re-calculate field information.
$new_fields = $order = array();
// Make an array with the weights
foreach ($form_state['values']['filters'] as $field => $info) {
// add each value that is a field with a weight to our list, but only if
// it has had its 'removed' checkbox checked.
if (is_array($info) && empty($info['removed'])) {
if (isset($info['weight'])) {
$order[$field] = $info['weight'];
}
if (isset($info['group'])) {
$old_fields[$field]['group'] = $info['group'];
$remember_groups[$info['group']][] = $field;
}
}
}
// Sort the array
asort($order);
// Create a new list of fields in the new order.
foreach (array_keys($order) as $field) {
$new_fields[$field] = $old_fields[$field];
}
// If the #group property is set on the clicked button, that means we are
// either adding or removing a group, not actually updating the filters.
if (!empty($form_state['clicked_button']['#group'])) {
if ($form_state['clicked_button']['#group'] == 'add') {
// Add a new group
$groups['groups'][] = 'AND';
}
else {
// Renumber groups above the removed one down.
foreach (array_keys($groups['groups']) as $group_id) {
if ($group_id >= $form_state['clicked_button']['#group']) {
$old_group = $group_id + 1;
if (isset($groups['groups'][$old_group])) {
$groups['groups'][$group_id] = $groups['groups'][$old_group];
if (isset($remember_groups[$old_group])) {
foreach ($remember_groups[$old_group] as $id) {
$new_fields[$id]['group'] = $group_id;
}
}
}
else {
// If this is the last one, just unset it.
unset($groups['groups'][$group_id]);
}
}
}
}
// Update our cache with values so that cancel still works the way
// people expect.
$form_state['view']->form_cache = array(
'key' => 'rearrange-filter',
'groups' => $groups,
'handlers' => $new_fields,
);
// Return to this form except on actual Update.
$form_state['view']->addFormToStack('rearrange-filter', $form_state['display_id'], 'filter');
}
else {
// The actual update button was clicked. Remove the empty groups, and
// renumber them sequentially.
ksort($remember_groups);
$groups['groups'] = views_array_key_plus(array_values(array_intersect_key($groups['groups'], $remember_groups)));
// Change the 'group' key on each field to match. Here, $mapping is an
// array whose keys are the old group numbers and whose values are the new
// (sequentially numbered) ones.
$mapping = array_flip(views_array_key_plus(array_keys($remember_groups)));
foreach ($new_fields as &$new_field) {
$new_field['group'] = $mapping[$new_field['group']];
}
// Write the changed handler values.
$display->setOption($types['filter']['plural'], $new_fields);
$display->setOption('filter_groups', $groups);
if (isset($form_state['view']->form_cache)) {
unset($form_state['view']->form_cache);
}
}
// Store in cache.
views_ui_cache_set($form_state['view']);
}
}

View File

@ -0,0 +1,140 @@
<?php
/**
* @file
* Contains \Drupal\views_ui\Form\Ajax\ReorderDisplays.
*/
namespace Drupal\views_ui\Form\Ajax;
use Drupal\views_ui\ViewUI;
/**
* Displays the display reorder form.
*/
class ReorderDisplays extends ViewsFormBase {
/**
* Implements \Drupal\views_ui\Form\Ajax\ViewsFormInterface::getFormKey().
*/
public function getFormKey() {
return 'reorder-displays';
}
/**
* Implements \Drupal\Core\Form\FormInterface::getFormID().
*/
public function getFormID() {
return 'views_ui_reorder_displays_form';
}
/**
* Implements \Drupal\Core\Form\FormInterface::buildForm().
*/
public function buildForm(array $form, array &$form_state) {
$view = $form_state['view'];
$display_id = $form_state['display_id'];
$form['view'] = array('#type' => 'value', '#value' => $view);
$form['#tree'] = TRUE;
$count = count($view->get('display'));
$displays = $view->get('display');
foreach ($displays as $display) {
$form[$display['id']] = array(
'title' => array('#markup' => $display['display_title']),
'weight' => array(
'#type' => 'weight',
'#value' => $display['position'],
'#delta' => $count,
'#title' => t('Weight for @display', array('@display' => $display['display_title'])),
'#title_display' => 'invisible',
),
'#tree' => TRUE,
'#display' => $display,
'removed' => array(
'#type' => 'checkbox',
'#id' => 'display-removed-' . $display['id'],
'#attributes' => array('class' => array('views-remove-checkbox')),
'#default_value' => isset($display['deleted']),
),
);
if (isset($display['deleted']) && $display['deleted']) {
$form[$display['id']]['deleted'] = array('#type' => 'value', '#value' => TRUE);
}
if ($display['id'] === 'default') {
unset($form[$display['id']]['weight']);
unset($form[$display['id']]['removed']);
}
}
$form['#title'] = t('Displays Reorder');
$form['#section'] = 'reorder';
// Add javascript settings that will be added via $.extend for tabledragging
$form['#js']['tableDrag']['reorder-displays']['weight'][0] = array(
'target' => 'weight',
'source' => NULL,
'relationship' => 'sibling',
'action' => 'order',
'hidden' => TRUE,
'limit' => 0,
);
$form['#action'] = url('admin/structure/views/nojs/reorder-displays/' . $view->id() . '/' . $display_id);
$view->getStandardButtons($form, $form_state, 'views_ui_reorder_displays_form');
return $form;
}
/**
* Overrides \Drupal\views_ui\Form\Ajax\ViewsFormBase::submitForm().
*/
public function submitForm(array &$form, array &$form_state) {
$view = $form_state['view'];
foreach ($form_state['input'] as $display => $info) {
// add each value that is a field with a weight to our list, but only if
// it has had its 'removed' checkbox checked.
if (is_array($info) && isset($info['weight']) && empty($info['removed'])) {
$order[$display] = $info['weight'];
}
}
// Sort the order array
asort($order);
// Fixing up positions
$position = 1;
foreach (array_keys($order) as $display) {
$order[$display] = $position++;
}
// Setting up position and removing deleted displays
$displays = $view->get('display');
foreach ($displays as $display_id => $display) {
// Don't touch the default !!!
if ($display_id === 'default') {
$displays[$display_id]['position'] = 0;
continue;
}
if (isset($order[$display_id])) {
$displays[$display_id]['position'] = $order[$display_id];
}
else {
$displays[$display_id]['deleted'] = TRUE;
}
}
$view->set('display', $displays);
// Store in cache
views_ui_cache_set($view);
$form_state['redirect'] = array('admin/structure/views/view/' . $view->id() . '/edit', array('fragment' => 'views-tab-default'));
}
}

View File

@ -0,0 +1,188 @@
<?php
/**
* @file
* Contains \Drupal\views_ui\Form\Ajax\ViewsFormBase.
*/
namespace Drupal\views_ui\Form\Ajax;
use Drupal\views_ui\ViewUI;
use Drupal\views\ViewStorageInterface;
use Drupal\views\Ajax;
use Drupal\Core\Ajax\AjaxResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
/**
* Provides a base class for Views UI AJAX forms.
*/
abstract class ViewsFormBase implements ViewsFormInterface {
/**
* The ID of the item this form is manipulating.
*
* @var string
*/
protected $id;
/**
* The type of item this form is manipulating.
*
* @var string
*/
protected $type;
/**
* Sets the ID for this form.
*
* @param string $id
* The ID of the item this form is manipulating.
*/
protected function setID($id) {
if ($id) {
$this->id = $id;
}
}
/**
* Sets the type for this form.
*
* @param string $type
* The type of the item this form is manipulating.
*/
protected function setType($type) {
if ($type) {
$this->type = $type;
}
}
/**
* Implements \Drupal\views_ui\Form\Ajax\ViewsFormInterface::getFormState().
*/
public function getFormState(ViewStorageInterface $view, $display_id, $js) {
// $js may already have been converted to a Boolean.
$ajax = is_string($js) ? $js === 'ajax' : $js;
return array(
'form_id' => $this->getFormID(),
'form_key' => $this->getFormKey(),
'ajax' => $ajax,
'display_id' => $display_id,
'view' => $view,
'type' => $this->type,
'id' => $this->id,
'no_redirect' => TRUE,
'build_info' => array(
'args' => array(),
'callback_object' => $this,
),
);
}
/**
* Implements \Drupal\views_ui\Form\Ajax\ViewsFormInterface::getForm().
*/
public function getForm(ViewStorageInterface $view, $display_id, $js) {
$form_state = $this->getFormState($view, $display_id, $js);
$view = $form_state['view'];
$key = $form_state['form_key'];
// @todo Remove the need for this.
module_load_include('inc', 'views_ui', 'admin');
module_load_include('inc', 'views', 'includes/ajax');
// Reset the cache of IDs. Drupal rather aggressively prevents ID
// duplication but this causes it to remember IDs that are no longer even
// being used.
$seen_ids_init = &drupal_static('drupal_html_id:init');
$seen_ids_init = array();
// check to see if this is the top form of the stack. If it is, pop
// it off; if it isn't, the user clicked somewhere else and the stack is
// now irrelevant.
if (!empty($view->stack)) {
$identifier = implode('-', array_filter(array($key, $view->id(), $display_id, $form_state['type'], $form_state['id'])));
// Retrieve the first form from the stack without changing the integer keys,
// as they're being used for the "2 of 3" progress indicator.
reset($view->stack);
list($key, $top) = each($view->stack);
unset($view->stack[$key]);
if (array_shift($top) != $identifier) {
$view->stack = array();
}
}
// Automatically remove the form cache if it is set and the key does
// not match. This way navigating away from the form without hitting
// update will work.
if (isset($view->form_cache) && $view->form_cache['key'] != $key) {
unset($view->form_cache);
}
// With the below logic, we may end up rendering a form twice (or two forms
// each sharing the same element ids), potentially resulting in
// drupal_add_js() being called twice to add the same setting. drupal_get_js()
// is ok with that, but until ajax_render() is (http://drupal.org/node/208611),
// reset the drupal_add_js() static before rendering the second time.
$drupal_add_js_original = drupal_add_js();
$drupal_add_js = &drupal_static('drupal_add_js');
$response = views_ajax_form_wrapper($form_state['form_id'], $form_state);
// If the form has not been submitted, or was not set for rerendering, stop.
if (!$form_state['submitted'] || !empty($form_state['rerender'])) {
return $response;
}
// Sometimes we need to re-generate the form for multi-step type operations.
if (!empty($view->stack)) {
$drupal_add_js = $drupal_add_js_original;
$stack = $view->stack;
$top = array_shift($stack);
// Build the new form state for the next form in the stack.
$reflection = new \ReflectionClass($view::$forms[$top[1]]);
$form_state = $reflection->newInstanceArgs(array_slice($top, 3, 2))->getFormState($view, $top[2], $form_state['ajax']);
$form_state['input'] = array();
$form_url = views_ui_build_form_url($form_state);
if (!$form_state['ajax']) {
return new RedirectResponse(url($form_url, array('absolute' => TRUE)));
}
$form_state['url'] = url($form_url);
$response = views_ajax_form_wrapper($form_state['form_id'], $form_state);
}
elseif (!$form_state['ajax']) {
// if nothing on the stack, non-js forms just go back to the main view editor.
return new RedirectResponse(url("admin/structure/views/view/{$view->id()}/edit/$form_state[display_id]", array('absolute' => TRUE)));
}
else {
$response = new AjaxResponse();
$response->addCommand(new Ajax\DismissFormCommand());
$response->addCommand(new Ajax\ShowButtonsCommand());
$response->addCommand(new Ajax\TriggerPreviewCommand());
if (!empty($form_state['#page_title'])) {
$response->addCommand(new Ajax\ReplaceTitleCommand($form_state['#page_title']));
}
}
// If this form was for view-wide changes, there's no need to regenerate
// the display section of the form.
if ($display_id !== '') {
drupal_container()->get('plugin.manager.entity')->getFormController('view', 'edit')->rebuildCurrentTab($view, $response, $display_id);
}
return $response;
}
/**
* Implements \Drupal\Core\Form\FormInterface::validateForm().
*/
public function validateForm(array &$form, array &$form_state) {
}
/**
* Implements \Drupal\Core\Form\FormInterface::submitForm().
*/
public function submitForm(array &$form, array &$form_state) {
}
}

View File

@ -0,0 +1,60 @@
<?php
/**
* @file
* Contains \Drupal\views_ui\Form\Ajax\ViewsFormInterface.
*/
namespace Drupal\views_ui\Form\Ajax;
use Drupal\Core\Form\FormInterface;
use Drupal\views\ViewStorageInterface;
interface ViewsFormInterface extends FormInterface {
/**
* Returns the key that represents this form.
*
* @return string
* The form key used in the URL, e.g., the string 'add-item' in
* 'admin/structure/views/%/add-item/%/%/%'.
*/
public function getFormKey();
/**
* Gets the form state for this form.
*
* @param \Drupal\views\ViewStorageInterface $view
* The view being edited.
* @param string|null $display_id
* The display ID being edited, or NULL to load the first available display.
* @param string $js
* If this is an AJAX form, it will be the string 'ajax'. Otherwise, it will
* be 'nojs'. This determines the response.
*
* @return array
* An associative array containing the current state of the form.
*/
public function getFormState(ViewStorageInterface $view, $display_id, $js);
/**
* Creates a new instance of this form.
*
* @param \Drupal\views\ViewStorageInterface $view
* The view being edited.
* @param string|null $display_id
* The display ID being edited, or NULL to load the first available display.
* @param string $js
* If this is an AJAX form, it will be the string 'ajax'. Otherwise, it will
* be 'nojs'. This determines the response.
*
* @return array
* An form for a specific operation in the Views UI, or an array of AJAX
* commands to render a form.
*
* @todo When http://drupal.org/node/1843224 is in, this will return
* \Drupal\Core\Ajax\AjaxResponse instead of the array of AJAX commands.
*/
public function getForm(ViewStorageInterface $view, $display_id, $js);
}

View File

@ -17,12 +17,9 @@ use Drupal\Core\Entity\EntityManager;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\ReplaceCommand;
use Drupal\views\Ajax;
/**
* Returns responses for Views UI routes.
*/
@ -281,127 +278,4 @@ class ViewsUIController {
return entity_get_form($view, 'preview', array('display_id' => $display_id));
}
/**
* Provides a generic entry point to handle AJAX forms.
*
* @param string $js
* If this is an AJAX form, it will be the string 'ajax'. Otherwise, it will
* be 'nojs'. This determines the response.
* @param string $key
* A string representing a section of the Views UI. Available keys are in
* \Drupal\views_ui\ViewUI::$forms.
* @param \Drupal\views_ui\ViewUI $view
* The view being edited.
* @param string|null $display_id
* The display ID being edited, or NULL to load the first available display.
* @param string|null $type
* If $key is 'add-item' or 'config-item', this is the type of handler being
* edited. Otherwise, it is the subsection of the Views UI. For example, the
* 'display' section has 'access' as a subsection, or the 'config-item' has
* 'style' as a handler type. NULL if the section has no subsections.
* @param string|null $id
* If $key is 'config-item', then this will be the plugin ID of the handler.
* Otherwise it will be NULL.
*
* @return array
* An form for a specific operation in the Views UI, or an array of AJAX
* commands to render a form.
*
* @todo When http://drupal.org/node/1843224 is in, this will return
* \Drupal\Core\Ajax\AjaxResponse instead of the array of AJAX commands.
*
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
*/
public function ajaxForm($js, $key, ViewUI $view, $display_id, $type, $id) {
// Determine if this is an AJAX submission.
$js = $js == 'ajax';
// @todo Remove the need for this.
module_load_include('inc', 'views_ui', 'admin');
module_load_include('inc', 'views', 'includes/ajax');
$args = array($type, $id);
// Reset the cache of IDs. Drupal rather aggressively prevents ID
// duplication but this causes it to remember IDs that are no longer even
// being used.
$seen_ids_init = &drupal_static('drupal_html_id:init');
$seen_ids_init = array();
if (empty($view::$forms[$key])) {
throw new NotFoundHttpException();
}
$form_state = $view->buildFormState($js, $key, $display_id, $args);
// check to see if this is the top form of the stack. If it is, pop
// it off; if it isn't, the user clicked somewhere else and the stack is
// now irrelevant.
if (!empty($view->stack)) {
$identifier = $view->buildIdentifier($key, $display_id, $args);
// Retrieve the first form from the stack without changing the integer keys,
// as they're being used for the "2 of 3" progress indicator.
reset($view->stack);
list($key, $top) = each($view->stack);
unset($view->stack[$key]);
if (array_shift($top) != $identifier) {
$view->stack = array();
}
}
// Automatically remove the form cache if it is set and the key does
// not match. This way navigating away from the form without hitting
// update will work.
if (isset($view->form_cache) && $view->form_cache['key'] != $key) {
unset($view->form_cache);
}
// With the below logic, we may end up rendering a form twice (or two forms
// each sharing the same element ids), potentially resulting in
// drupal_add_js() being called twice to add the same setting. drupal_get_js()
// is ok with that, but until ajax_render() is (http://drupal.org/node/208611),
// reset the drupal_add_js() static before rendering the second time.
$drupal_add_js_original = drupal_add_js();
$drupal_add_js = &drupal_static('drupal_add_js');
$response = views_ajax_form_wrapper($form_state['form_id'], $form_state);
if ($form_state['submitted'] && empty($form_state['rerender'])) {
// Sometimes we need to re-generate the form for multi-step type operations.
$object = NULL;
if (!empty($view->stack)) {
$drupal_add_js = $drupal_add_js_original;
$stack = $view->stack;
$top = array_shift($stack);
$top[0] = $js;
$form_state = call_user_func_array(array($view, 'buildFormState'), $top);
$form_state['input'] = array();
$form_url = views_ui_build_form_url($form_state);
if (!$js) {
return new RedirectResponse(url($form_url, array('absolute' => TRUE)));
}
$form_state['url'] = url($form_url);
$response = views_ajax_form_wrapper($form_state['form_id'], $form_state);
}
elseif (!$js) {
// if nothing on the stack, non-js forms just go back to the main view editor.
return new RedirectResponse(url("admin/structure/views/view/{$view->id()}/edit", array('absolute' => TRUE)));
}
else {
$response = new AjaxResponse();
$response->addCommand(new Ajax\DismissFormCommand());
$response->addCommand(new Ajax\ShowButtonsCommand());
$response->addCommand(new Ajax\TriggerPreviewCommand());
if (!empty($form_state['#page_title'])) {
$response->addCommand(new Ajax\ReplaceTitleCommand($form_state['#page_title']));
}
}
// If this form was for view-wide changes, there's no need to regenerate
// the display section of the form.
if ($display_id !== '') {
$this->entityManager->getFormController('view', 'edit')->rebuildCurrentTab($view, $response, $display_id);
}
}
return $response;
}
}

View File

@ -884,7 +884,7 @@ class ViewEditFormController extends ViewFormControllerBase {
case 'filter':
// The rearrange form for filters contains the and/or UI, so override
// the used path.
$rearrange_url = "admin/structure/views/nojs/rearrange-$type/{$view->id()}/{$display['id']}/$type";
$rearrange_url = "admin/structure/views/nojs/rearrange-filter/{$view->id()}/{$display['id']}";
$rearrange_text = t('And/Or, Rearrange');
// TODO: Add another class to have another symbol for filter rearrange.
$class = 'icon compact rearrange';

View File

@ -106,8 +106,6 @@ abstract class ViewFormControllerBase extends EntityFormController {
// Create a tab for each display.
$displays = $view->get('display');
uasort($displays, array($view, 'sortPosition'));
$view->set('display', $displays);
foreach ($displays as $id => $display) {
$tabs[$id] = array(
'#theme' => 'menu_local_task',

View File

@ -121,52 +121,22 @@ class ViewUI implements ViewStorageInterface {
*/
protected $additionalQueries;
/**
* Contains an array of form keys and their respective classes.
*
* @var array
*/
public static $forms = array(
'display' => array(
'form_id' => 'views_ui_edit_display_form',
'args' => array('section'),
),
'remove-display' => array(
'form_id' => 'views_ui_remove_display_form',
'args' => array(),
),
'rearrange' => array(
'form_id' => 'views_ui_rearrange_form',
'args' => array('type'),
),
'rearrange-filter' => array(
'form_id' => 'views_ui_rearrange_filter_form',
'args' => array('type'),
),
'reorder-displays' => array(
'form_id' => 'views_ui_reorder_displays_form',
'args' => array(),
'callback' => 'buildDisplaysReorderForm',
),
'add-item' => array(
'form_id' => 'views_ui_add_item_form',
'args' => array('type'),
),
'config-item' => array(
'form_id' => 'views_ui_config_item_form',
'args' => array('type', 'id'),
),
'config-item-extra' => array(
'form_id' => 'views_ui_config_item_extra_form',
'args' => array('type', 'id'),
),
'config-item-group' => array(
'form_id' => 'views_ui_config_item_group_form',
'args' => array('type', 'id'),
),
'edit-details' => array(
'form_id' => 'views_ui_edit_details_form',
'args' => array(),
),
'analyze' => array(
'form_id' => 'views_ui_analyze_view_form',
'args' => array(),
),
'add-item' => '\Drupal\views_ui\Form\Ajax\AddItem',
'analyze' => '\Drupal\views_ui\Form\Ajax\Analyze',
'config-item' => '\Drupal\views_ui\Form\Ajax\ConfigItem',
'config-item-extra' => '\Drupal\views_ui\Form\Ajax\ConfigItemExtra',
'config-item-group' => '\Drupal\views_ui\Form\Ajax\ConfigItemGroup',
'display' => '\Drupal\views_ui\Form\Ajax\Display',
'edit-details' => '\Drupal\views_ui\Form\Ajax\EditDetails',
'rearrange' => '\Drupal\views_ui\Form\Ajax\Rearrange',
'rearrange-filter' => '\Drupal\views_ui\Form\Ajax\RearrangeFilter',
'reorder-displays' => '\Drupal\views_ui\Form\Ajax\ReorderDisplays',
);
/**
@ -263,8 +233,11 @@ class ViewUI implements ViewStorageInterface {
}
$submit_handler = $form['#form_id'] . '_submit';
if (function_exists($submit_handler)) {
$submit_handler($form, $form_state);
if (isset($form_state['build_info']['callback_object'])) {
$submit_handler = array($form_state['build_info']['callback_object'], 'submitForm');
}
if (is_callable($submit_handler)) {
call_user_func($submit_handler, $form, $form_state);
}
}
@ -288,7 +261,7 @@ class ViewUI implements ViewStorageInterface {
* TODO: Is the hidden op operator still here somewhere, or is that part of the
* docblock outdated?
*/
public function getStandardButtons(&$form, &$form_state, $form_id, $name = NULL, $third = NULL, $submit = NULL) {
public function getStandardButtons(&$form, &$form_state, $form_id, $name = NULL) {
$form['buttons'] = array(
'#prefix' => '<div class="clearfix"><div class="form-buttons">',
'#suffix' => '</div></div>',
@ -320,7 +293,7 @@ class ViewUI implements ViewStorageInterface {
// same between the form build of the initial page request, and the
// initial form build of the request processing the form submission.
// Ideally, the button's #value shouldn't change until the form rebuild
// step. However, \Drupal\views_ui\Routing\ViewsUIController::ajaxForm()
// step. However, \Drupal\views_ui\Form\Ajax\ViewsFormBase::getForm()
// implements a different multistep form workflow than the Form API does,
// and adjusts $view->stack prior to form processing, so we compensate by
// extending button click detection code to support any of the possible
@ -330,6 +303,9 @@ class ViewUI implements ViewStorageInterface {
$form['buttons']['submit']['#process'] = array_merge(array('views_ui_form_button_was_clicked'), element_info_property($form['buttons']['submit']['#type'], '#process', array()));
}
// If a validation handler exists for the form, assign it to this button.
if (isset($form_state['build_info']['callback_object'])) {
$form['buttons']['submit']['#validate'][] = array($form_state['build_info']['callback_object'], 'validateForm');
}
if (function_exists($form_id . '_validate')) {
$form['buttons']['submit']['#validate'][] = $form_id . '_validate';
}
@ -344,21 +320,6 @@ class ViewUI implements ViewStorageInterface {
'#validate' => array(),
);
// Some forms specify a third button, with a name and submit handler.
if ($third) {
if (empty($submit)) {
$submit = 'third';
}
$third_submit = function_exists($form_id . '_' . $submit) ? $form_id . '_' . $submit : array($this, 'standardCancel');
$form['buttons'][$submit] = array(
'#type' => 'submit',
'#value' => $third,
'#validate' => array(),
'#submit' => array($third_submit),
);
}
// Compatibility, to be removed later: // TODO: When is "later"?
// We used to set these items on the form, but now we want them on the $form_state:
if (isset($form['#title'])) {
@ -407,124 +368,11 @@ class ViewUI implements ViewStorageInterface {
return array($was_defaulted, $is_defaulted, $revert);
}
/**
* Form constructor callback to reorder displays on a view
*/
public function buildDisplaysReorderForm($form, &$form_state) {
$display_id = $form_state['display_id'];
$form['view'] = array('#type' => 'value', '#value' => $this);
$form['#tree'] = TRUE;
$count = count($this->get('display'));
$displays = $this->get('display');
uasort($displays, array('static', 'sortPosition'));
$this->set('display', $displays);
foreach ($displays as $display) {
$form[$display['id']] = array(
'title' => array('#markup' => $display['display_title']),
'weight' => array(
'#type' => 'weight',
'#value' => $display['position'],
'#delta' => $count,
'#title' => t('Weight for @display', array('@display' => $display['display_title'])),
'#title_display' => 'invisible',
),
'#tree' => TRUE,
'#display' => $display,
'removed' => array(
'#type' => 'checkbox',
'#id' => 'display-removed-' . $display['id'],
'#attributes' => array('class' => array('views-remove-checkbox')),
'#default_value' => isset($display['deleted']),
),
);
if (isset($display['deleted']) && $display['deleted']) {
$form[$display['id']]['deleted'] = array('#type' => 'value', '#value' => TRUE);
}
if ($display['id'] === 'default') {
unset($form[$display['id']]['weight']);
unset($form[$display['id']]['removed']);
}
}
$form['#title'] = t('Displays Reorder');
$form['#section'] = 'reorder';
// Add javascript settings that will be added via $.extend for tabledragging
$form['#js']['tableDrag']['reorder-displays']['weight'][0] = array(
'target' => 'weight',
'source' => NULL,
'relationship' => 'sibling',
'action' => 'order',
'hidden' => TRUE,
'limit' => 0,
);
$form['#action'] = url('admin/structure/views/nojs/reorder-displays/' . $this->id() . '/' . $display_id);
$this->getStandardButtons($form, $form_state, 'views_ui_reorder_displays_form');
$form['buttons']['submit']['#submit'] = array(array($this, 'submitDisplaysReorderForm'));
return $form;
}
/**
* Submit handler for rearranging display form
*/
public function submitDisplaysReorderForm($form, &$form_state) {
foreach ($form_state['input'] as $display => $info) {
// add each value that is a field with a weight to our list, but only if
// it has had its 'removed' checkbox checked.
if (is_array($info) && isset($info['weight']) && empty($info['removed'])) {
$order[$display] = $info['weight'];
}
}
// Sort the order array
asort($order);
// Fixing up positions
$position = 1;
foreach (array_keys($order) as $display) {
$order[$display] = $position++;
}
// Setting up position and removing deleted displays
$displays = $this->get('display');
foreach ($displays as $display_id => $display) {
// Don't touch the default !!!
if ($display_id === 'default') {
$displays[$display_id]['position'] = 0;
continue;
}
if (isset($order[$display_id])) {
$displays[$display_id]['position'] = $order[$display_id];
}
else {
$displays[$display_id]['deleted'] = TRUE;
}
}
// Sorting back the display array as the position is not enough
uasort($displays, array('static', 'sortPosition'));
$this->set('display', $displays);
// Store in cache
views_ui_cache_set($this);
$form_state['redirect'] = array('admin/structure/views/view/' . $this->id() . '/edit', array('fragment' => 'views-tab-default'));
}
/**
* Add another form to the stack; clicking 'apply' will go to this form
* rather than closing the ajax popup.
*/
public function addFormToStack($key, $display_id, $args, $top = FALSE, $rebuild_keys = FALSE) {
public function addFormToStack($key, $display_id, $type, $id = NULL, $top = FALSE, $rebuild_keys = FALSE) {
// Reset the cache of IDs. Drupal rather aggressively prevents ID
// duplication but this causes it to remember IDs that are no longer even
// being used.
@ -535,7 +383,7 @@ class ViewUI implements ViewStorageInterface {
$this->stack = array();
}
$stack = array($this->buildIdentifier($key, $display_id, $args), $key, $display_id, $args);
$stack = array(implode('-', array_filter(array($key, $this->id(), $display_id, $type, $id))), $key, $display_id, $type, $id);
// If we're being asked to add this form to the bottom of the stack, no
// special logic is required. Our work is equally easy if we were asked to add
// to the top of the stack, but there's nothing in it yet.
@ -613,15 +461,15 @@ class ViewUI implements ViewStorageInterface {
}
$handler = views_get_handler($table, $field, $key);
if ($this->executable->displayHandlers->get('default')->useGroupBy() && $handler->usesGroupBy()) {
$this->addFormToStack('config-item-group', $form_state['display_id'], array($type, $id));
$this->addFormToStack('config-item-group', $form_state['display_id'], $type, $id);
}
// check to see if this type has settings, if so add the settings form first
if ($handler && $handler->hasExtraOptions()) {
$this->addFormToStack('config-item-extra', $form_state['display_id'], array($type, $id));
$this->addFormToStack('config-item-extra', $form_state['display_id'], $type, $id);
}
// Then add the form to the stack
$this->addFormToStack('config-item', $form_state['display_id'], array($type, $id));
$this->addFormToStack('config-item', $form_state['display_id'], $type, $id);
}
}
@ -881,63 +729,6 @@ class ViewUI implements ViewStorageInterface {
return $progress;
}
/**
* Build a form identifier that we can use to see if one form
* is the same as another. Since the arguments differ slightly
* we do a lot of spiffy concatenation here.
*/
public function buildIdentifier($key, $display_id, $args) {
$form = static::$forms[$key];
// Automatically remove the single-form cache if it exists and
// does not match the key.
$identifier = implode('-', array($key, $this->id(), $display_id));
foreach ($form['args'] as $id) {
$arg = (!empty($args)) ? array_shift($args) : NULL;
$identifier .= '-' . $arg;
}
return $identifier;
}
/**
* Display position sorting function
*/
public static function sortPosition($display1, $display2) {
if ($display1['position'] != $display2['position']) {
return $display1['position'] < $display2['position'] ? -1 : 1;
}
return 0;
}
/**
* Build up a $form_state object suitable for use with drupal_build_form
* based on known information about a form.
*/
public function buildFormState($js, $key, $display_id, $args) {
$form = static::$forms[$key];
// Build up form state
$form_state = array(
'form_key' => $key,
'form_id' => $form['form_id'],
'view' => &$this,
'ajax' => $js,
'display_id' => $display_id,
'no_redirect' => TRUE,
);
// If an method was specified, use that for the callback.
if (isset($form['callback'])) {
$form_state['build_info']['args'] = array();
$form_state['build_info']['callback'] = array($this, $form['callback']);
}
foreach ($form['args'] as $id) {
$form_state[$id] = (!empty($args)) ? array_shift($args) : NULL;
}
return $form_state;
}
/**
* Passes through all unknown calls onto the storage object.
*/

View File

@ -109,15 +109,113 @@ views_ui.breakLock:
requirements:
_permission: 'administer views'
views_ui.ajaxForm:
pattern: '/admin/structure/views/{js}/{key}/{view}/{display_id}/{type}/{id}'
views_ui.form.addItem:
pattern: '/admin/structure/views/{js}/add-item/{view}/{display_id}/{type}'
options:
tempstore:
view: 'views'
defaults:
_controller: 'views_ui.controller:ajaxForm'
type: NULL
id: NULL
_controller: '\Drupal\views_ui\Form\Ajax\AddItem::getForm'
requirements:
_permission: 'administer views'
js: 'nojs|ajax'
views_ui.form.editDetails:
pattern: '/admin/structure/views/{js}/edit-details/{view}/{display_id}'
options:
tempstore:
view: 'views'
defaults:
_controller: '\Drupal\views_ui\Form\Ajax\EditDetails::getForm'
requirements:
_permission: 'administer views'
js: 'nojs|ajax'
views_ui.form.reorderDisplays:
pattern: '/admin/structure/views/{js}/reorder-displays/{view}/{display_id}'
options:
tempstore:
view: 'views'
defaults:
_controller: '\Drupal\views_ui\Form\Ajax\ReorderDisplays::getForm'
requirements:
_permission: 'administer views'
js: 'nojs|ajax'
views_ui.form.analyze:
pattern: '/admin/structure/views/{js}/analyze/{view}/{display_id}'
options:
tempstore:
view: 'views'
defaults:
_controller: '\Drupal\views_ui\Form\Ajax\Analyze::getForm'
requirements:
_permission: 'administer views'
js: 'nojs|ajax'
views_ui.form.rearrange:
pattern: '/admin/structure/views/{js}/rearrange/{view}/{display_id}/{type}'
options:
tempstore:
view: 'views'
defaults:
_controller: '\Drupal\views_ui\Form\Ajax\Rearrange::getForm'
requirements:
_permission: 'administer views'
js: 'nojs|ajax'
views_ui.form.rearrangeFilter:
pattern: '/admin/structure/views/{js}/rearrange-filter/{view}/{display_id}'
options:
tempstore:
view: 'views'
defaults:
_controller: '\Drupal\views_ui\Form\Ajax\RearrangeFilter::getForm'
requirements:
_permission: 'administer views'
js: 'nojs|ajax'
views_ui.form.display:
pattern: '/admin/structure/views/{js}/display/{view}/{display_id}/{type}'
options:
tempstore:
view: 'views'
defaults:
_controller: '\Drupal\views_ui\Form\Ajax\Display::getForm'
requirements:
_permission: 'administer views'
js: 'nojs|ajax'
views_ui.form.configItem:
pattern: '/admin/structure/views/{js}/config-item/{view}/{display_id}/{type}/{id}'
options:
tempstore:
view: 'views'
defaults:
_controller: '\Drupal\views_ui\Form\Ajax\ConfigItem::getForm'
requirements:
_permission: 'administer views'
js: 'nojs|ajax'
views_ui.form.configItemExtra:
pattern: '/admin/structure/views/{js}/config-item-extra/{view}/{display_id}/{type}/{id}'
options:
tempstore:
view: 'views'
defaults:
_controller: '\Drupal\views_ui\Form\Ajax\ConfigItemExtra::getForm'
requirements:
_permission: 'administer views'
js: 'nojs|ajax'
views_ui.form.configItemGroup:
pattern: '/admin/structure/views/{js}/config-item-group/{view}/{display_id}/{type}/{id}'
options:
tempstore:
view: 'views'
defaults:
_controller: '\Drupal\views_ui\Form\Ajax\ConfigItemGroup::getForm'
form_state: NULL
requirements:
_permission: 'administer views'
js: 'nojs|ajax'