From 057dd5110f83f56461fc7b52e66a15d3a1ed5452 Mon Sep 17 00:00:00 2001 From: webchick Date: Sun, 3 Mar 2013 17:01:21 -0500 Subject: [PATCH] Issue #1919142 by tim.plunkett, dawehner: Convert Views UI AJAX forms to use FormInterface. --- .../Drupal/field/Plugin/views/field/Field.php | 2 +- .../Drupal/views/Plugin/Core/Entity/View.php | 2 +- .../Drupal/views/Plugin/views/HandlerBase.php | 82 ++ .../views/display/DisplayPluginBase.php | 48 +- .../views/Plugin/views/display/Page.php | 2 +- .../Plugin/views/filter/FilterPluginBase.php | 51 +- .../Plugin/views/sort/SortPluginBase.php | 4 +- .../Drupal/views/Tests/Plugin/PagerTest.php | 4 +- .../lib/Drupal/views/Tests/UI/HandlerTest.php | 2 +- .../Drupal/views/ViewStorageController.php | 35 +- core/modules/views/views_ui/admin.inc | 1222 +---------------- .../lib/Drupal/views_ui/Form/Ajax/AddItem.php | 187 +++ .../lib/Drupal/views_ui/Form/Ajax/Analyze.php | 63 + .../Drupal/views_ui/Form/Ajax/ConfigItem.php | 268 ++++ .../views_ui/Form/Ajax/ConfigItemExtra.php | 121 ++ .../views_ui/Form/Ajax/ConfigItemGroup.php | 115 ++ .../lib/Drupal/views_ui/Form/Ajax/Display.php | 119 ++ .../Drupal/views_ui/Form/Ajax/EditDetails.php | 88 ++ .../Drupal/views_ui/Form/Ajax/Rearrange.php | 167 +++ .../views_ui/Form/Ajax/RearrangeFilter.php | 318 +++++ .../views_ui/Form/Ajax/ReorderDisplays.php | 140 ++ .../views_ui/Form/Ajax/ViewsFormBase.php | 188 +++ .../views_ui/Form/Ajax/ViewsFormInterface.php | 60 + .../views_ui/Routing/ViewsUIController.php | 126 -- .../views_ui/ViewEditFormController.php | 2 +- .../views_ui/ViewFormControllerBase.php | 2 - .../views_ui/lib/Drupal/views_ui/ViewUI.php | 269 +--- .../views/views_ui/views_ui.routing.yml | 108 +- 28 files changed, 2169 insertions(+), 1626 deletions(-) create mode 100644 core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/AddItem.php create mode 100644 core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/Analyze.php create mode 100644 core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/ConfigItem.php create mode 100644 core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/ConfigItemExtra.php create mode 100644 core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/ConfigItemGroup.php create mode 100644 core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/Display.php create mode 100644 core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/EditDetails.php create mode 100644 core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/Rearrange.php create mode 100644 core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/RearrangeFilter.php create mode 100644 core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/ReorderDisplays.php create mode 100644 core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/ViewsFormBase.php create mode 100644 core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/ViewsFormInterface.php diff --git a/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php b/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php index 30836a33e29..e7909165286 100644 --- a/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php +++ b/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php @@ -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, ); diff --git a/core/modules/views/lib/Drupal/views/Plugin/Core/Entity/View.php b/core/modules/views/lib/Drupal/views/Plugin/Core/Entity/View.php index 7cd98af8659..f3c02be3f72 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/Core/Entity/View.php +++ b/core/modules/views/lib/Drupal/views/Plugin/Core/Entity/View.php @@ -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. diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php index b187be802fb..a0fad3684a7 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php +++ b/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php @@ -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']); + } + } diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php index 584ea9b6a6a..8da5f600e63 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php +++ b/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php @@ -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' => '
' . t("Important! 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.") . '
', @@ -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'); } } } diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/display/Page.php b/core/modules/views/lib/Drupal/views/Plugin/views/display/Page.php index f2846c3abc5..0baba6a2e65 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/views/display/Page.php +++ b/core/modules/views/lib/Drupal/views/Plugin/views/display/Page.php @@ -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': diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/filter/FilterPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/filter/FilterPluginBase.php index b737cc7a8ae..7289e146ded 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/views/filter/FilterPluginBase.php +++ b/core/modules/views/lib/Drupal/views/Plugin/views/filter/FilterPluginBase.php @@ -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' => '', '#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 diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/sort/SortPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/sort/SortPluginBase.php index 17f1a5f4997..ece3ae5a715 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/views/sort/SortPluginBase.php +++ b/core/modules/views/lib/Drupal/views/Plugin/views/sort/SortPluginBase.php @@ -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; } diff --git a/core/modules/views/lib/Drupal/views/Tests/Plugin/PagerTest.php b/core/modules/views/lib/Drupal/views/Tests/Plugin/PagerTest.php index 6be2dc846ea..f6c0e31f9c3 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Plugin/PagerTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/Plugin/PagerTest.php @@ -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.'); } diff --git a/core/modules/views/lib/Drupal/views/Tests/UI/HandlerTest.php b/core/modules/views/lib/Drupal/views/Tests/UI/HandlerTest.php index 19fa21c4e23..e795248d0d9 100644 --- a/core/modules/views/lib/Drupal/views/Tests/UI/HandlerTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/UI/HandlerTest.php @@ -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.'); diff --git a/core/modules/views/lib/Drupal/views/ViewStorageController.php b/core/modules/views/lib/Drupal/views/ViewStorageController.php index 8a5f87ae6ca..1605b0c261e 100644 --- a/core/modules/views/lib/Drupal/views/ViewStorageController.php +++ b/core/modules/views/lib/Drupal/views/ViewStorageController.php @@ -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); } } diff --git a/core/modules/views/views_ui/admin.inc b/core/modules/views/views_ui/admin.inc index 53601bff09f..abf75b02ba7 100644 --- a/core/modules/views/views_ui/admin.inc +++ b/core/modules/views/views_ui/admin.inc @@ -6,7 +6,6 @@ */ use Drupal\Component\Utility\NestedArray; -use Drupal\views\Analyzer; use Drupal\views\ViewExecutable; /** @@ -396,1229 +395,18 @@ function views_ui_standard_display_dropdown(&$form, &$form_state, $section) { * information about the form. */ function views_ui_build_form_url($form_state) { - $form = $form_state['view']::$forms[$form_state['form_key']]; $ajax = empty($form_state['ajax']) ? 'nojs' : 'ajax'; $name = $form_state['view']->id(); $url = "admin/structure/views/$ajax/$form_state[form_key]/$name/$form_state[display_id]"; - foreach ($form['args'] as $arg) { - $url .= '/' . $form_state[$arg]; + if (isset($form_state['type'])) { + $url .= '/' . $form_state['type']; + } + if (isset($form_state['id'])) { + $url .= '/' . $form_state['id']; } return $url; } -/** - * Form constructor callback to display analysis information on a view - */ -function views_ui_analyze_view_form($form, &$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' => '
', - '#suffix' => '
', - '#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; -} - -/** - * Submit handler for views_ui_analyze_view_form - */ -function views_ui_analyze_view_form_submit($form, &$form_state) { - $form_state['redirect'] = 'admin/structure/views/view/' . $form_state['view']->id() . '/edit'; -} - -/** - * Form builder to edit details of a view. - */ -function views_ui_edit_details_form($form, &$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; -} - -/** - * Submit handler for views_ui_edit_details_form. - */ -function views_ui_edit_details_form_submit($form, &$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); -} - -/** - * Form constructor callback to edit display of a view - */ -function views_ui_edit_display_form($form, &$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; -} - -/** - * Validate handler for views_ui_edit_display_form - */ -function views_ui_edit_display_form_validate($form, &$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; - } -} - -/** - * Submit handler for views_ui_edit_display_form - */ -function views_ui_edit_display_form_submit($form, &$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']); -} - -/** - * Override handler for views_ui_edit_display_form - * - * @TODO: Not currently used. Remove unless we implement an override toggle. - */ -function views_ui_edit_display_form_override($form, &$form_state) { - $form_state['view']->get('executable')->displayHandlers->get($form_state['display_id'])->optionsOverride($form['options'], $form_state); - - views_ui_cache_set($form_state['view']); - $form_state['rerender'] = TRUE; - $form_state['rebuild'] = TRUE; -} - -/** - * Form to rearrange items in the views UI. - */ -function views_ui_rearrange_form($form, &$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; -} - -/** - * Submit handler for rearranging form. - */ -function views_ui_rearrange_form_submit($form, &$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']); -} - -/** - * Form to rearrange items in the views UI. - */ -function views_ui_rearrange_filter_form($form, &$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_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; -} - -/** - * Submit handler for rearranging form - */ -function views_ui_rearrange_filter_form_submit($form, &$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[$form_state['type']]['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'], array($form_state['type'])); - } - 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[$form_state['type']]['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']); -} - -/** - * Form to add_item items in the views UI. - */ -function views_ui_add_item_form($form, &$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' => '
', - '#suffix' => '
', - '#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' => "
", - '#suffix' => '
', - '#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' => '
' . t('There are no @types available to add.', array('@types' => $ltitle)) . '
', - ); - } - // Add a div to show the selected items - $form['selected'] = array( - '#type' => 'item', - '#markup' => '
', - '#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; -} - -/** - * Override handler for views_ui_edit_display_form - */ -function views_ui_config_item_form_build_group($form, &$form_state) { - $item = &$form_state['handler']->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']) { - $form_state['handler']->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'], array($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; -} - -/** - * Add a new group to the exposed filter groups. - */ -function views_ui_config_item_form_add_group($form, &$form_state) { - $item =& $form_state['handler']->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; -} - -/** - * Form to config_item items in the views UI. - */ -function views_ui_config_item_form($form, &$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, t('Remove'), 'remove'); - // Only validate the override values, because this values are required for - // the override selection. - $form['buttons']['remove']['#limit_validation_errors'] = array(array('override')); - } - - if ($save_ui_cache) { - views_ui_cache_set($view); - } - - return $form; -} - -/** - * Submit handler for configing new item(s) to a view. - */ -function views_ui_config_item_form_validate($form, &$form_state) { - $form_state['handler']->validateOptionsForm($form['options'], $form_state); - - if (form_get_errors()) { - $form_state['rerender'] = TRUE; - } -} - -/** - * A submit handler that is used for storing temporary items when using - * multi-step changes, such as ajax requests. - */ -function views_ui_config_item_form_submit_temporary($form, &$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 = $form_state['view']->get('executable'); - $executable->temporary_options[$type][$form_state['id']] = $handler->options; - - // @todo Decide if \Drupal\views_ui\Routing\ViewsUIController::ajaxForm() is - // perhaps the better place to fix the issue. - // \Drupal\views_ui\Routing\ViewsUIController::ajaxForm() 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'], array($type, $item['id']), TRUE); - - $form_state['rerender'] = TRUE; - $form_state['rebuild'] = TRUE; - // Write to cache - views_ui_cache_set($form_state['view']); -} - -/** - * Submit handler for configing new item(s) to a view. - */ -function views_ui_config_item_form_submit($form, &$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']); -} - -/** - * Form to config_item items in the views UI. - */ -function views_ui_config_item_group_form($type, &$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; -} - -/** - * Submit handler for configing group settings on a view. - */ -function views_ui_config_item_group_form_submit($form, &$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']); -} - -/** - * Submit handler for removing an item from a view - */ -function views_ui_config_item_form_remove($form, &$form_state) { - // Store the item back on the view - list($was_defaulted, $is_defaulted) = $form_state['view']->getOverrideValues($form, $form_state); - // If the display selection was changed toggle the override value. - if ($was_defaulted != $is_defaulted) { - $display =& $form_state['view']->get('executable')->displayHandlers->get($form_state['display_id']); - $display->optionsOverride($form, $form_state); - } - $form_state['view']->get('executable')->removeItem($form_state['display_id'], $form_state['type'], $form_state['id']); - - // Write to cache - views_ui_cache_set($form_state['view']); -} - -/** - * Override handler for views_ui_edit_display_form - */ -function views_ui_config_item_form_expose($form, &$form_state) { - $item = &$form_state['handler']->options; - // flip - $item['exposed'] = empty($item['exposed']); - - // If necessary, set new defaults: - if ($item['exposed']) { - $form_state['handler']->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'], array($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; -} - -/** - * Form to config_item items in the views UI. - */ -function views_ui_config_item_extra_form($form, &$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; -} - -/** - * Validation handler for configing new item(s) to a view. - */ -function views_ui_config_item_extra_form_validate($form, &$form_state) { - $form_state['handler']->validateExtraOptionsForm($form['options'], $form_state); -} - -/** - * Submit handler for configing new item(s) to a view. - */ -function views_ui_config_item_extra_form_submit($form, &$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']); -} - -/** - * Submit hook to clear Drupal's theme registry (thereby triggering - * a templates rescan). - */ -function views_ui_config_item_form_rescan($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; -} - -/** - * Override handler for views_ui_edit_display_form - */ -function views_ui_edit_display_form_change_theme($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; -} - function _views_sort_types($a, $b) { $a_group = drupal_strtolower($a['group']); $b_group = drupal_strtolower($b['group']); diff --git a/core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/AddItem.php b/core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/AddItem.php new file mode 100644 index 00000000000..1c8d13e0456 --- /dev/null +++ b/core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/AddItem.php @@ -0,0 +1,187 @@ +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' => '
', + '#suffix' => '
', + '#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' => "
", + '#suffix' => '
', + '#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' => '
' . t('There are no @types available to add.', array('@types' => $ltitle)) . '
', + ); + } + // Add a div to show the selected items + $form['selected'] = array( + '#type' => 'item', + '#markup' => '
', + '#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; + } + +} diff --git a/core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/Analyze.php b/core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/Analyze.php new file mode 100644 index 00000000000..f80fee46bac --- /dev/null +++ b/core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/Analyze.php @@ -0,0 +1,63 @@ +get('views.analyzer'); + $messages = $analyzer->getMessages($view->get('executable')); + + $form['analysis'] = array( + '#prefix' => '
', + '#suffix' => '
', + '#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'; + } + +} diff --git a/core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/ConfigItem.php b/core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/ConfigItem.php new file mode 100644 index 00000000000..67ed22b1c9f --- /dev/null +++ b/core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/ConfigItem.php @@ -0,0 +1,268 @@ +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']); + } + +} diff --git a/core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/ConfigItemExtra.php b/core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/ConfigItemExtra.php new file mode 100644 index 00000000000..c0d673e7d69 --- /dev/null +++ b/core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/ConfigItemExtra.php @@ -0,0 +1,121 @@ +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']); + } + +} diff --git a/core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/ConfigItemGroup.php b/core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/ConfigItemGroup.php new file mode 100644 index 00000000000..96229d42af9 --- /dev/null +++ b/core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/ConfigItemGroup.php @@ -0,0 +1,115 @@ +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']); + } + +} diff --git a/core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/Display.php b/core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/Display.php new file mode 100644 index 00000000000..5642e8640ba --- /dev/null +++ b/core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/Display.php @@ -0,0 +1,119 @@ +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']); + } + +} diff --git a/core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/EditDetails.php b/core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/EditDetails.php new file mode 100644 index 00000000000..ad9a0562b2d --- /dev/null +++ b/core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/EditDetails.php @@ -0,0 +1,88 @@ + 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); + } + +} diff --git a/core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/Rearrange.php b/core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/Rearrange.php new file mode 100644 index 00000000000..de01217fb31 --- /dev/null +++ b/core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/Rearrange.php @@ -0,0 +1,167 @@ +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']); + } + +} diff --git a/core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/RearrangeFilter.php b/core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/RearrangeFilter.php new file mode 100644 index 00000000000..446d8874e1a --- /dev/null +++ b/core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/RearrangeFilter.php @@ -0,0 +1,318 @@ +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']); + } + +} + diff --git a/core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/ReorderDisplays.php b/core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/ReorderDisplays.php new file mode 100644 index 00000000000..a14210cd8a5 --- /dev/null +++ b/core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/ReorderDisplays.php @@ -0,0 +1,140 @@ + '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')); + } + +} diff --git a/core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/ViewsFormBase.php b/core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/ViewsFormBase.php new file mode 100644 index 00000000000..b5ce81c378f --- /dev/null +++ b/core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/ViewsFormBase.php @@ -0,0 +1,188 @@ +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) { + } + +} diff --git a/core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/ViewsFormInterface.php b/core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/ViewsFormInterface.php new file mode 100644 index 00000000000..ad134af7a6a --- /dev/null +++ b/core/modules/views/views_ui/lib/Drupal/views_ui/Form/Ajax/ViewsFormInterface.php @@ -0,0 +1,60 @@ + $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; - } - } diff --git a/core/modules/views/views_ui/lib/Drupal/views_ui/ViewEditFormController.php b/core/modules/views/views_ui/lib/Drupal/views_ui/ViewEditFormController.php index 80d1b7cb81a..3ce86e8b67b 100644 --- a/core/modules/views/views_ui/lib/Drupal/views_ui/ViewEditFormController.php +++ b/core/modules/views/views_ui/lib/Drupal/views_ui/ViewEditFormController.php @@ -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'; diff --git a/core/modules/views/views_ui/lib/Drupal/views_ui/ViewFormControllerBase.php b/core/modules/views/views_ui/lib/Drupal/views_ui/ViewFormControllerBase.php index f05f0dedb0c..17db2ecf27d 100644 --- a/core/modules/views/views_ui/lib/Drupal/views_ui/ViewFormControllerBase.php +++ b/core/modules/views/views_ui/lib/Drupal/views_ui/ViewFormControllerBase.php @@ -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', diff --git a/core/modules/views/views_ui/lib/Drupal/views_ui/ViewUI.php b/core/modules/views/views_ui/lib/Drupal/views_ui/ViewUI.php index 8a26695d1dd..73d96e74ec8 100644 --- a/core/modules/views/views_ui/lib/Drupal/views_ui/ViewUI.php +++ b/core/modules/views/views_ui/lib/Drupal/views_ui/ViewUI.php @@ -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' => '
', '#suffix' => '
', @@ -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. */ diff --git a/core/modules/views/views_ui/views_ui.routing.yml b/core/modules/views/views_ui/views_ui.routing.yml index ed597c25e6e..dfb53ca3057 100644 --- a/core/modules/views/views_ui/views_ui.routing.yml +++ b/core/modules/views/views_ui/views_ui.routing.yml @@ -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'