renderPreview($display_id, $args);
}
/**
* Page callback to add a new view.
*/
function views_ui_add_page() {
drupal_set_title(t('Add new view'));
$form_state['build_info']['args'] = array();
$form_state['build_info']['callback'] = array('Drupal\views\ViewUI', 'addForm');
return drupal_build_form('views_ui_add_form', $form_state);
}
/**
* Gets the current value of a #select element, from within a form constructor function.
*
* This function is intended for use in highly dynamic forms (in particular the
* add view wizard) which are rebuilt in different ways depending on which
* triggering element (AJAX or otherwise) was most recently fired. For example,
* sometimes it is necessary to decide how to build one dynamic form element
* based on the value of a different dynamic form element that may not have
* even been present on the form the last time it was submitted. This function
* takes care of resolving those conflicts and gives you the proper current
* value of the requested #select element.
*
* By necessity, this function sometimes uses non-validated user input from
* $form_state['input'] in making its determination. Although it performs some
* minor validation of its own, it is not complete. The intention is that the
* return value of this function should only be used to help decide how to
* build the current form the next time it is reloaded, not to be saved as if
* it had gone through the normal, final form validation process. Do NOT use
* the results of this function for any other purpose besides deciding how to
* build the next version of the form.
*
* @param $form_state
* The standard associative array containing the current state of the form.
* @param $parents
* An array of parent keys that point to the part of the submitted form
* values that are expected to contain the element's value (in the case where
* this form element was actually submitted). In a simple case (assuming
* #tree is TRUE throughout the form), if the select element is located in
* $form['wrapper']['select'], so that the submitted form values would
* normally be found in $form_state['values']['wrapper']['select'], you would
* pass array('wrapper', 'select') for this parameter.
* @param $default_value
* The default value to return if the #select element does not currently have
* a proper value set based on the submitted input.
* @param $element
* An array representing the current version of the #select element within
* the form.
*
* @return
* The current value of the #select element. A common use for this is to feed
* it back into $element['#default_value'] so that the form will be rendered
* with the correct value selected.
*/
function views_ui_get_selected($form_state, $parents, $default_value, $element) {
// For now, don't trust this to work on anything but a #select element.
if (!isset($element['#type']) || $element['#type'] != 'select' || !isset($element['#options'])) {
return $default_value;
}
// If there is a user-submitted value for this element that matches one of
// the currently available options attached to it, use that. We need to check
// $form_state['input'] rather than $form_state['values'] here because the
// triggering element often has the #limit_validation_errors property set to
// prevent unwanted errors elsewhere on the form. This means that the
// $form_state['values'] array won't be complete. We could make it complete
// by adding each required part of the form to the #limit_validation_errors
// property individually as the form is being built, but this is difficult to
// do for a highly dynamic and extensible form. This method is much simpler.
if (!empty($form_state['input'])) {
$key_exists = NULL;
$submitted = drupal_array_get_nested_value($form_state['input'], $parents, $key_exists);
// Check that the user-submitted value is one of the allowed options before
// returning it. This is not a substitute for actual form validation;
// rather it is necessary because, for example, the same select element
// might have #options A, B, and C under one set of conditions but #options
// D, E, F under a different set of conditions. So the form submission
// might have occurred with option A selected, but when the form is rebuilt
// option A is no longer one of the choices. In that case, we don't want to
// use the value that was submitted anymore but rather fall back to the
// default value.
if ($key_exists && in_array($submitted, array_keys($element['#options']))) {
return $submitted;
}
}
// Fall back on returning the default value if nothing was returned above.
return $default_value;
}
/**
* Converts a form element in the add view wizard to be AJAX-enabled.
*
* This function takes a form element and adds AJAX behaviors to it such that
* changing it triggers another part of the form to update automatically. It
* also adds a submit button to the form that appears next to the triggering
* element and that duplicates its functionality for users who do not have
* JavaScript enabled (the button is automatically hidden for users who do have
* JavaScript).
*
* To use this function, call it directly from your form builder function
* immediately after you have defined the form element that will serve as the
* JavaScript trigger. Calling it elsewhere (such as in hook_form_alter()) may
* mean that the non-JavaScript fallback button does not appear in the correct
* place in the form.
*
* @param $wrapping_element
* The element whose child will server as the AJAX trigger. For example, if
* $form['some_wrapper']['triggering_element'] represents the element which
* will trigger the AJAX behavior, you would pass $form['some_wrapper'] for
* this parameter.
* @param $trigger_key
* The key within the wrapping element that identifies which of its children
* serves as the AJAX trigger. In the above example, you would pass
* 'triggering_element' for this parameter.
* @param $refresh_parents
* An array of parent keys that point to the part of the form that will be
* refreshed by AJAX. For example, if triggering the AJAX behavior should
* cause $form['dynamic_content']['section'] to be refreshed, you would pass
* array('dynamic_content', 'section') for this parameter.
*/
function views_ui_add_ajax_trigger(&$wrapping_element, $trigger_key, $refresh_parents) {
$seen_ids = &drupal_static(__FUNCTION__ . ':seen_ids', array());
$seen_buttons = &drupal_static(__FUNCTION__ . ':seen_buttons', array());
// Add the AJAX behavior to the triggering element.
$triggering_element = &$wrapping_element[$trigger_key];
$triggering_element['#ajax']['callback'] = 'views_ui_ajax_update_form';
// We do not use drupal_html_id() to get an ID for the AJAX wrapper, because
// it remembers IDs across AJAX requests (and won't reuse them), but in our
// case we need to use the same ID from request to request so that the
// wrapper can be recognized by the AJAX system and its content can be
// dynamically updated. So instead, we will keep track of duplicate IDs
// (within a single request) on our own, later in this function.
$triggering_element['#ajax']['wrapper'] = 'edit-view-' . implode('-', $refresh_parents) . '-wrapper';
// Add a submit button for users who do not have JavaScript enabled. It
// should be displayed next to the triggering element on the form.
$button_key = $trigger_key . '_trigger_update';
$wrapping_element[$button_key] = array(
'#type' => 'submit',
// Hide this button when JavaScript is enabled.
'#attributes' => array('class' => array('js-hide')),
'#submit' => array('views_ui_nojs_submit'),
// Add a process function to limit this button's validation errors to the
// triggering element only. We have to do this in #process since until the
// form API has added the #parents property to the triggering element for
// us, we don't have any (easy) way to find out where its submitted values
// will eventually appear in $form_state['values'].
'#process' => array_merge(array('views_ui_add_limited_validation'), element_info_property('submit', '#process', array())),
// Add an after-build function that inserts a wrapper around the region of
// the form that needs to be refreshed by AJAX (so that the AJAX system can
// detect and dynamically update it). This is done in #after_build because
// it's a convenient place where we have automatic access to the complete
// form array, but also to minimize the chance that the HTML we add will
// get clobbered by code that runs after we have added it.
'#after_build' => array_merge(element_info_property('submit', '#after_build', array()), array('views_ui_add_ajax_wrapper')),
);
// Copy #weight and #access from the triggering element to the button, so
// that the two elements will be displayed together.
foreach (array('#weight', '#access') as $property) {
if (isset($triggering_element[$property])) {
$wrapping_element[$button_key][$property] = $triggering_element[$property];
}
}
// For easiest integration with the form API and the testing framework, we
// always give the button a unique #value, rather than playing around with
// #name.
$button_title = !empty($triggering_element['#title']) ? $triggering_element['#title'] : $trigger_key;
if (empty($seen_buttons[$button_title])) {
$wrapping_element[$button_key]['#value'] = t('Update "@title" choice', array(
'@title' => $button_title,
));
$seen_buttons[$button_title] = 1;
}
else {
$wrapping_element[$button_key]['#value'] = t('Update "@title" choice (@number)', array(
'@title' => $button_title,
'@number' => ++$seen_buttons[$button_title],
));
}
// Attach custom data to the triggering element and submit button, so we can
// use it in both the process function and AJAX callback.
$ajax_data = array(
'wrapper' => $triggering_element['#ajax']['wrapper'],
'trigger_key' => $trigger_key,
'refresh_parents' => $refresh_parents,
// Keep track of duplicate wrappers so we don't add the same wrapper to the
// page more than once.
'duplicate_wrapper' => !empty($seen_ids[$triggering_element['#ajax']['wrapper']]),
);
$seen_ids[$triggering_element['#ajax']['wrapper']] = TRUE;
$triggering_element['#views_ui_ajax_data'] = $ajax_data;
$wrapping_element[$button_key]['#views_ui_ajax_data'] = $ajax_data;
}
/**
* Processes a non-JavaScript fallback submit button to limit its validation errors.
*/
function views_ui_add_limited_validation($element, &$form_state) {
// Retrieve the AJAX triggering element so we can determine its parents. (We
// know it's at the same level of the complete form array as the submit
// button, so all we have to do to find it is swap out the submit button's
// last array parent.)
$array_parents = $element['#array_parents'];
array_pop($array_parents);
$array_parents[] = $element['#views_ui_ajax_data']['trigger_key'];
$ajax_triggering_element = drupal_array_get_nested_value($form_state['complete_form'], $array_parents);
// Limit this button's validation to the AJAX triggering element, so it can
// update the form for that change without requiring that the rest of the
// form be filled out properly yet.
$element['#limit_validation_errors'] = array($ajax_triggering_element['#parents']);
// If we are in the process of a form submission and this is the button that
// was clicked, the form API workflow in form_builder() will have already
// copied it to $form_state['triggering_element'] before our #process
// function is run. So we need to make the same modifications in $form_state
// as we did to the element itself, to ensure that #limit_validation_errors
// will actually be set in the correct place.
if (!empty($form_state['triggering_element'])) {
$clicked_button = &$form_state['triggering_element'];
if ($clicked_button['#name'] == $element['#name'] && $clicked_button['#value'] == $element['#value']) {
$clicked_button['#limit_validation_errors'] = $element['#limit_validation_errors'];
}
}
return $element;
}
/**
* After-build function that adds a wrapper to a form region (for AJAX refreshes).
*
* This function inserts a wrapper around the region of the form that needs to
* be refreshed by AJAX, based on information stored in the corresponding
* submit button form element.
*/
function views_ui_add_ajax_wrapper($element, &$form_state) {
// Don't add the wrapper
if the same one was already inserted on this
// form.
if (empty($element['#views_ui_ajax_data']['duplicate_wrapper'])) {
// Find the region of the complete form that needs to be refreshed by AJAX.
// This was earlier stored in a property on the element.
$complete_form = &$form_state['complete_form'];
$refresh_parents = $element['#views_ui_ajax_data']['refresh_parents'];
$refresh_element = drupal_array_get_nested_value($complete_form, $refresh_parents);
// The HTML ID that AJAX expects was also stored in a property on the
// element, so use that information to insert the wrapper
';
// Copy the element that needs to be refreshed back into the form, with our
// modifications to it.
drupal_array_set_nested_value($complete_form, $refresh_parents, $refresh_element);
}
return $element;
}
/**
* Updates a part of the add view form via AJAX.
*
* @return
* The part of the form that has changed.
*/
function views_ui_ajax_update_form($form, $form_state) {
// The region that needs to be updated was stored in a property of the
// triggering element by views_ui_add_ajax_trigger(), so all we have to do is
// retrieve that here.
return drupal_array_get_nested_value($form, $form_state['triggering_element']['#views_ui_ajax_data']['refresh_parents']);
}
/**
* Non-Javascript fallback for updating the add view form.
*/
function views_ui_nojs_submit($form, &$form_state) {
$form_state['rebuild'] = TRUE;
}
/**
* Validate the add view form.
*/
function views_ui_wizard_form_validate($form, &$form_state) {
$wizard = views_ui_get_wizard($form_state['values']['show']['wizard_key']);
$form_state['wizard'] = $wizard;
$form_state['wizard_instance'] = views_get_plugin('wizard', $wizard['id']);
$errors = $form_state['wizard_instance']->validateView($form, $form_state);
foreach ($errors as $name => $message) {
form_set_error($name, $message);
}
}
/**
* Process the add view form, 'save'.
*/
function views_ui_add_form_save_submit($form, &$form_state) {
try {
$view = $form_state['wizard_instance']->create_view($form, $form_state);
}
catch (WizardException $e) {
drupal_set_message($e->getMessage(), 'error');
$form_state['redirect'] = 'admin/structure/views';
}
$view->save();
$form_state['redirect'] = 'admin/structure/views';
if (!empty($view->displayHandlers['page'])) {
$display = $view->displayHandlers['page'];
if ($display->hasPath()) {
$one_path = $display->getOption('path');
if (strpos($one_path, '%') === FALSE) {
$form_state['redirect'] = $one_path; // PATH TO THE VIEW IF IT HAS ONE
return;
}
}
}
drupal_set_message(t('Your view was saved. You may edit it from the list below.'));
}
/**
* Process the add view form, 'continue'.
*/
function views_ui_add_form_store_edit_submit($form, &$form_state) {
try {
$view = $form_state['wizard_instance']->create_view($form, $form_state);
}
catch (WizardException $e) {
drupal_set_message($e->getMessage(), 'error');
$form_state['redirect'] = 'admin/structure/views';
}
// Just cache it temporarily to edit it.
views_ui_cache_set($view);
// If there is a destination query, ensure we still redirect the user to the
// edit view page, and then redirect the user to the destination.
// @todo: Revisit this when http://drupal.org/node/1668866 is in.
$destination = array();
$query = drupal_container()->get('request')->query;
if ($query->has('destination')) {
$destination = drupal_get_destination();
$query->remove('destination');
}
$form_state['redirect'] = array('admin/structure/views/view/' . $view->storage->name, array('query' => $destination));
}
/**
* Cancel the add view form.
*/
function views_ui_add_form_cancel_submit($form, &$form_state) {
$form_state['redirect'] = 'admin/structure/views';
}
/**
* Form element validation handler for a taxonomy autocomplete field.
*
* This allows a taxonomy autocomplete field to be validated outside the
* standard Field API workflow, without passing in a complete field widget.
* Instead, all that is required is that $element['#field_name'] contain the
* name of the taxonomy autocomplete field that is being validated.
*
* This function is currently not used for validation directly, although it
* could be. Instead, it is only used to store the term IDs and vocabulary name
* in the element value, based on the tags that the user typed in.
*
* @see taxonomy_autocomplete_validate()
*/
function views_ui_taxonomy_autocomplete_validate($element, &$form_state) {
$value = array();
if ($tags = $element['#value']) {
// Get the machine names of the vocabularies we will search, keyed by the
// vocabulary IDs.
$field = field_info_field($element['#field_name']);
$vocabularies = array();
if (!empty($field['settings']['allowed_values'])) {
foreach ($field['settings']['allowed_values'] as $tree) {
if ($vocabulary = taxonomy_vocabulary_machine_name_load($tree['vocabulary'])) {
$vocabularies[$vocabulary->vid] = $tree['vocabulary'];
}
}
}
// Store the term ID of each (valid) tag that the user typed.
$typed_terms = drupal_explode_tags($tags);
foreach ($typed_terms as $typed_term) {
if ($terms = entity_load_multiple_by_properties('taxonomy_term', array('name' => trim($typed_term), 'vid' => array_keys($vocabularies)))) {
$term = array_pop($terms);
$value['tids'][] = $term->tid;
}
}
// Store the term IDs along with the name of the vocabulary. Currently
// Views (as well as the Field UI) assumes that there will only be one
// vocabulary, although technically the API allows there to be more than
// one.
if (!empty($value['tids'])) {
$value['tids'] = array_unique($value['tids']);
$value['vocabulary'] = array_pop($vocabularies);
}
}
form_set_value($element, $value, $form_state);
}
/**
* Implements hook_preprocess_HOOK() for theme_views_ui_view_info().
*/
function template_preprocess_views_ui_view_info(&$variables) {
$variables['title'] = $variables['view']->getHumanName();
$displays = $variables['view']->getDisplaysList();
$variables['displays'] = empty($displays) ? t('None') : format_plural(count($displays), 'Display', 'Displays') . ': ' . '' . implode(', ', $displays) . '';
}
/**
* Returns basic administrative information about a view.
*/
function theme_views_ui_view_info($variables) {
$output = '';
$output .= '
' . $variables['title'] . "
\n";
$output .= '
' . $variables['displays'] . "
\n";
return $output;
}
/**
* Page to delete a view.
*/
function views_ui_break_lock_confirm($form, &$form_state, ViewUI $view) {
$form_state['view'] = &$view;
$form = array();
if (empty($view->locked)) {
$form['message']['#markup'] = t('There is no lock on view %name to break.', array('%name' => $view->storage->name));
return $form;
}
$cancel = drupal_container()->get('request')->query->get('cancel');
if (empty($cancel)) {
$cancel = 'admin/structure/views/view/' . $view->storage->name . '/edit';
}
$account = user_load($view->locked->ownerID);
$form = confirm_form($form,
t('Are you sure you want to break the lock on view %name?',
array('%name' => $view->storage->name)),
$cancel,
t('By breaking this lock, any unsaved changes made by !user will be lost!', array('!user' => theme('username', array('account' => $account)))),
t('Break lock'),
t('Cancel'));
$form['actions']['submit']['#submit'][] = array($view, 'submitBreakLock');
return $form;
}
/**
* Page callback for the Edit View page.
*/
function views_ui_edit_page(ViewUI $view, $display_id = NULL) {
$display_id = $view->getDisplayEditPage($display_id);
if (!in_array($display_id, array(MENU_ACCESS_DENIED, MENU_NOT_FOUND))) {
$build = array();
$form_state['build_info']['args'] = array($display_id);
$form_state['build_info']['callback'] = array($view, 'editForm');
$build['edit_form'] = drupal_build_form('views_ui_edit_form', $form_state);
$build['preview'] = views_ui_build_preview($view, $display_id, FALSE);
}
else {
$build = $display_id;
}
return $build;
}
function views_ui_build_preview(ViewUI $view, $display_id, $render = TRUE) {
$build = array(
'#theme_wrappers' => array('container'),
'#attributes' => array('id' => 'views-preview-wrapper', 'class' => 'views-admin clearfix'),
);
$form_state['build_info']['args'] = array($display_id);
$form_state['build_info']['callback'] = array($view, 'buildPreviewForm');
$build['controls'] = drupal_build_form('views_ui_preview_form', $form_state);
$args = array();
if (!empty($form_state['values']['view_args'])) {
$args = explode('/', $form_state['values']['view_args']);
}
$build['preview'] = array(
'#theme_wrappers' => array('container'),
'#attributes' => array('id' => 'views-live-preview'),
'#markup' => $render ? views_ui_preview($view->cloneView(FALSE, TRUE), $display_id, $args) : '',
);
return $build;
}
function template_preprocess_views_ui_display_tab_setting(&$variables) {
static $zebra = 0;
$variables['zebra'] = ($zebra % 2 === 0 ? 'odd' : 'even');
$zebra++;
// Put the main link to the left side
array_unshift($variables['settings_links'], $variables['link']);
$variables['settings_links'] = implode(' | ', $variables['settings_links']);
if (!empty($variables['defaulted'])) {
$variables['attributes']['class'][] = 'defaulted';
}
if (!empty($variables['overridden'])) {
$variables['attributes']['class'][] = 'overridden';
$variables['attributes_array']['title'][] = t('Overridden');
}
// Append a colon to the description, if requested.
if ($variables['description'] && $variables['description_separator']) {
$variables['description'] .= t(':');
}
}
function template_preprocess_views_ui_display_tab_bucket(&$variables) {
$element = $variables['element'];
if (!empty($element['#name'])) {
$variables['attributes']['class'][] = drupal_html_class($element['#name']);
}
if (!empty($element['#overridden'])) {
$variables['attributes']['class'][] = 'overridden';
$variables['attributes_array']['title'][] = t('Overridden');
}
$variables['content'] = $element['#children'];
$variables['title'] = $element['#title'];
$variables['actions'] = !empty($element['#actions']) ? $element['#actions'] : '';
}
function template_preprocess_views_ui_display_tab_column(&$variables) {
$element = $variables['element'];
$variables['content'] = $element['#children'];
$variables['column'] = $element['#column'];
}
/**
* Move form elements into fieldsets for presentation purposes.
*
* Many views forms use #tree = TRUE to keep their values in a hierarchy for
* easier storage. Moving the form elements into fieldsets during form building
* would break up that hierarchy. Therefore, we wait until the pre_render stage,
* where any changes we make affect presentation only and aren't reflected in
* $form_state['values'].
*/
function views_ui_pre_render_add_fieldset_markup($form) {
foreach (element_children($form) as $key) {
$element = $form[$key];
// In our form builder functions, we added an arbitrary #fieldset property
// to any element that belongs in a fieldset. If this form element has that
// property, move it into its fieldset.
if (isset($element['#fieldset']) && isset($form[$element['#fieldset']])) {
$form[$element['#fieldset']][$key] = $element;
// Remove the original element this duplicates.
unset($form[$key]);
}
}
return $form;
}
/**
* Flattens the structure of an element containing the #flatten property.
*
* If a form element has #flatten = TRUE, then all of it's children
* get moved to the same level as the element itself.
* So $form['to_be_flattened'][$key] becomes $form[$key], and
* $form['to_be_flattened'] gets unset.
*/
function views_ui_pre_render_flatten_data($form) {
foreach (element_children($form) as $key) {
$element = $form[$key];
if (!empty($element['#flatten'])) {
foreach (element_children($element) as $child_key) {
$form[$child_key] = $form[$key][$child_key];
}
// All done, remove the now-empty parent.
unset($form[$key]);
}
}
return $form;
}
/**
* Moves argument options into their place.
*
* When configuring the default argument behavior, almost each of the radio
* buttons has its own fieldset shown bellow it when the radio button is
* clicked. That fieldset is created through a custom form process callback.
* Each element that has #argument_option defined and pointing to a default
* behavior gets moved to the appropriate fieldset.
* So if #argument_option is specified as 'default', the element is moved
* to the 'default_options' fieldset.
*/
function views_ui_pre_render_move_argument_options($form) {
foreach (element_children($form) as $key) {
$element = $form[$key];
if (!empty($element['#argument_option'])) {
$container_name = $element['#argument_option'] . '_options';
if (isset($form['no_argument']['default_action'][$container_name])) {
$form['no_argument']['default_action'][$container_name][$key] = $element;
}
// Remove the original element this duplicates.
unset($form[$key]);
}
}
return $form;
}
/**
* Validate that a view is complete and whole.
*/
function views_ui_edit_view_form_validate($form, &$form_state) {
// Do not validate cancel or delete or revert.
if (empty($form_state['clicked_button']['#value']) || $form_state['clicked_button']['#value'] != t('Save')) {
return;
}
$errors = $form_state['view']->validate();
if ($errors !== TRUE) {
foreach ($errors as $error) {
form_set_error('', $error);
}
}
}
/**
* Submit handler for the edit view form.
*/
function views_ui_edit_view_form_submit($form, &$form_state) {
// Go through and remove displayed scheduled for removal.
foreach ($form_state['view']->storage->display as $id => $display) {
if (!empty($display['deleted'])) {
unset($form_state['view']->displayHandlers[$id]);
unset($form_state['view']->storage->display[$id]);
}
}
// Rename display ids if needed.
foreach ($form_state['view']->displayHandlers as $id => $display) {
if (!empty($display->display['new_id'])) {
$new_id = $display->display['new_id'];
$form_state['view']->displayHandlers[$new_id] = $form_state['view']->displayHandlers[$id];
$form_state['view']->displayHandlers[$new_id]->display['id'] = $new_id;
$form_state['view']->storage->display[$new_id] = $form_state['view']->storage->display[$id];
unset($form_state['view']->storage->display[$id]);
// Redirect the user to the renamed display to be sure that the page itself exists and doesn't throw errors.
$form_state['redirect'] = 'admin/structure/views/view/' . $form_state['view']->storage->name . '/edit/' . $new_id;
}
}
// Direct the user to the right url, if the path of the display has changed.
$query = drupal_container()->get('request')->query;
// @todo: Revisit this when http://drupal.org/node/1668866 is in.
$destination = $query->get('destination');
if (!empty($destination)) {
// Find out the first display which has a changed path and redirect to this url.
$old_view = views_get_view($form_state['view']->storage->name);
foreach ($old_view->displayHandlers as $id => $display) {
// Only check for displays with a path.
if (!isset($display->display['display_options']['path'])) {
continue;
}
$old_path = $display->display['display_options']['path'];
if (($display->display['display_plugin'] == 'page') && ($old_path == $destination) && ($old_path != $form_state['view']->display[$id]->display['display_options']['path'])) {
$destination = $form_state['view']->displayHandlers[$id]->display['display_options']['path'];
$query->remove('destination');
}
}
$form_state['redirect'] = $destination;
}
$form_state['view']->save();
drupal_set_message(t('The view %name has been saved.', array('%name' => $form_state['view']->storage->getHumanName())));
// Remove this view from cache so we can edit it properly.
views_temp_store()->delete($form_state['view']->storage->name);
}
/**
* Submit handler for the edit view form.
*/
function views_ui_edit_view_form_cancel($form, &$form_state) {
// Remove this view from cache so edits will be lost.
views_temp_store()->delete($form_state['view']->storage->name);
if (empty($form['view']->vid)) {
// I seem to have to drupal_goto here because I can't get fapi to
// honor the redirect target. Not sure what I screwed up here.
drupal_goto('admin/structure/views');
}
}
/**
* Add a