• Main Page
  • Related Pages
  • Modules
  • Classes
  • Files
  • File List
  • File Members

includes/admin.inc

Go to the documentation of this file.
00001 <?php
00015 function views_ui_get_admin_css() {
00016   $module_path = drupal_get_path('module', 'views_ui');
00017   $list = array();
00018   $list[$module_path . '/css/views-admin.css'] = array();
00019 
00020   $list[$module_path . '/css/ie/views-admin.ie7.css'] = array(
00021     'browsers' => array(
00022       'IE' => 'lte IE 7',
00023       '!IE' => FALSE
00024     ),
00025     'preprocess' => FALSE,
00026   );
00027 
00028   $list[$module_path . '/css/views-admin.theme.css'] = array();
00029 
00030   // Add in any theme specific CSS files we have
00031   $themes = list_themes();
00032   $theme_key = $GLOBALS['theme'];
00033   while ($theme_key) {
00034     // Try to find the admin css file for non-core themes.
00035     if (!in_array($theme_key, array('garland', 'seven', 'bartik'))) {
00036       $theme_path = drupal_get_path('theme', $theme_key);
00037       // First search in the css directory, then in the root folder of the theme.
00038       if (file_exists($theme_path . "/css/views-admin.$theme_key.css")) {
00039         $list[$theme_path . "/css/views-admin.$theme_key.css"] = array(
00040           'group' => CSS_THEME,
00041         );
00042       }
00043       else if (file_exists($theme_path . "/views-admin.$theme_key.css")) {
00044         $list[$theme_path . "/views-admin.$theme_key.css"] = array(
00045           'group' => CSS_THEME,
00046         );
00047       }
00048     }
00049     else {
00050       $list[$module_path . "/css/views-admin.$theme_key.css"] = array(
00051         'group' => CSS_THEME,
00052       );
00053     }
00054     $theme_key = isset($themes[$theme_key]->base_theme) ? $themes[$theme_key]->base_theme : '';
00055   }
00056   // Views contains style overrides for the following modules
00057   $module_list = array('contextual', 'advanced_help', 'ctools');
00058   foreach ($module_list as $module) {
00059     if (module_exists($module)) {
00060       $list[$module_path . '/css/views-admin.' . $module . '.css'] = array();
00061     }
00062   }
00063 
00064 
00065   return $list;
00066 }
00067 
00071 function views_ui_add_admin_css() {
00072   foreach (views_ui_get_admin_css() as $file => $options) {
00073     drupal_add_css($file, $options);
00074   }
00075 }
00076 
00084 function views_ui_check_advanced_help() {
00085   if (!variable_get('views_ui_show_advanced_help_warning', TRUE)) {
00086     return;
00087   }
00088 
00089   if (!module_exists('advanced_help')) {
00090     $filename = db_query_range("SELECT filename FROM {system} WHERE type = 'module' AND name = 'advanced_help'", 0, 1)
00091       ->fetchField();
00092     if ($filename && file_exists($filename)) {
00093       drupal_set_message(t('If you <a href="@modules">enable the advanced help module</a>, Views will provide more and better help. <a href="@hide">Hide this message.</a>', array('@modules' => url('admin/modules'),'@hide' => url('admin/structure/views/settings'))));
00094     }
00095     else {
00096       drupal_set_message(t('If you install the advanced help module from !href, Views will provide more and better help. <a href="@hide">Hide this message.</a>', array('!href' => l('http://drupal.org/project/advanced_help', 'http://drupal.org/project/advanced_help'), '@hide' => url('admin/structure/views/settings'))));
00097     }
00098   }
00099 }
00100 
00104 function views_ui_preview($view, $display_id, $args = array()) {
00105   // When this function is invoked as a page callback, each Views argument is
00106   // passed separately.
00107   if (!is_array($args)) {
00108     $args = array_slice(func_get_args(), 2);
00109   }
00110 
00111   // Save $_GET['q'] so it can be restored before returning from this function.
00112   $q = $_GET['q'];
00113 
00114   // Determine where the query and performance statistics should be output.
00115   $show_query = variable_get('views_ui_show_sql_query', FALSE);
00116   $show_info = variable_get('views_ui_show_preview_information', FALSE);
00117   $show_location = variable_get('views_ui_show_sql_query_where', 'above');
00118 
00119   $show_stats = variable_get('views_ui_show_performance_statistics', FALSE);
00120   if ($show_stats) {
00121     $show_stats = variable_get('views_ui_show_sql_query_where', 'above');
00122   }
00123 
00124   $combined = $show_query && $show_stats;
00125 
00126   $rows = array('query' => array(), 'statistics' => array());
00127   $output = '';
00128 
00129   $errors = $view->validate();
00130   if ($errors === TRUE) {
00131     $view->ajax = TRUE;
00132     $view->live_preview = TRUE;
00133     $view->views_ui_context = TRUE;
00134 
00135     // AJAX happens via $_POST but everything expects exposed data to
00136     // be in GET. Copy stuff but remove ajax-framework specific keys.
00137     // If we're clicking on links in a preview, though, we could actually
00138     // still have some in $_GET, so we use $_REQUEST to ensure we get it all.
00139     $exposed_input = $_REQUEST;
00140     foreach (array('view_name', 'view_display_id', 'view_args', 'view_path', 'view_dom_id', 'pager_element', 'view_base_path', 'ajax_html_ids', 'ajax_page_state', 'form_id', 'form_build_id', 'form_token') as $key) {
00141       if (isset($exposed_input[$key])) {
00142         unset($exposed_input[$key]);
00143       }
00144     }
00145 
00146     $view->set_exposed_input($exposed_input);
00147 
00148 
00149     if (!$view->set_display($display_id)) {
00150       return t('Invalid display id @display', array('@display' => $display_id));
00151     }
00152 
00153     $view->set_arguments($args);
00154 
00155     // Store the current view URL for later use:
00156     if ($view->display_handler->get_option('path')) {
00157       $path = $view->get_url();
00158     }
00159 
00160     // Make view links come back to preview.
00161     $view->override_path = 'admin/structure/views/nojs/preview/' . $view->name . '/' . $display_id;
00162 
00163     // Also override $_GET['q'] so we get the pager.
00164     $original_path = current_path();
00165     $_GET['q'] = $view->override_path;
00166     if ($args) {
00167       $_GET['q'] .= '/' . implode('/', $args);
00168     }
00169 
00170     // Suppress contextual links of entities within the result set during a
00171     // Preview.
00172     // @todo We'll want to add contextual links specific to editing the View, so
00173     //   the suppression may need to be moved deeper into the Preview pipeline.
00174     views_ui_contextual_links_suppress_push();
00175     $preview = $view->preview($display_id, $args);
00176     views_ui_contextual_links_suppress_pop();
00177 
00178     // Reset variables.
00179     unset($view->override_path);
00180     $_GET['q'] = $original_path;
00181 
00182     // Prepare the query information and statistics to show either above or
00183     // below the view preview.
00184     if ($show_info || $show_query || $show_stats) {
00185       // Get information from the preview for display.
00186       if (!empty($view->build_info['query'])) {
00187         if ($show_query) {
00188           $query = $view->build_info['query'];
00189           // Only the sql default class has a method getArguments.
00190           $quoted = array();
00191 
00192           if (get_class($view->query) == 'views_plugin_query_default') {
00193             $quoted = $query->getArguments();
00194             $connection = Database::getConnection();
00195             foreach ($quoted as $key => $val) {
00196               if (is_array($val)) {
00197                 $quoted[$key] = implode(', ', array_map(array($connection, 'quote'), $val));
00198               }
00199               else {
00200                 $quoted[$key] = $connection->quote($val);
00201               }
00202             }
00203           }
00204           $rows['query'][] = array('<strong>' . t('Query') . '</strong>', '<pre>' . check_plain(strtr($query, $quoted)) . '</pre>');
00205           if (!empty($view->additional_queries)) {
00206             $queries = '<strong>' . t('These queries were run during view rendering:') . '</strong>';
00207             foreach ($view->additional_queries as $query) {
00208               if ($queries) {
00209                 $queries .= "\n";
00210               }
00211               $queries .= t('[@time ms]', array('@time' => intval($query[1] * 100000) / 100)) . ' ' . $query[0];
00212             }
00213 
00214             $rows['query'][] = array('<strong>' . t('Other queries') . '</strong>', '<pre>' . $queries . '</pre>');
00215           }
00216         }
00217         if ($show_info) {
00218           $rows['query'][] = array('<strong>' . t('Title') . '</strong>', filter_xss_admin($view->get_title()));
00219           if (isset($path)) {
00220             $path = l($path, $path);
00221           }
00222           else {
00223             $path = t('This display has no path.');
00224           }
00225           $rows['query'][] = array('<strong>' . t('Path') . '</strong>', $path);
00226         }
00227 
00228         if ($show_stats) {
00229           $rows['statistics'][] = array('<strong>' . t('Query build time') . '</strong>', t('@time ms', array('@time' => intval($view->build_time * 100000) / 100)));
00230           $rows['statistics'][] = array('<strong>' . t('Query execute time') . '</strong>', t('@time ms', array('@time' => intval($view->execute_time * 100000) / 100)));
00231           $rows['statistics'][] = array('<strong>' . t('View render time') . '</strong>', t('@time ms', array('@time' => intval($view->render_time * 100000) / 100)));
00232 
00233         }
00234         drupal_alter('views_preview_info', $rows, $view);
00235       }
00236       else {
00237         // No query was run. Display that information in place of either the
00238         // query or the performance statistics, whichever comes first.
00239         if ($combined || ($show_location === 'above')) {
00240           $rows['query'] = array(array('<strong>' . t('Query') . '</strong>', t('No query was run')));
00241         }
00242         else {
00243           $rows['statistics'] = array(array('<strong>' . t('Query') . '</strong>', t('No query was run')));
00244         }
00245       }
00246     }
00247   }
00248   else {
00249     foreach ($errors as $error) {
00250       drupal_set_message($error, 'error');
00251     }
00252     $preview = t('Unable to preview due to validation errors.');
00253   }
00254 
00255   // Assemble the preview, the query info, and the query statistics in the
00256   // requested order.
00257   if ($show_location === 'above') {
00258     if ($combined) {
00259       $output .= '<div class="views-query-info">' . theme('table', array('rows' => array_merge($rows['query'], $rows['statistics']))) . '</div>';
00260     }
00261     else {
00262       $output .= '<div class="views-query-info">' . theme('table', array('rows' => $rows['query'])) . '</div>';
00263     }
00264   }
00265   elseif ($show_stats === 'above') {
00266     $output .= '<div class="views-query-info">' . theme('table', array('rows' => $rows['statistics'])) . '</div>';
00267   }
00268 
00269   $output .= $preview;
00270 
00271   if ($show_location === 'below') {
00272     if ($combined) {
00273       $output .= '<div class="views-query-info">' . theme('table', array('rows' => array_merge($rows['query'], $rows['statistics']))) . '</div>';
00274     }
00275     else {
00276       $output .= '<div class="views-query-info">' . theme('table', array('rows' => $rows['query'])) . '</div>';
00277     }
00278   }
00279   elseif ($show_stats === 'below') {
00280     $output .= '<div class="views-query-info">' . theme('table', array('rows' => $rows['statistics'])) . '</div>';
00281   }
00282 
00283   $_GET['q'] = $q;
00284   return $output;
00285 }
00286 
00290 function views_ui_add_page() {
00291   views_ui_add_admin_css();
00292   drupal_set_title(t('Add new view'));
00293   return drupal_get_form('views_ui_add_form');
00294 }
00295 
00299 function views_ui_add_form($form, &$form_state) {
00300   ctools_include('dependent');
00301   $form['#attached']['js'][] = drupal_get_path('module', 'views_ui') . '/js/views-admin.js';
00302   $form['#attributes']['class'] = array('views-admin');
00303 
00304   $form['human_name'] = array(
00305     '#type' => 'textfield',
00306     '#title' => t('View name'),
00307     '#required' => TRUE,
00308     '#size' => 32,
00309     '#default_value' => !empty($form_state['view']) ? $form_state['view']->human_name : '',
00310     '#maxlength' => 255,
00311   );
00312   $form['name'] = array(
00313     '#type' => 'machine_name',
00314     '#maxlength' => 128,
00315     '#machine_name' => array(
00316       'exists' => 'views_get_view',
00317       'source' => array('human_name'),
00318     ),
00319     '#description' => t('A unique machine-readable name for this View. It must only contain lowercase letters, numbers, and underscores.'),
00320   );
00321 
00322   $form['description_enable'] = array(
00323     '#type' => 'checkbox',
00324     '#title' => t('Description'),
00325   );
00326   $form['description'] = array(
00327     '#type' => 'textfield',
00328     '#title' => t('Provide description'),
00329     '#title_display' => 'invisible',
00330     '#size' => 64,
00331     '#default_value' => !empty($form_state['view']) ? $form_state['view']->description : '',
00332     '#dependency' => array(
00333       'edit-description-enable' => array(1),
00334     ),
00335   );
00336 
00337   // Create a wrapper for the entire dynamic portion of the form. Everything
00338   // that can be updated by AJAX goes somewhere inside here. For example, this
00339   // is needed by "Show" dropdown (below); it changes the base table of the
00340   // view and therefore potentially requires all options on the form to be
00341   // dynamically updated.
00342   $form['displays'] = array();
00343 
00344   // Create the part of the form that allows the user to select the basic
00345   // properties of what the view will display.
00346   $form['displays']['show'] = array(
00347     '#type' => 'fieldset',
00348     '#tree' => TRUE,
00349     '#attributes' => array('class' => array('container-inline')),
00350   );
00351 
00352   // Create the "Show" dropdown, which allows the base table of the view to be
00353   // selected.
00354   $wizard_plugins = views_ui_get_wizards();
00355   $options = array();
00356   foreach ($wizard_plugins as $key => $wizard) {
00357     $options[$key] = $wizard['title'];
00358   }
00359   $form['displays']['show']['wizard_key'] = array(
00360     '#type' => 'select',
00361     '#title' => t('Show'),
00362     '#options' => $options,
00363   );
00364   $show_form = &$form['displays']['show'];
00365   $show_form['wizard_key']['#default_value'] = views_ui_get_selected($form_state, array('show', 'wizard_key'), 'node', $show_form['wizard_key']);
00366   // Changing this dropdown updates the entire content of $form['displays'] via
00367   // AJAX.
00368   views_ui_add_ajax_trigger($show_form, 'wizard_key', array('displays'));
00369 
00370   // Build the rest of the form based on the currently selected wizard plugin.
00371   $wizard_key = $show_form['wizard_key']['#default_value'];
00372   $get_instance = $wizard_plugins[$wizard_key]['get_instance'];
00373   $wizard_instance = $get_instance($wizard_plugins[$wizard_key]);
00374   $form = $wizard_instance->build_form($form, $form_state);
00375 
00376   $form['save'] = array(
00377     '#type' => 'submit',
00378     '#value' => t('Save & exit'),
00379     '#validate' => array('views_ui_wizard_form_validate'),
00380     '#submit' => array('views_ui_add_form_save_submit'),
00381   );
00382   $form['continue'] = array(
00383     '#type' => 'submit',
00384     '#value' => t('Continue & edit'),
00385     '#validate' => array('views_ui_wizard_form_validate'),
00386     '#submit' => array('views_ui_add_form_store_edit_submit'),
00387     '#process' => array_merge(array('views_ui_default_button'), element_info_property('submit', '#process', array())),
00388   );
00389   $form['cancel'] = array(
00390     '#type' => 'submit',
00391     '#value' => t('Cancel'),
00392     '#submit' => array('views_ui_add_form_cancel_submit'),
00393     '#limit_validation_errors' => array(),
00394   );
00395 
00396   return $form;
00397 }
00398 
00408 function views_element_validate_integer($element, &$form_state) {
00409   $value = $element['#value'];
00410   if ($value !== '' && (!is_numeric($value) || intval($value) != $value)) {
00411     form_error($element, t('%name must be a positive integer.', array('%name' => $element['#title'])));
00412   }
00413 }
00414 
00458 function views_ui_get_selected($form_state, $parents, $default_value, $element) {
00459   // For now, don't trust this to work on anything but a #select element.
00460   if (!isset($element['#type']) || $element['#type'] != 'select' || !isset($element['#options'])) {
00461     return $default_value;
00462   }
00463 
00464   // If there is a user-submitted value for this element that matches one of
00465   // the currently available options attached to it, use that. We need to check
00466   // $form_state['input'] rather than $form_state['values'] here because the
00467   // triggering element often has the #limit_validation_errors property set to
00468   // prevent unwanted errors elsewhere on the form. This means that the
00469   // $form_state['values'] array won't be complete. We could make it complete
00470   // by adding each required part of the form to the #limit_validation_errors
00471   // property individually as the form is being built, but this is difficult to
00472   // do for a highly dynamic and extensible form. This method is much simpler.
00473   if (!empty($form_state['input'])) {
00474     $key_exists = NULL;
00475     $submitted = drupal_array_get_nested_value($form_state['input'], $parents, $key_exists);
00476     // Check that the user-submitted value is one of the allowed options before
00477     // returning it. This is not a substitute for actual form validation;
00478     // rather it is necessary because, for example, the same select element
00479     // might have #options A, B, and C under one set of conditions but #options
00480     // D, E, F under a different set of conditions. So the form submission
00481     // might have occurred with option A selected, but when the form is rebuilt
00482     // option A is no longer one of the choices. In that case, we don't want to
00483     // use the value that was submitted anymore but rather fall back to the
00484     // default value.
00485     if ($key_exists && in_array($submitted, array_keys($element['#options']))) {
00486       return $submitted;
00487     }
00488   }
00489 
00490   // Fall back on returning the default value if nothing was returned above.
00491   return $default_value;
00492 }
00493 
00525 function views_ui_add_ajax_trigger(&$wrapping_element, $trigger_key, $refresh_parents) {
00526   $seen_ids = &drupal_static(__FUNCTION__ . ':seen_ids', array());
00527   $seen_buttons = &drupal_static(__FUNCTION__ . ':seen_buttons', array());
00528 
00529   // Add the AJAX behavior to the triggering element.
00530   $triggering_element = &$wrapping_element[$trigger_key];
00531   $triggering_element['#ajax']['callback'] = 'views_ui_ajax_update_form';
00532   // We do not use drupal_html_id() to get an ID for the AJAX wrapper, because
00533   // it remembers IDs across AJAX requests (and won't reuse them), but in our
00534   // case we need to use the same ID from request to request so that the
00535   // wrapper can be recognized by the AJAX system and its content can be
00536   // dynamically updated. So instead, we will keep track of duplicate IDs
00537   // (within a single request) on our own, later in this function.
00538   $triggering_element['#ajax']['wrapper'] = 'edit-view-' . implode('-', $refresh_parents) . '-wrapper';
00539 
00540   // Add a submit button for users who do not have JavaScript enabled. It
00541   // should be displayed next to the triggering element on the form.
00542   $button_key = $trigger_key . '_trigger_update';
00543   $wrapping_element[$button_key] = array(
00544     '#type' => 'submit',
00545     // Hide this button when JavaScript is enabled.
00546     '#attributes' => array('class' => array('js-hide')),
00547     '#submit' => array('views_ui_nojs_submit'),
00548     // Add a process function to limit this button's validation errors to the
00549     // triggering element only. We have to do this in #process since until the
00550     // form API has added the #parents property to the triggering element for
00551     // us, we don't have any (easy) way to find out where its submitted values
00552     // will eventually appear in $form_state['values'].
00553     '#process' => array_merge(array('views_ui_add_limited_validation'), element_info_property('submit', '#process', array())),
00554     // Add an after-build function that inserts a wrapper around the region of
00555     // the form that needs to be refreshed by AJAX (so that the AJAX system can
00556     // detect and dynamically update it). This is done in #after_build because
00557     // it's a convenient place where we have automatic access to the complete
00558     // form array, but also to minimize the chance that the HTML we add will
00559     // get clobbered by code that runs after we have added it.
00560     '#after_build' => array_merge(element_info_property('submit', '#after_build', array()), array('views_ui_add_ajax_wrapper')),
00561   );
00562   // Copy #weight and #access from the triggering element to the button, so
00563   // that the two elements will be displayed together.
00564   foreach (array('#weight', '#access') as $property) {
00565     if (isset($triggering_element[$property])) {
00566       $wrapping_element[$button_key][$property] = $triggering_element[$property];
00567     }
00568   }
00569   // For easiest integration with the form API and the testing framework, we
00570   // always give the button a unique #value, rather than playing around with
00571   // #name.
00572   $button_title = !empty($triggering_element['#title']) ? $triggering_element['#title'] : $trigger_key;
00573   if (empty($seen_buttons[$button_title])) {
00574     $wrapping_element[$button_key]['#value'] = t('Update "@title" choice', array(
00575       '@title' => $button_title,
00576     ));
00577     $seen_buttons[$button_title] = 1;
00578   }
00579   else {
00580     $wrapping_element[$button_key]['#value'] = t('Update "@title" choice (@number)', array(
00581       '@title' => $button_title,
00582       '@number' => ++$seen_buttons[$button_title],
00583    ));
00584   }
00585 
00586   // Attach custom data to the triggering element and submit button, so we can
00587   // use it in both the process function and AJAX callback.
00588   $ajax_data = array(
00589     'wrapper' => $triggering_element['#ajax']['wrapper'],
00590     'trigger_key' => $trigger_key,
00591     'refresh_parents' => $refresh_parents,
00592     // Keep track of duplicate wrappers so we don't add the same wrapper to the
00593     // page more than once.
00594     'duplicate_wrapper' => !empty($seen_ids[$triggering_element['#ajax']['wrapper']]),
00595   );
00596   $seen_ids[$triggering_element['#ajax']['wrapper']] = TRUE;
00597   $triggering_element['#views_ui_ajax_data'] = $ajax_data;
00598   $wrapping_element[$button_key]['#views_ui_ajax_data'] = $ajax_data;
00599 }
00600 
00604 function views_ui_add_limited_validation($element, &$form_state) {
00605   // Retrieve the AJAX triggering element so we can determine its parents. (We
00606   // know it's at the same level of the complete form array as the submit
00607   // button, so all we have to do to find it is swap out the submit button's
00608   // last array parent.)
00609   $array_parents = $element['#array_parents'];
00610   array_pop($array_parents);
00611   $array_parents[] = $element['#views_ui_ajax_data']['trigger_key'];
00612   $ajax_triggering_element = drupal_array_get_nested_value($form_state['complete form'], $array_parents);
00613 
00614   // Limit this button's validation to the AJAX triggering element, so it can
00615   // update the form for that change without requiring that the rest of the
00616   // form be filled out properly yet.
00617   $element['#limit_validation_errors'] = array($ajax_triggering_element['#parents']);
00618 
00619   // If we are in the process of a form submission and this is the button that
00620   // was clicked, the form API workflow in form_builder() will have already
00621   // copied it to $form_state['triggering_element'] before our #process
00622   // function is run. So we need to make the same modifications in $form_state
00623   // as we did to the element itself, to ensure that #limit_validation_errors
00624   // will actually be set in the correct place.
00625   if (!empty($form_state['triggering_element'])) {
00626     $clicked_button = &$form_state['triggering_element'];
00627     if ($clicked_button['#name'] == $element['#name'] && $clicked_button['#value'] == $element['#value']) {
00628       $clicked_button['#limit_validation_errors'] = $element['#limit_validation_errors'];
00629     }
00630   }
00631 
00632   return $element;
00633 }
00634 
00642 function views_ui_add_ajax_wrapper($element, &$form_state) {
00643   // Don't add the wrapper <div> if the same one was already inserted on this
00644   // form.
00645   if (empty($element['#views_ui_ajax_data']['duplicate_wrapper'])) {
00646     // Find the region of the complete form that needs to be refreshed by AJAX.
00647     // This was earlier stored in a property on the element.
00648     $complete_form = &$form_state['complete form'];
00649     $refresh_parents = $element['#views_ui_ajax_data']['refresh_parents'];
00650     $refresh_element = drupal_array_get_nested_value($complete_form, $refresh_parents);
00651 
00652     // The HTML ID that AJAX expects was also stored in a property on the
00653     // element, so use that information to insert the wrapper <div> here.
00654     $id = $element['#views_ui_ajax_data']['wrapper'];
00655     $refresh_element += array(
00656       '#prefix' => '',
00657       '#suffix' => '',
00658     );
00659     $refresh_element['#prefix'] = '<div id="' . $id . '" class="views-ui-ajax-wrapper">' . $refresh_element['#prefix'];
00660     $refresh_element['#suffix'] .= '</div>';
00661 
00662     // Copy the element that needs to be refreshed back into the form, with our
00663     // modifications to it.
00664     drupal_array_set_nested_value($complete_form, $refresh_parents, $refresh_element);
00665   }
00666 
00667   return $element;
00668 }
00669 
00676 function views_ui_ajax_update_form($form, $form_state) {
00677   // The region that needs to be updated was stored in a property of the
00678   // triggering element by views_ui_add_ajax_trigger(), so all we have to do is
00679   // retrieve that here.
00680   return drupal_array_get_nested_value($form, $form_state['triggering_element']['#views_ui_ajax_data']['refresh_parents']);
00681 }
00682 
00686 function views_ui_nojs_submit($form, &$form_state) {
00687   $form_state['rebuild'] = TRUE;
00688 }
00689 
00693 function views_ui_wizard_form_validate($form, &$form_state) {
00694   $wizard = views_ui_get_wizard($form_state['values']['show']['wizard_key']);
00695   $form_state['wizard'] = $wizard;
00696   $get_instance = $wizard['get_instance'];
00697   $form_state['wizard_instance'] = $get_instance($wizard);
00698   $errors = $form_state['wizard_instance']->validate($form, $form_state);
00699   foreach ($errors as $name => $message) {
00700     form_set_error($name, $message);
00701   }
00702 }
00703 
00707 function views_ui_add_form_save_submit($form, &$form_state) {
00708   try {
00709     $view = $form_state['wizard_instance']->create_view($form, $form_state);
00710   }
00711   catch (ViewsWizardException $e) {
00712     drupal_set_message($e->getMessage(), 'error');
00713     $form_state['redirect'] = 'admin/structure/views';
00714   }
00715   $view->save();
00716   menu_rebuild();
00717   cache_clear_all('*', 'cache_views', TRUE);
00718   cache_clear_all();
00719   $form_state['redirect'] = 'admin/structure/views';
00720   if (!empty($view->display['page'])) {
00721     $display = $view->display['page'];
00722     if ($display->handler->has_path()) {
00723       $one_path = $display->handler->get_option('path');
00724       if (strpos($one_path, '%') === FALSE) {
00725         $form_state['redirect'] = $one_path;  // PATH TO THE VIEW IF IT HAS ONE
00726         return;
00727       }
00728     }
00729   }
00730   drupal_set_message(t('Your view was saved. You may edit it from the list below.'));
00731 }
00732 
00736 function views_ui_add_form_store_edit_submit($form, &$form_state) {
00737   try {
00738     $view = $form_state['wizard_instance']->create_view($form, $form_state);
00739   }
00740   catch (ViewsWizardException $e) {
00741     drupal_set_message($e->getMessage(), 'error');
00742     $form_state['redirect'] = 'admin/structure/views';
00743   }
00744   // Just cache it temporarily to edit it.
00745   views_ui_cache_set($view);
00746 
00747   // If there is a destination query, ensure we still redirect the user to the
00748   // edit view page, and then redirect the user to the destination.
00749   $destination = array();
00750   if (isset($_GET['destination'])) {
00751     $destination = drupal_get_destination();
00752     unset($_GET['destination']);
00753   }
00754   $form_state['redirect'] = array('admin/structure/views/view/' . $view->name, array('query' => $destination));
00755 }
00756 
00760 function views_ui_add_form_cancel_submit($form, &$form_state) {
00761   $form_state['redirect'] = 'admin/structure/views';
00762 }
00763 
00778 function views_ui_taxonomy_autocomplete_validate($element, &$form_state) {
00779   $value = array();
00780   if ($tags = $element['#value']) {
00781     // Get the machine names of the vocabularies we will search, keyed by the
00782     // vocabulary IDs.
00783     $field = field_info_field($element['#field_name']);
00784     $vocabularies = array();
00785     if (!empty($field['settings']['allowed_values'])) {
00786       foreach ($field['settings']['allowed_values'] as $tree) {
00787         if ($vocabulary = taxonomy_vocabulary_machine_name_load($tree['vocabulary'])) {
00788           $vocabularies[$vocabulary->vid] = $tree['vocabulary'];
00789         }
00790       }
00791     }
00792     // Store the term ID of each (valid) tag that the user typed.
00793     $typed_terms = drupal_explode_tags($tags);
00794     foreach ($typed_terms as $typed_term) {
00795       if ($terms = taxonomy_term_load_multiple(array(), array('name' => trim($typed_term), 'vid' => array_keys($vocabularies)))) {
00796         $term = array_pop($terms);
00797         $value['tids'][] = $term->tid;
00798       }
00799     }
00800     // Store the term IDs along with the name of the vocabulary. Currently
00801     // Views (as well as the Field UI) assumes that there will only be one
00802     // vocabulary, although technically the API allows there to be more than
00803     // one.
00804     if (!empty($value['tids'])) {
00805       $value['tids'] = array_unique($value['tids']);
00806       $value['vocabulary'] = array_pop($vocabularies);
00807     }
00808   }
00809   form_set_value($element, $value, $form_state);
00810 }
00811 
00817 function theme_views_ui_view_info($variables) {
00818   $view = $variables['view'];
00819   $title = $view->get_human_name();
00820 
00821   $displays = _views_ui_get_displays_list($view);
00822   $displays = empty($displays) ? t('None') : format_plural(count($displays), 'Display', 'Displays') . ': ' . '<em>' . implode(', ', $displays) . '</em>';
00823 
00824   switch ($view->type) {
00825     case t('Default'):
00826     default:
00827       $type = t('In code');
00828       break;
00829 
00830     case t('Normal'):
00831       $type = t('In database');
00832       break;
00833 
00834     case t('Overridden'):
00835       $type = t('Database overriding code');
00836   }
00837 
00838   $output = '';
00839   $output .= '<div class="views-ui-view-title">' . $title . "</div>\n";
00840   $output .= '<div class="views-ui-view-displays">' . $displays . "</div>\n";
00841   $output .= '<div class="views-ui-view-storage">' . $type . "</div>\n";
00842   $output .= '<div class="views-ui-view-base">' . t('Type') . ': ' . $variables['base']. "</div>\n";
00843   return $output;
00844 }
00845 
00849 function views_ui_break_lock_confirm($form, &$form_state, $view) {
00850   $form_state['view'] = &$view;
00851   $form = array();
00852 
00853   if (empty($view->locked)) {
00854     $form['#markup'] = t('There is no lock on view %view to break.', array('%name' => $view->name));
00855     return $form;
00856   }
00857 
00858   $cancel = 'admin/structure/views/view/' . $view->name . '/edit';
00859   if (!empty($_REQUEST['cancel'])) {
00860     $cancel = $_REQUEST['cancel'];
00861   }
00862 
00863   $account = user_load($view->locked->uid);
00864   return confirm_form($form,
00865                   t('Are you sure you want to break the lock on view %name?',
00866                   array('%name' => $view->name)),
00867                   $cancel,
00868                   t('By breaking this lock, any unsaved changes made by !user will be lost!', array('!user' => theme('username', array('account' => $account)))),
00869                   t('Break lock'),
00870                   t('Cancel'));
00871 }
00872 
00876 function views_ui_break_lock_confirm_submit(&$form, &$form_state) {
00877   ctools_object_cache_clear_all('view', $form_state['view']->name);
00878   $form_state['redirect'] = 'admin/structure/views/view/' . $form_state['view']->name . '/edit';
00879   drupal_set_message(t('The lock has been broken and you may now edit this view.'));
00880 }
00881 
00887 function views_ui_edit_page_display($view, $display_id) {
00888   // Determine the displays available for editing.
00889   if ($tabs = views_ui_edit_page_display_tabs($view, $display_id)) {
00890     // If a display isn't specified, use the first one.
00891     if (empty($display_id)) {
00892       foreach ($tabs as $id => $tab) {
00893         if (!isset($tab['#access']) || $tab['#access']) {
00894           $display_id = $id;
00895           break;
00896         }
00897       }
00898     }
00899     // If a display is specified, but we don't have access to it, return
00900     // an access denied page.
00901     if ($display_id && (!isset($tabs[$display_id]) || (isset($tabs[$display_id]['#access']) && !$tabs[$display_id]['#access']))) {
00902       return MENU_ACCESS_DENIED;
00903     }
00904 
00905     return $display_id;
00906   }
00907   elseif ($display_id) {
00908     return MENU_ACCESS_DENIED;
00909   }
00910   else {
00911     $display_id = NULL;
00912   }
00913 
00914   return $display_id;
00915 }
00916 
00920 function views_ui_edit_page($view, $display_id = NULL) {
00921   $display_id = views_ui_edit_page_display($view, $display_id);
00922   if (!in_array($display_id, array(MENU_ACCESS_DENIED, MENU_NOT_FOUND))) {
00923     $build = array();
00924     $build['edit_form'] = drupal_get_form('views_ui_edit_form', $view, $display_id);
00925     $build['preview'] = views_ui_build_preview($view, $display_id, FALSE);
00926   }
00927   else {
00928     $build = $display_id;
00929   }
00930 
00931   return $build;
00932 }
00933 
00934 function views_ui_build_preview($view, $display_id, $render = TRUE) {
00935   if (isset($_POST['ajax_html_ids'])) {
00936     unset($_POST['ajax_html_ids']);
00937   }
00938 
00939   $build = array(
00940     '#theme_wrappers' => array('container'),
00941     '#attributes' => array('id' => 'views-preview-wrapper', 'class' => 'views-admin clearfix'),
00942   );
00943 
00944   $form_state = array('build_info' => array('args' => array($view, $display_id)));
00945   $build['controls'] = drupal_build_form('views_ui_preview_form', $form_state);
00946 
00947   $args = array();
00948   if (!empty($form_state['values']['view_args'])) {
00949     $args = explode('/', $form_state['values']['view_args']);
00950   }
00951 
00952   $build['preview'] = array(
00953     '#theme_wrappers' => array('container'),
00954     '#attributes' => array('id' => 'views-live-preview'),
00955     '#markup' => $render ? views_ui_preview($view->clone_view(), $display_id, $args) : '',
00956   );
00957 
00958   return $build;
00959 }
00960 
00970 function views_ui_edit_form($form, &$form_state, $view, $display_id = NULL) {
00971   // Do not allow the form to be cached, because $form_state['view'] can become
00972   // stale between page requests.
00973   // @see views_ui_ajax_get_form() for how this affects #ajax.
00974   // @todo To remove this and allow the form to be cacheable:
00975   //   - Change $form_state['view'] to $form_state['temporary']['view'].
00976   //   - Add a #process function to initialize $form_state['temporary']['view']
00977   //     on cached form submissions.
00978   //   - Update ctools_include() to support cached forms, or else use
00979   //     form_load_include().
00980   $form_state['no_cache'] = TRUE;
00981 
00982   if ($display_id) {
00983     if (!$view->set_display($display_id)) {
00984       $form['#markup'] = t('Invalid display id @display', array('@display' => $display_id));
00985       return $form;
00986     }
00987 
00988     $view->fix_missing_relationships();
00989   }
00990 
00991   ctools_include('dependent');
00992   $form['#attached']['js'][] = ctools_attach_js('dependent');
00993   $form['#attached']['js'][] = ctools_attach_js('collapsible-div');
00994 
00995   $form['#tree'] = TRUE;
00996   // @todo When more functionality is added to this form, cloning here may be
00997   //   too soon. But some of what we do with $view later in this function
00998   //   results in making it unserializable due to PDO limitations.
00999   $form_state['view'] = clone($view);
01000 
01001   $form['#attached']['library'][] = array('system', 'ui.tabs');
01002   $form['#attached']['library'][] = array('system', 'ui.dialog');
01003   $form['#attached']['library'][] = array('system', 'drupal.ajax');
01004   $form['#attached']['library'][] = array('system', 'jquery.form');
01005   // TODO: This should be getting added to the page when an ajax popup calls
01006   // for it, instead of having to add it manually here.
01007   $form['#attached']['js'][] = 'misc/tabledrag.js';
01008 
01009   $form['#attached']['css'] = views_ui_get_admin_css();
01010   $module_path = drupal_get_path('module', 'views_ui');
01011 
01012   $form['#attached']['js'][] = $module_path . '/js/views-admin.js';
01013   $form['#attached']['js'][] = array(
01014     'data' => array('views' => array('ajax' => array(
01015       'id' => '#views-ajax-body',
01016       'title' => '#views-ajax-title',
01017       'popup' => '#views-ajax-popup',
01018       'defaultForm' => views_ui_get_default_ajax_message(),
01019     ))),
01020     'type' => 'setting',
01021   );
01022 
01023   $form += array(
01024     '#prefix' => '',
01025     '#suffix' => '',
01026   );
01027   $form['#prefix'] = $form['#prefix'] . '<div class="views-edit-view views-admin clearfix">';
01028   $form['#suffix'] = '</div>' . $form['#suffix'];
01029 
01030   $form['#attributes']['class'] = array('form-edit');
01031 
01032   if (isset($view->locked) && is_object($view->locked)) {
01033     $form['locked'] = array(
01034       '#theme_wrappers' => array('container'),
01035       '#attributes' => array('class' => array('view-locked', 'messages', 'warning')),
01036       '#markup' => t('This view is being edited by user !user, and is therefore locked from editing by others. This lock is !age old. Click here to <a href="!break">break this lock</a>.', array('!user' => theme('username', array('account' => user_load($view->locked->uid))), '!age' => format_interval(REQUEST_TIME - $view->locked->updated), '!break' => url('admin/structure/views/view/' . $view->name . '/break-lock'))),
01037     );
01038   }
01039   if (isset($view->vid) && $view->vid == 'new') {
01040     $message = t('* All changes are stored temporarily. Click Save to make your changes permanent. Click Cancel to discard the view.');
01041   }
01042   else {
01043     $message = t('* All changes are stored temporarily. Click Save to make your changes permanent. Click Cancel to discard your changes.');
01044   }
01045 
01046   $form['changed'] = array(
01047     '#theme_wrappers' => array('container'),
01048     '#attributes' => array('class' => array('view-changed', 'messages', 'warning')),
01049     '#markup' => $message,
01050   );
01051   if (empty($view->changed)) {
01052     $form['changed']['#attributes']['class'][] = 'js-hide';
01053   }
01054 
01055   $form['help_text'] = array(
01056     '#prefix' => '<div>',
01057     '#suffix' => '</div>',
01058     '#markup' => t('Modify the display(s) of your view below or add new displays.'),
01059   );
01060 
01061   $form['actions'] = array(
01062     '#type' => 'actions',
01063     '#weight' => 0,
01064   );
01065 
01066   if (empty($view->changed)) {
01067     $form['actions']['#attributes'] = array(
01068       'class' => array(
01069         'js-hide',
01070       ),
01071     );
01072   }
01073 
01074   $form['actions']['save'] = array(
01075     '#type' => 'submit',
01076     '#value' => t('Save'),
01077     // Taken from the "old" UI. @TODO: Review and rename.
01078     '#validate' => array('views_ui_edit_view_form_validate'),
01079     '#submit' => array('views_ui_edit_view_form_submit'),
01080   );
01081   $form['actions']['cancel'] = array(
01082     '#type' => 'submit',
01083     '#value' => t('Cancel'),
01084     '#submit' => array('views_ui_edit_view_form_cancel'),
01085   );
01086 
01087   $form['displays'] = array(
01088     '#prefix' => '<h1 class="unit-title clearfix">' . t('Displays') . '</h1>' . "\n" . '<div class="views-displays">',
01089     '#suffix' => '</div>',
01090   );
01091 
01092   $form['displays']['top'] = views_ui_render_display_top($view, $display_id);
01093 
01094   // The rest requires a display to be selected.
01095   if ($display_id) {
01096     $form_state['display_id'] = $display_id;
01097 
01098     // The part of the page where editing will take place.
01099     // This element is the ctools collapsible-div container for the display edit elements.
01100     $form['displays']['settings'] = array(
01101       '#theme_wrappers' => array('container'),
01102       '#attributes' => array(
01103         'class' => array(
01104           'views-display-settings',
01105           'box-margin',
01106           'ctools-collapsible-container',
01107         ),
01108       ),
01109       '#id' => 'edit-display-settings',
01110     );
01111     $display_title = views_ui_get_display_label($view, $display_id, FALSE);
01112     // Add a handle for the ctools collapsible-div. The handle is the title of the display
01113     $form['displays']['settings']['tab_title']['#markup'] = '<h2 id="edit-display-settings-title" class="ctools-collapsible-handle">' . t('@display_title details', array('@display_title' => ucwords($display_title))) . '</h2>';
01114     // Add a text that the display is disabled.
01115     if (!empty($view->display[$display_id]->handler)) {
01116       $enabled = $view->display[$display_id]->handler->get_option('enabled');
01117       if (empty($enabled)) {
01118         $form['displays']['settings']['disabled']['#markup'] = t('This display is disabled.');
01119       }
01120     }
01121     // The ctools collapsible-div content
01122     $form['displays']['settings']['settings_content']= array(
01123       '#theme_wrappers' => array('container'),
01124       '#id' => 'edit-display-settings-content',
01125       '#attributes' => array(
01126         'class' => array(
01127           'ctools-collapsible-content',
01128         ),
01129       ),
01130     );
01131     // Add the edit display content
01132     $form['displays']['settings']['settings_content']['tab_content'] = views_ui_get_display_tab($view, $display_id);
01133     $form['displays']['settings']['settings_content']['tab_content']['#theme_wrappers'] = array('container');
01134     $form['displays']['settings']['settings_content']['tab_content']['#attributes'] = array('class' => array('views-display-tab'));
01135     $form['displays']['settings']['settings_content']['tab_content']['#id'] = 'views-tab-' . $display_id;
01136     // Mark deleted displays as such.
01137     if (!empty($view->display[$display_id]->deleted)) {
01138       $form['displays']['settings']['settings_content']['tab_content']['#attributes']['class'][] = 'views-display-deleted';
01139     }
01140     // Mark disabled displays as such.
01141     if (empty($enabled)) {
01142       $form['displays']['settings']['settings_content']['tab_content']['#attributes']['class'][] = 'views-display-disabled';
01143     }
01144 
01145     // The content of the popup dialog.
01146     $form['ajax-area'] = array(
01147       '#theme_wrappers' => array('container'),
01148       '#id' => 'views-ajax-popup',
01149     );
01150     $form['ajax-area']['ajax-title'] = array(
01151       '#markup' => '<h2 id="views-ajax-title"></h2>',
01152     );
01153     $form['ajax-area']['ajax-body'] = array(
01154       '#theme_wrappers' => array('container'),
01155       '#id' => 'views-ajax-body',
01156       '#markup' => views_ui_get_default_ajax_message(),
01157     );
01158   }
01159 
01160   // If relationships had to be fixed, we want to get that into the cache
01161   // so that edits work properly, and to try to get the user to save it
01162   // so that it's not using weird fixed up relationships.
01163   if (!empty($view->relationships_changed) && empty($_POST)) {
01164     drupal_set_message(t('This view has been automatically updated to fix missing relationships. While this View should continue to work, you should verify that the automatic updates are correct and save this view.'));
01165     views_ui_cache_set($view);
01166   }
01167   return $form;
01168 }
01169 
01173 function views_ui_preview_form($form, &$form_state, $view, $display_id = 'default') {
01174   $form_state['no_cache'] = TRUE;
01175   $form_state['view'] = $view;
01176 
01177   $form['#attributes'] = array('class' => array('clearfix',));
01178 
01179   // Add a checkbox controlling whether or not this display auto-previews.
01180   $form['live_preview'] = array(
01181     '#type' => 'checkbox',
01182     '#id' => 'edit-displays-live-preview',
01183     '#title' => t('Auto preview'),
01184     '#default_value' => variable_get('views_ui_always_live_preview', TRUE),
01185   );
01186 
01187   // Add the arguments textfield
01188   $form['view_args'] = array(
01189     '#type' => 'textfield',
01190     '#title' => t('Preview with contextual filters:'),
01191     '#description' => t('Separate contextual filter values with a "/". For example, %example.', array('%example' => '40/12/10')),
01192     '#id' => 'preview-args',
01193 //      '#attributes' => array('class' => array('ctools-auto-submit')),
01194   );
01195 
01196   // Add the preview button
01197   $form['button'] = array(
01198     '#type' => 'submit',
01199     '#value' => t('Update preview'),
01200     '#attributes' => array('class' => array('arguments-preview', 'ctools-auto-submit-click')),
01201     '#pre_render' => array('ctools_dependent_pre_render'),
01202     '#prefix' => '<div id="preview-submit-wrapper">',
01203     '#suffix' => '</div>',
01204     '#id' => 'preview-submit',
01205     '#submit' => array('views_ui_edit_form_submit_preview'),
01206     '#ajax' => array(
01207       'path' => 'admin/structure/views/view/' . $view->name . '/preview/' . $display_id . '/ajax',
01208       'wrapper' => 'views-preview-wrapper',
01209       'event' => 'click',
01210       'progress' => array('type' => 'throbber'),
01211       'method' => 'replace',
01212     ),
01213     // Make ENTER in arguments textfield (and other controls) submit the form
01214     // as this button, not the Save button.
01215     // @todo This only works for JS users. To make this work for nojs users,
01216     //   we may need to split Preview into a separate form.
01217     '#process' => array_merge(array('views_ui_default_button'), element_info_property('submit', '#process', array())),
01218   );
01219   $form['#action'] = url('admin/structure/views/view/' . $view->name .'/preview/' . $display_id);
01220 
01221   return $form;
01222 }
01223 
01227 function views_ui_render_display_top($view, $display_id) {
01228   $element['#theme_wrappers'] = array('views_container');
01229   $element['#attributes']['class'] = array('views-display-top', 'clearfix');
01230   $element['#attributes']['id'] = array('views-display-top');
01231 
01232   // Extra actions for the display
01233   $element['extra_actions'] = array(
01234     '#theme' => 'links__ctools_dropbutton',
01235     '#attributes' => array(
01236         'id' => 'views-display-extra-actions',
01237         'class' => array(
01238           'horizontal', 'right', 'links', 'actions',
01239         ),
01240       ),
01241     '#links' => array(
01242       'edit-details' => array(
01243         'title' => t('edit view name/description'),
01244         'href' => "admin/structure/views/nojs/edit-details/$view->name",
01245         'attributes' => array('class' => array('views-ajax-link')),
01246       ),
01247       'analyze' => array(
01248         'title' => t('analyze view'),
01249         'href' => "admin/structure/views/nojs/analyze/$view->name/$display_id",
01250         'attributes' => array('class' => array('views-ajax-link')),
01251       ),
01252       'clone' => array(
01253         'title' => t('clone view'),
01254         'href' => "admin/structure/views/view/$view->name/clone",
01255       ),
01256       'export' => array(
01257         'title' => t('export view'),
01258         'href' => "admin/structure/views/view/$view->name/export",
01259       ),
01260       'reorder' => array(
01261         'title' => t('reorder displays'),
01262         'href' => "admin/structure/views/nojs/reorder-displays/$view->name/$display_id",
01263         'attributes' => array('class' => array('views-ajax-link')),
01264       ),
01265     ),
01266   );
01267 
01268   // Let other modules add additional links here.
01269   drupal_alter('views_ui_display_top_links', $element['extra_actions']['#links'], $view, $display_id);
01270 
01271   if (isset($view->type) && $view->type != t('Default')) {
01272     if ($view->type == t('Overridden')) {
01273       $element['extra_actions']['#links']['revert'] = array(
01274         'title' => t('revert view'),
01275         'href' => "admin/structure/views/view/$view->name/revert",
01276         'query' => array('destination' => "admin/structure/views/view/$view->name"),
01277       );
01278     }
01279     else {
01280       $element['extra_actions']['#links']['delete'] = array(
01281         'title' => t('delete view'),
01282         'href' => "admin/structure/views/view/$view->name/delete",
01283       );
01284     }
01285   }
01286 
01287   // Determine the displays available for editing.
01288   if ($tabs = views_ui_edit_page_display_tabs($view, $display_id)) {
01289     if ($display_id) {
01290       $tabs[$display_id]['#active'] = TRUE;
01291     }
01292     $tabs['#prefix'] = '<h2 class="element-invisible">' . t('Secondary tabs') . '</h2><ul id = "views-display-menu-tabs" class="tabs secondary">';
01293     $tabs['#suffix'] = '</ul>';
01294     $element['tabs'] = $tabs;
01295   }
01296 
01297   // Buttons for adding a new display.
01298   foreach (views_fetch_plugin_names('display', NULL, array($view->base_table)) as $type => $label) {
01299     $element['add_display'][$type] = array(
01300       '#type' => 'submit',
01301       '#value' => t('Add !display', array('!display' => $label)),
01302       '#limit_validation_errors' => array(),
01303       '#submit' => array('views_ui_edit_form_submit_add_display', 'views_ui_edit_form_submit_delay_destination'),
01304       '#attributes' => array('class' => array('add-display')),
01305       // Allow JavaScript to remove the 'Add ' prefix from the button label when
01306       // placing the button in a "Add" dropdown menu.
01307       '#process' => array_merge(array('views_ui_form_button_was_clicked'), element_info_property('submit', '#process', array())),
01308       '#values' => array(t('Add !display', array('!display' => $label)), $label),
01309     );
01310   }
01311 
01312   return $element;
01313 }
01314 
01315 function views_ui_get_default_ajax_message() {
01316   return '<div class="message">' . t("Click on an item to edit that item's details.") . '</div>';
01317 }
01318 
01322 function views_ui_edit_form_submit_add_display($form, &$form_state) {
01323   $view = $form_state['view'];
01324 
01325   // Create the new display.
01326   $parents = $form_state['triggering_element']['#parents'];
01327   $display_type = array_pop($parents);
01328   $display_id = $view->add_display($display_type);
01329   views_ui_cache_set($view);
01330 
01331   // Redirect to the new display's edit page.
01332   $form_state['redirect'] = 'admin/structure/views/view/' . $view->name . '/edit/' . $display_id;
01333 }
01334 
01338 function views_ui_edit_form_submit_duplicate_display($form, &$form_state) {
01339   $view = $form_state['view'];
01340   $display_id = $form_state['display_id'];
01341 
01342   // Create the new display.
01343   $display = $view->display[$display_id];
01344   $new_display_id = $view->add_display($display->display_plugin);
01345   $view->display[$new_display_id] = clone $display;
01346   $view->display[$new_display_id]->id = $new_display_id;
01347 
01348   // By setting the current display the changed marker will appear on the new
01349   // display.
01350   $view->current_display = $new_display_id;
01351   views_ui_cache_set($view);
01352 
01353   // Redirect to the new display's edit page.
01354   $form_state['redirect'] = 'admin/structure/views/view/' . $view->name . '/edit/' . $new_display_id;
01355 }
01356 
01360 function views_ui_edit_form_submit_delete_display($form, &$form_state) {
01361   $view = $form_state['view'];
01362   $display_id = $form_state['display_id'];
01363 
01364   // Mark the display for deletion.
01365   $view->display[$display_id]->deleted = TRUE;
01366   views_ui_cache_set($view);
01367 
01368   // Redirect to the top-level edit page. The first remaining display will
01369   // become the active display.
01370   $form_state['redirect'] = 'admin/structure/views/view/' . $view->name;
01371 }
01372 
01376 function views_ui_edit_form_submit_undo_delete_display($form, &$form_state) {
01377   // Create the new display
01378   $id = $form_state['display_id'];
01379   $form_state['view']->display[$id]->deleted = FALSE;
01380 
01381   // Store in cache
01382   views_ui_cache_set($form_state['view']);
01383 
01384   // Redirect to the top-level edit page.
01385   $form_state['redirect'] = 'admin/structure/views/view/' . $form_state['view']->name . '/edit/' . $id;
01386 }
01387 
01391 function views_ui_edit_form_submit_enable_display($form, &$form_state) {
01392   $id = $form_state['display_id'];
01393   // set_option doesn't work because this would might affect upper displays
01394   $form_state['view']->display[$id]->handler->set_option('enabled', TRUE);
01395 
01396   // Store in cache
01397   views_ui_cache_set($form_state['view']);
01398 
01399   // Redirect to the top-level edit page.
01400   $form_state['redirect'] = 'admin/structure/views/view/' . $form_state['view']->name . '/edit/' . $id;
01401 }
01402 
01406 function views_ui_edit_form_submit_disable_display($form, &$form_state) {
01407   $id = $form_state['display_id'];
01408   $form_state['view']->display[$id]->handler->set_option('enabled', FALSE);
01409 
01410   // Store in cache
01411   views_ui_cache_set($form_state['view']);
01412 
01413   // Redirect to the top-level edit page.
01414   $form_state['redirect'] = 'admin/structure/views/view/' . $form_state['view']->name . '/edit/' . $id;
01415 }
01416 
01420 function views_ui_edit_form_submit_preview($form, &$form_state) {
01421   // Rebuild the form with a pristine $view object.
01422   $form_state['build_info']['args'][0] = views_ui_cache_load($form_state['view']->name);
01423   $form_state['show_preview'] = TRUE;
01424   $form_state['rebuild'] = TRUE;
01425 }
01426 
01437 function views_ui_edit_form_submit_delay_destination($form, &$form_state) {
01438   if (isset($_GET['destination']) && $form_state['redirect'] !== FALSE) {
01439     if (!isset($form_state['redirect'])) {
01440       $form_state['redirect'] = $_GET['q'];
01441     }
01442     if (is_string($form_state['redirect'])) {
01443       $form_state['redirect'] = array($form_state['redirect']);
01444     }
01445     $options = isset($form_state['redirect'][1]) ? $form_state['redirect'][1] : array();
01446     if (!isset($options['query']['destination'])) {
01447       $options['query']['destination'] = $_GET['destination'];
01448     }
01449     $form_state['redirect'][1] = $options;
01450     unset($_GET['destination']);
01451   }
01452 }
01453 
01467 function views_ui_edit_page_display_tabs($view, $display_id = NULL) {
01468   $tabs = array();
01469 
01470   // Create a tab for each display.
01471   foreach ($view->display as $id => $display) {
01472     $tabs[$id] = array(
01473       '#theme' => 'menu_local_task',
01474       '#link' => array(
01475         'title' => views_ui_get_display_label($view, $id),
01476         'href' => 'admin/structure/views/view/' . $view->name . '/edit/' . $id,
01477         'localized_options' => array(),
01478       ),
01479     );
01480     if (!empty($display->deleted)) {
01481       $tabs[$id]['#link']['localized_options']['attributes']['class'][] = 'views-display-deleted-link';
01482     }
01483     if (empty($display->options['enabled'])) {
01484       $tabs[$id]['#link']['localized_options']['attributes']['class'][] = 'views-display-disabled-link';
01485     }
01486   }
01487 
01488   // If the default display isn't supposed to be shown, don't display its tab, unless it's the only display.
01489   if ((!views_ui_show_default_display($view) && $display_id != 'default') && count($tabs) > 1) {
01490     $tabs['default']['#access'] = FALSE;
01491   }
01492 
01493   return $tabs;
01494 }
01495 
01499 function views_ui_show_default_display($view) {
01500   // Always show the default display for advanced users who prefer that mode.
01501   $advanced_mode = variable_get('views_ui_show_master_display', FALSE);
01502   // For other users, show the default display only if there are no others, and
01503   // hide it if there's at least one "real" display.
01504   $additional_displays = (count($view->display) == 1);
01505 
01506   return $advanced_mode || $additional_displays;
01507 }
01508 
01512 function views_ui_get_display_tab($view, $display_id) {
01513   $build = array();
01514   $display = $view->display[$display_id];
01515   // If the plugin doesn't exist, display an error message instead of an edit
01516   // page.
01517   if (empty($display->handler)) {
01518     $title = isset($display->display_title) ? $display->display_title : t('Invalid');
01519     // @TODO: Improved UX for the case where a plugin is missing.
01520     $build['#markup'] = t("Error: Display @display refers to a plugin named '@plugin', but that plugin is not available.", array('@display' => $display->id, '@plugin' => $display->display_plugin));
01521   }
01522   // Build the content of the edit page.
01523   else {
01524     $build['details'] = views_ui_get_display_tab_details($view, $display);
01525   }
01526   // In AJAX context, views_ui_regenerate_tab() returns this outside of form
01527   // context, so hook_form_views_ui_edit_form_alter() is insufficient.
01528   drupal_alter('views_ui_display_tab', $build, $view, $display_id);
01529   return $build;
01530 }
01531 
01541 function views_ui_get_display_tab_details($view, $display) {
01542   $display_title = views_ui_get_display_label($view, $display->id, FALSE);
01543   $build = array(
01544     '#theme_wrappers' => array('container'),
01545     '#attributes' => array('id' => 'edit-display-settings-details',),
01546   );
01547 
01548   $plugin = views_fetch_plugin_data('display', $view->display[$display->id]->display_plugin);
01549   // The following is for display purposes only. We need to determine if there is more than one button and wrap
01550   // the buttons in a .ctools-dropbutton class if more than one is present.  Otherwise, we'll just wrap the
01551   // actions in the .ctools-button class.
01552   $isDisplayDeleted = !empty($display->deleted);
01553   $isDeletable = empty($plugin['no remove']);
01554   // The master display cannot be cloned.
01555   $isDefault = $display->id == 'default';
01556   // @todo: Figure out why get_option doesn't work here.
01557   $isEnabled = $display->handler->get_option('enabled');
01558 
01559   if (!$isDisplayDeleted && $isDeletable && !$isDefault) {
01560     $prefix = '<div class="ctools-no-js ctools-button ctools-dropbutton"><div class="ctools-link"><a href="#" class="ctools-twisty ctools-text">open</a></div><div class="ctools-content"><ul class="horizontal right actions">';
01561     $suffix = '</ul></div></div>';
01562     $itemElement = 'li';
01563   }
01564   else {
01565     $prefix = '<div class="ctools-button"><div class="ctools-content"><ul class="horizontal right actions">';
01566     $suffix = '</ul></div></div>';
01567     $itemElement = 'li';
01568   }
01569 
01570   if ($display->id != 'default') {
01571     $build['top']['#theme_wrappers'] = array('container');
01572     $build['top']['#attributes']['id'] = 'edit-display-settings-top';
01573     $build['top']['#attributes']['class'] = array('views-ui-display-tab-actions', 'views-ui-display-tab-bucket', 'clearfix');
01574 
01575     // The Delete, Duplicate and Undo Delete buttons.
01576     $build['top']['actions'] = array(
01577       '#prefix' => $prefix,
01578       '#suffix' => $suffix,
01579     );
01580 
01581     if (!$isDisplayDeleted) {
01582       if (!$isEnabled) {
01583         $build['top']['actions']['enable'] = array(
01584           '#type' => 'submit',
01585           '#value' => t('enable @display_title', array('@display_title' => $display_title)),
01586           '#limit_validation_errors' => array(),
01587           '#submit' => array('views_ui_edit_form_submit_enable_display', 'views_ui_edit_form_submit_delay_destination'),
01588           '#prefix' => '<' . $itemElement . ' class="enable">',
01589           "#suffix" => '</' . $itemElement . '>',
01590         );
01591       }
01592       // Add a link to view the page.
01593       elseif ($display->handler->has_path()) {
01594         $path = $display->handler->get_path();
01595         if (strpos($path, '%') === FALSE) {
01596           $build['top']['actions']['path'] = array(
01597             '#type' => 'link',
01598             '#title' => t('view @display', array('@display' => $display->display_title)),
01599             '#options' => array('alt' => array(t("Go to the real page for this display"))),
01600             '#href' => $path,
01601             '#prefix' => '<' . $itemElement . ' class="view">',
01602             "#suffix" => '</' . $itemElement . '>',
01603           );
01604         }
01605       }
01606       if (!$isDefault) {
01607         $build['top']['actions']['duplicate'] = array(
01608           '#type' => 'submit',
01609           '#value' => t('clone @display_title', array('@display_title' => $display_title)),
01610           '#limit_validation_errors' => array(),
01611           '#submit' => array('views_ui_edit_form_submit_duplicate_display', 'views_ui_edit_form_submit_delay_destination'),
01612           '#prefix' => '<' . $itemElement . ' class="duplicate">',
01613           "#suffix" => '</' . $itemElement . '>',
01614         );
01615       }
01616       if ($isDeletable) {
01617         $build['top']['actions']['delete'] = array(
01618           '#type' => 'submit',
01619           '#value' => t('delete @display_title', array('@display_title' => $display_title)),
01620           '#limit_validation_errors' => array(),
01621           '#submit' => array('views_ui_edit_form_submit_delete_display', 'views_ui_edit_form_submit_delay_destination'),
01622           '#prefix' => '<' . $itemElement . ' class="delete">',
01623           "#suffix" => '</' . $itemElement . '>',
01624         );
01625       }
01626       if ($isEnabled) {
01627         $build['top']['actions']['disable'] = array(
01628           '#type' => 'submit',
01629           '#value' => t('disable @display_title', array('@display_title' => $display_title)),
01630           '#limit_validation_errors' => array(),
01631           '#submit' => array('views_ui_edit_form_submit_disable_display', 'views_ui_edit_form_submit_delay_destination'),
01632           '#prefix' => '<' . $itemElement . ' class="disable">',
01633           "#suffix" => '</' . $itemElement . '>',
01634         );
01635       }
01636     }
01637     else {
01638       $build['top']['actions']['undo_delete'] = array(
01639         '#type' => 'submit',
01640         '#value' => t('undo delete of @display_title', array('@display_title' => $display_title)),
01641         '#limit_validation_errors' => array(),
01642         '#submit' => array('views_ui_edit_form_submit_undo_delete_display', 'views_ui_edit_form_submit_delay_destination'),
01643         '#prefix' => '<' . $itemElement . ' class="undo-delete">',
01644         "#suffix" => '</' . $itemElement . '>',
01645       );
01646     }
01647 
01648     // The area above the three columns.
01649     $build['top']['display_title'] = array(
01650       '#theme' => 'views_ui_display_tab_setting',
01651       '#description' => t('Display name'),
01652       '#link' => $display->handler->option_link(check_plain($display_title), 'display_title'),
01653     );
01654   }
01655 
01656   $build['columns'] = array();
01657   $build['columns']['#theme_wrappers'] = array('container');
01658   $build['columns']['#attributes'] = array('id' => 'edit-display-settings-main', 'class' => array('clearfix', 'views-display-columns'),);
01659 
01660   $build['columns']['first']['#theme_wrappers'] = array('container');
01661   $build['columns']['first']['#attributes'] = array('class' => array('views-display-column', 'first'));
01662 
01663   $build['columns']['second']['#theme_wrappers'] = array('container');
01664   $build['columns']['second']['#attributes'] = array('class' => array('views-display-column', 'second'));
01665 
01666   $build['columns']['second']['settings'] = array();
01667   $build['columns']['second']['header'] = array();
01668   $build['columns']['second']['footer'] = array();
01669   $build['columns']['second']['pager'] = array();
01670 
01671   // The third column buckets are wrapped in a CTools collapsible div
01672   $build['columns']['third']['#theme_wrappers'] = array('container');
01673   $build['columns']['third']['#attributes'] = array('class' => array('views-display-column', 'third', 'ctools-collapsible-container', 'ctools-collapsible-remember'));
01674   // Specify an id that won't change after AJAX requests, so ctools can keep
01675   // track of the user's preferred collapsible state. Use the same id across
01676   // different displays of the same view, so changing displays doesn't
01677   // recollapse the column.
01678   $build['columns']['third']['#attributes']['id'] = 'views-ui-advanced-column-' . $view->name;
01679   // Collapse the div by default.
01680   if (!variable_get('views_ui_show_advanced_column', FALSE)) {
01681     $build['columns']['third']['#attributes']['class'][] = 'ctools-collapsed';
01682   }
01683   $build['columns']['third']['advanced'] = array('#markup' => '<h3 class="ctools-collapsible-handle"><a href="">' . t('Advanced') . '</a></h3>',);
01684   $build['columns']['third']['collapse']['#theme_wrappers'] = array('container');
01685   $build['columns']['third']['collapse']['#attributes'] = array('class' => array('ctools-collapsible-content',),);
01686 
01687   // Each option (e.g. title, access, display as grid/table/list) fits into one
01688   // of several "buckets," or boxes (Format, Fields, Sort, and so on).
01689   $buckets = array();
01690 
01691   // Fetch options from the display plugin, with a list of buckets they go into.
01692   $options = array();
01693   $display->handler->options_summary($buckets, $options);
01694 
01695   // Place each option into its bucket.
01696   foreach ($options as $id => $option) {
01697     // Each option self-identifies as belonging in a particular bucket.
01698     $buckets[$option['category']]['build'][$id] = views_ui_edit_form_get_build_from_option($id, $option, $view, $display);
01699   }
01700 
01701   // Place each bucket into the proper column.
01702   foreach ($buckets as $id => $bucket) {
01703     // Let buckets identify themselves as belonging in a column.
01704     if (isset($bucket['column']) && isset($build['columns'][$bucket['column']])) {
01705       $column = $bucket['column'];
01706     }
01707     // If a bucket doesn't pick one of our predefined columns to belong to, put
01708     // it in the last one.
01709     else {
01710       $column = 'third';
01711     }
01712     if (isset($bucket['build']) && is_array($bucket['build'])) {
01713       // The third column is a CTools collapsible div, so
01714       // the structure of the form is a little different for this column
01715       if ($column === 'third') {
01716         $build['columns'][$column]['collapse'][$id] = $bucket['build'];
01717         $build['columns'][$column]['collapse'][$id]['#theme_wrappers'][] = 'views_ui_display_tab_bucket';
01718         $build['columns'][$column]['collapse'][$id]['#title'] = !empty($bucket['title']) ? $bucket['title'] : '';
01719         $build['columns'][$column]['collapse'][$id]['#name'] = !empty($bucket['title']) ? $bucket['title'] : $id;
01720       }
01721       else {
01722         $build['columns'][$column][$id] = $bucket['build'];
01723         $build['columns'][$column][$id]['#theme_wrappers'][] = 'views_ui_display_tab_bucket';
01724         $build['columns'][$column][$id]['#title'] = !empty($bucket['title']) ? $bucket['title'] : '';
01725         $build['columns'][$column][$id]['#name'] = !empty($bucket['title']) ? $bucket['title'] : $id;
01726       }
01727     }
01728   }
01729 
01730   $build['columns']['first']['fields'] = views_ui_edit_form_get_bucket('field', $view, $display);
01731   $build['columns']['first']['filters'] = views_ui_edit_form_get_bucket('filter', $view, $display);
01732   $build['columns']['first']['sorts'] = views_ui_edit_form_get_bucket('sort', $view, $display);
01733   $build['columns']['second']['header'] = views_ui_edit_form_get_bucket('header', $view, $display);
01734   $build['columns']['second']['footer'] = views_ui_edit_form_get_bucket('footer', $view, $display);
01735   $build['columns']['third']['collapse']['arguments'] = views_ui_edit_form_get_bucket('argument', $view, $display);
01736   $build['columns']['third']['collapse']['relationships'] = views_ui_edit_form_get_bucket('relationship', $view, $display);
01737   $build['columns']['third']['collapse']['empty'] = views_ui_edit_form_get_bucket('empty', $view, $display);
01738 
01739   return $build;
01740 }
01741 
01748 function views_ui_edit_form_get_build_from_option($id, $option, $view, $display) {
01749   $option_build = array();
01750   $option_build['#theme'] = 'views_ui_display_tab_setting';
01751 
01752   $option_build['#description'] = $option['title'];
01753 
01754   $option_build['#link'] = $display->handler->option_link($option['value'], $id, '', empty($option['desc']) ? '' : $option['desc']);
01755 
01756   $option_build['#links'] = array();
01757   if (!empty($option['links']) && is_array($option['links'])) {
01758     foreach ($option['links'] as $link_id => $link_value) {
01759       $option_build['#settings_links'][] = $display->handler->option_link($option['setting'], $link_id, 'views-button-configure', $link_value);
01760     }
01761   }
01762 
01763   if (!empty($display->handler->options['defaults'][$id])) {
01764     $display_id = 'default';
01765     $option_build['#defaulted'] = TRUE;
01766   }
01767   else {
01768     $display_id = $display->id;
01769     if (!$display->handler->is_default_display()) {
01770       if ($display->handler->defaultable_sections($id)) {
01771         $option_build['#overridden'] = TRUE;
01772       }
01773     }
01774   }
01775   $option_build['#attributes']['class'][] = drupal_clean_css_identifier($display_id . '-' . $id);
01776   if (!empty($view->changed_sections[$display_id . '-' . $id])) {
01777     $option_build['#changed'] = TRUE;
01778   }
01779   return $option_build;
01780 }
01781 
01782 function template_preprocess_views_ui_display_tab_setting(&$variables) {
01783   static $zebra = 0;
01784   $variables['zebra'] = ($zebra % 2 === 0 ? 'odd' : 'even');
01785   $zebra++;
01786 
01787   // Put the main link to the left side
01788   array_unshift($variables['settings_links'], $variables['link']);
01789   $variables['settings_links'] = implode('<span class="label">&nbsp;|&nbsp;</span>', $variables['settings_links']);
01790 
01791   // Add classes associated with this display tab to the overall list.
01792   $variables['classes_array'] = array_merge($variables['classes_array'], $variables['class']);
01793 
01794   if (!empty($variables['defaulted'])) {
01795     $variables['classes_array'][] = 'defaulted';
01796   }
01797   if (!empty($variables['overridden'])) {
01798     $variables['classes_array'][] = 'overridden';
01799     $variables['attributes_array']['title'][] = t('Overridden');
01800   }
01801 
01802   // Append a colon to the description, if requested.
01803   if ($variables['description'] && $variables['description_separator']) {
01804     $variables['description'] .= t(':');
01805   }
01806 }
01807 
01808 function template_preprocess_views_ui_display_tab_bucket(&$variables) {
01809   $element = $variables['element'];
01810 
01811   $variables['item_help_icon'] = '';
01812   if (!empty($element['#item_help_icon'])) {
01813     $variables['item_help_icon'] = render($element['#item_help_icon']);
01814   }
01815   if (!empty($element['#name'])) {
01816     $variables['classes_array'][] = drupal_clean_css_identifier(strtolower($element['#name']));
01817   }
01818   if (!empty($element['#overridden'])) {
01819     $variables['classes_array'][] = 'overridden';
01820     $variables['attributes_array']['title'][] = t('Overridden');
01821   }
01822 
01823   $variables['content'] = $element['#children'];
01824   $variables['title'] = $element['#title'];
01825   $variables['actions'] = !empty($element['#actions']) ? $element['#actions'] : '';
01826 }
01827 
01828 function template_preprocess_views_ui_display_tab_column(&$variables) {
01829   $element = $variables['element'];
01830 
01831   $variables['content'] = $element['#children'];
01832   $variables['column'] = $element['#column'];
01833 }
01834 
01844 function views_ui_pre_render_add_fieldset_markup($form) {
01845   foreach (element_children($form) as $key) {
01846     $element = $form[$key];
01847     // In our form builder functions, we added an arbitrary #fieldset property
01848     // to any element that belongs in a fieldset. If this form element has that
01849     // property, move it into its fieldset.
01850     if (isset($element['#fieldset']) && isset($form[$element['#fieldset']])) {
01851       $form[$element['#fieldset']][$key] = $element;
01852       // Remove the original element this duplicates.
01853       unset($form[$key]);
01854     }
01855   }
01856 
01857   return $form;
01858 }
01859 
01868 function views_ui_pre_render_flatten_data($form) {
01869   foreach (element_children($form) as $key) {
01870     $element = $form[$key];
01871     if (!empty($element['#flatten'])) {
01872       foreach (element_children($element) as $child_key) {
01873         $form[$child_key] = $form[$key][$child_key];
01874       }
01875       // All done, remove the now-empty parent.
01876       unset($form[$key]);
01877     }
01878   }
01879 
01880   return $form;
01881 }
01882 
01894 function views_ui_pre_render_move_argument_options($form) {
01895   foreach (element_children($form) as $key) {
01896     $element = $form[$key];
01897     if (!empty($element['#argument_option'])) {
01898       $container_name = $element['#argument_option'] . '_options';
01899       if (isset($form['no_argument']['default_action'][$container_name])) {
01900         $form['no_argument']['default_action'][$container_name][$key] = $element;
01901       }
01902       // Remove the original element this duplicates.
01903       unset($form[$key]);
01904     }
01905   }
01906   return $form;
01907 }
01908 
01919 function views_ui_process_container_radios($element) {
01920   if (count($element['#options']) > 0) {
01921     foreach ($element['#options'] as $key => $choice) {
01922       $element += array($key => array());
01923       // Generate the parents as the autogenerator does, so we will have a
01924       // unique id for each radio button.
01925       $parents_for_id = array_merge($element['#parents'], array($key));
01926 
01927       $element[$key] += array(
01928         '#type' => 'radio',
01929         '#title' => $choice,
01930         // The key is sanitized in drupal_attributes() during output from the
01931         // theme function.
01932         '#return_value' => $key,
01933         '#default_value' => isset($element['#default_value']) ? $element['#default_value'] : NULL,
01934         '#attributes' => $element['#attributes'],
01935         '#parents' => $element['#parents'],
01936         '#id' => drupal_html_id('edit-' . implode('-', $parents_for_id)),
01937         '#ajax' => isset($element['#ajax']) ? $element['#ajax'] : NULL,
01938       );
01939       $element[$key . '_options'] = array(
01940         '#type' => 'container',
01941         '#attributes' => array('class' => array('views-admin-dependent')),
01942       );
01943     }
01944   }
01945   return $element;
01946 }
01947 
01948 /*
01949  * Import a view from cut & paste.
01950  */
01951 function views_ui_import_page($form, &$form_state) {
01952   $form['name'] = array(
01953     '#type' => 'textfield',
01954     '#title' => t('View name'),
01955     '#description' => t('Enter the name to use for this view if it is different from the source view. Leave blank to use the name of the view.'),
01956   );
01957 
01958   $form['name_override'] = array(
01959     '#type' => 'checkbox',
01960     '#title' => t('Replace an existing view if one exists with the same name'),
01961   );
01962 
01963   $form['view'] = array(
01964     '#type' => 'textarea',
01965     '#title' => t('Paste view code here'),
01966     '#required' => TRUE,
01967   );
01968 
01969   $form['submit'] = array(
01970     '#type' => 'submit',
01971     '#value' => t('Import'),
01972     '#submit' => array('views_ui_import_submit'),
01973     '#validate' => array('views_ui_import_validate'),
01974   );
01975   return $form;
01976 }
01977 
01981 function views_ui_import_validate($form, &$form_state) {
01982   $view = '';
01983   views_include('view');
01984   // Be forgiving if someone pastes views code that starts with '<?php'.
01985   if (substr($form_state['values']['view'], 0, 5) == '<?php') {
01986     $form_state['values']['view'] = substr($form_state['values']['view'], 5);
01987   }
01988   ob_start();
01989   eval($form_state['values']['view']);
01990   ob_end_clean();
01991 
01992   if (!is_object($view)) {
01993     return form_error($form['view'], t('Unable to interpret view code.'));
01994   }
01995 
01996   if (empty($view->api_version) || $view->api_version < 2) {
01997     form_error($form['view'], t('That view is not compatible with this version of Views.
01998       If you have a view from views1 you have to go to a drupal6 installation and import it there.'));
01999   }
02000   elseif (version_compare($view->api_version, views_api_version(), '>')) {
02001     form_error($form['view'], t('That view is created for the version @import_version of views, but you only have @api_version', array(
02002       '@import_version' => $view->api_version,
02003       '@api_version' => views_api_version())));
02004   }
02005 
02006   // View name must be alphanumeric or underscores, no other punctuation.
02007   if (!empty($form_state['values']['name']) && preg_match('/[^a-zA-Z0-9_]/', $form_state['values']['name'])) {
02008     form_error($form['name'], t('View name must be alphanumeric or underscores only.'));
02009   }
02010 
02011   if ($form_state['values']['name']) {
02012     $view->name = $form_state['values']['name'];
02013   }
02014 
02015   $test = views_get_view($view->name);
02016   if (!$form_state['values']['name_override']) {
02017     if ($test && $test->type != t('Default')) {
02018       form_set_error('', t('A view by that name already exists; please choose a different name'));
02019     }
02020   }
02021   else {
02022     if ($test->vid) {
02023       $view->vid = $test->vid;
02024     }
02025   }
02026 
02027   // Make sure base table gets set properly if it got moved.
02028   $view->update();
02029 
02030   $view->init_display();
02031 
02032   $broken = FALSE;
02033   // Make sure that all plugins and handlers needed by this view actually exist.
02034   foreach ($view->display as $id => $display) {
02035     if (empty($display->handler) || !empty($display->handler->broken)) {
02036       drupal_set_message(t('Display plugin @plugin is not available.', array('@plugin' => $display->display_plugin)), 'error');
02037       $broken = TRUE;
02038       continue;
02039     }
02040 
02041     $plugin = views_get_plugin('style', $display->handler->get_option('style_plugin'));
02042     if (!$plugin) {
02043       drupal_set_message(t('Style plugin @plugin is not available.', array('@plugin' => $display->handler->get_option('style_plugin'))), 'error');
02044       $broken = TRUE;
02045     }
02046     elseif ($plugin->uses_row_plugin()) {
02047       $plugin = views_get_plugin('row', $display->handler->get_option('row_plugin'));
02048       if (!$plugin) {
02049         drupal_set_message(t('Row plugin @plugin is not available.', array('@plugin' => $display->handler->get_option('row_plugin'))), 'error');
02050         $broken = TRUE;
02051       }
02052     }
02053 
02054     foreach (views_object_types() as $type => $info) {
02055       $handlers = $display->handler->get_handlers($type);
02056       if ($handlers) {
02057         foreach ($handlers as $id => $handler) {
02058           if ($handler->broken()) {
02059             drupal_set_message(t('@type handler @table.@field is not available.', array(
02060               '@type' => $info['stitle'],
02061               '@table' => $handler->table,
02062               '@field' => $handler->field,
02063             )), 'error');
02064             $broken = TRUE;
02065           }
02066         }
02067       }
02068     }
02069   }
02070 
02071   if ($broken) {
02072     form_set_error('', t('Unable to import view.'));
02073   }
02074 
02075   $form_state['view'] = &$view;
02076 }
02077 
02081 function views_ui_import_submit($form, &$form_state) {
02082   // Store in cache and then go to edit.
02083   views_ui_cache_set($form_state['view']);
02084   $form_state['redirect'] = 'admin/structure/views/view/' . $form_state['view']->name . '/edit';
02085 }
02086 
02090 function views_ui_edit_view_form_validate($form, &$form_state) {
02091   // Do not validate cancel or delete or revert.
02092   if (empty($form_state['clicked_button']['#value']) || $form_state['clicked_button']['#value'] != t('Save')) {
02093     return;
02094   }
02095 
02096   $errors = $form_state['view']->validate();
02097   if ($errors !== TRUE) {
02098     foreach ($errors as $error) {
02099       form_set_error('', $error);
02100     }
02101   }
02102 }
02103 
02107 function views_ui_edit_view_form_submit($form, &$form_state) {
02108   // Go through and remove displayed scheduled for removal.
02109   foreach ($form_state['view']->display as $id => $display) {
02110     if (!empty($display->deleted)) {
02111       unset($form_state['view']->display[$id]);
02112     }
02113   }
02114   // Rename display ids if needed.
02115   foreach ($form_state['view']->display as $id => $display) {
02116     if (!empty($display->new_id)) {
02117       $form_state['view']->display[$id]->id = $display->new_id;
02118       // Redirect the user to the renamed display to be sure that the page itself exists and doesn't throw errors.
02119       $form_state['redirect'] = 'admin/structure/views/view/' . $form_state['view']->name . '/edit/' . $display->new_id;
02120     }
02121   }
02122 
02123   // Direct the user to the right url, if the path of the display has changed.
02124   if (!empty($_GET['destination'])) {
02125     $destination = $_GET['destination'];
02126     // Find out the first display which has a changed path and redirect to this url.
02127     $old_view = views_get_view($form_state['view']->name);
02128     foreach ($old_view->display as $id => $display) {
02129       // Only check for displays with a path.
02130       if (!isset($display->display_options['path'])) {
02131         continue;
02132       }
02133       $old_path = $display->display_options['path'];
02134       if (($display->display_plugin == 'page') && ($old_path == $destination) && ($old_path != $form_state['view']->display[$id]->display_options['path'])) {
02135         $destination = $form_state['view']->display[$id]->display_options['path'];
02136         unset($_GET['destination']);
02137       }
02138     }
02139     $form_state['redirect'] = $destination;
02140   }
02141 
02142   $form_state['view']->save();
02143   drupal_set_message(t('The view %name has been saved.', array('%name' => $form_state['view']->get_human_name())));
02144 
02145   // Make sure menu items get rebuilt as neces
02146   menu_rebuild();
02147 
02148   // Clear the views cache.
02149   cache_clear_all('*', 'cache_views');
02150 
02151   // Clear the page cache.
02152   cache_clear_all();
02153 
02154   // Remove this view from cache so we can edit it properly.
02155   ctools_object_cache_clear('view', $form_state['view']->name);
02156 }
02157 
02161 function views_ui_edit_view_form_cancel($form, &$form_state) {
02162   // Remove this view from cache so edits will be lost.
02163   ctools_object_cache_clear('view', $form_state['view']->name);
02164   if (empty($form['view']->vid)) {
02165     // I seem to have to drupal_goto here because I can't get fapi to
02166     // honor the redirect target. Not sure what I screwed up here.
02167     drupal_goto('admin/structure/views');
02168   }
02169 }
02170 
02171 function views_ui_edit_view_form_delete($form, &$form_state) {
02172   unset($_REQUEST['destination']);
02173   // Redirect to the delete confirm page
02174   $form_state['redirect'] = array('admin/structure/views/view/' . $form_state['view']->name . '/delete', array('query' => drupal_get_destination() + array('cancel' => 'admin/structure/views/view/' . $form_state['view']->name . '/edit')));
02175 }
02176 
02180 function views_ui_edit_form_get_bucket($type, $view, $display) {
02181   $build = array(
02182     '#theme_wrappers' => array('views_ui_display_tab_bucket'),
02183   );
02184   $types = views_object_types();
02185 
02186   $build['#overridden'] = FALSE;
02187   $build['#defaulted'] = FALSE;
02188   if (module_exists('advanced_help')) {
02189     $build['#item_help_icon'] = array(
02190       '#theme' => 'advanced_help_topic',
02191       '#module' => 'views',
02192       '#topic' => $type,
02193     );
02194   }
02195 
02196   $build['#name'] = $build['#title'] = $types[$type]['title'];
02197 
02198   // Different types now have different rearrange forms, so we use this switch
02199   // to get the right one.
02200   switch ($type) {
02201     case 'filter':
02202       $rearrange_url = "admin/structure/views/nojs/rearrange-$type/$view->name/$display->id/$type";
02203       $rearrange_text = t('And/Or, Rearrange');
02204       // TODO: Add another class to have another symbol for filter rearrange.
02205       $class = 'icon compact rearrange';
02206       break;
02207     case 'field':
02208       // Fetch the style plugin info so we know whether to list fields or not.
02209       $style_plugin = $display->handler->get_plugin();
02210       $uses_fields = $style_plugin && $style_plugin->uses_fields();
02211       if (!$uses_fields) {
02212         $build['fields'][] = array(
02213           '#markup' => t('The selected style or row format does not utilize fields.'),
02214           '#theme_wrappers' => array('views_container'),
02215           '#attributes' => array('class' => array('views-display-setting')),
02216         );
02217         return $build;
02218       }
02219 
02220     default:
02221       $rearrange_url = "admin/structure/views/nojs/rearrange/$view->name/$display->id/$type";
02222       $rearrange_text = t('Rearrange');
02223       $class = 'icon compact rearrange';
02224   }
02225 
02226   // Create an array of actions to pass to theme_links
02227   $actions = array();
02228   $count_handlers = count($display->handler->get_handlers($type));
02229   $actions['add'] = array(
02230     'title' => t('Add'),
02231     'href' => "admin/structure/views/nojs/add-item/$view->name/$display->id/$type",
02232     'attributes'=> array('class' => array('icon compact add', 'views-ajax-link'), 'title' => t('Add'), 'id' => 'views-add-' . $type),
02233     'html' => TRUE,
02234   );
02235   if ($count_handlers > 0) {
02236     $actions['rearrange'] = array(
02237       'title' => $rearrange_text,
02238       'href' => $rearrange_url,
02239       'attributes' => array('class' => array($class, 'views-ajax-link'), 'title' => t('Rearrange'), 'id' => 'views-rearrange-' . $type),
02240       'html' => TRUE,
02241     );
02242   }
02243 
02244   // Render the array of links
02245   $build['#actions'] = theme('links__ctools_dropbutton',
02246     array(
02247       'links' => $actions,
02248       'attributes' => array(
02249         'class' => array('inline', 'links', 'actions', 'horizontal', 'right')
02250       ),
02251       'class' => array('views-ui-settings-bucket-operations'),
02252     )
02253   );
02254 
02255   if (!$display->handler->is_default_display()) {
02256     if (!$display->handler->is_defaulted($types[$type]['plural'])) {
02257       $build['#overridden'] = TRUE;
02258     }
02259     else {
02260       $build['#defaulted'] = TRUE;
02261     }
02262   }
02263 
02264   // If there's an options form for the bucket, link to it.
02265   if (!empty($types[$type]['options'])) {
02266     $build['#title'] = l($build['#title'], "admin/structure/views/nojs/config-type/$view->name/$display->id/$type", array('attributes' => array('class' => array('views-ajax-link'), 'id' => 'views-title-' . $type)));
02267   }
02268 
02269   static $relationships = NULL;
02270   if (!isset($relationships)) {
02271     // Get relationship labels
02272     $relationships = array();
02273     // @todo: get_handlers()
02274     $handlers = $display->handler->get_option('relationships');
02275     if ($handlers) {
02276       foreach ($handlers as $id => $info) {
02277         $handler = $display->handler->get_handler('relationship', $id);
02278         $relationships[$id] = $handler->label();
02279       }
02280     }
02281   }
02282 
02283   // Filters can now be grouped so we do a little bit extra:
02284   $groups = array();
02285   $grouping = FALSE;
02286   if ($type == 'filter') {
02287     $group_info = $view->display_handler->get_option('filter_groups');
02288     // If there is only one group but it is using the "OR" filter, we still
02289     // treat it as a group for display purposes, since we want to display the
02290     // "OR" label next to items within the group.
02291     if (!empty($group_info['groups']) && (count($group_info['groups']) > 1 || current($group_info['groups']) == 'OR')) {
02292       $grouping = TRUE;
02293       $groups = array(0 => array());
02294     }
02295   }
02296 
02297   $build['fields'] = array();
02298 
02299   foreach ($display->handler->get_option($types[$type]['plural']) as $id => $field) {
02300     // Build the option link for this handler ("Node: ID = article").
02301     $build['fields'][$id] = array();
02302     $build['fields'][$id]['#theme'] = 'views_ui_display_tab_setting';
02303 
02304     $handler = $display->handler->get_handler($type, $id);
02305     if (empty($handler)) {
02306       $build['fields'][$id]['#class'][] = 'broken';
02307       $field_name = t('Broken/missing handler: @table > @field', array('@table' => $field['table'], '@field' => $field['field']));
02308       $build['fields'][$id]['#link'] = l($field_name, "admin/structure/views/nojs/config-item/$view->name/$display->id/$type/$id", array('attributes' => array('class' => array('views-ajax-link')), 'html' => TRUE));
02309       continue;
02310     }
02311 
02312     $field_name = $handler->ui_name(TRUE);
02313     if (!empty($field['relationship']) && !empty($relationships[$field['relationship']])) {
02314       $field_name = '(' . $relationships[$field['relationship']] . ') ' . $field_name;
02315     }
02316 
02317     $description = $handler->admin_summary();
02318     $link_text = $field_name . (empty($description) ? '' : " ($description)");
02319     $build['fields'][$id]['#link'] = l($link_text, "admin/structure/views/nojs/config-item/$view->name/$display->id/$type/$id", array('attributes' => array('class' => array('views-ajax-link')), 'html' => TRUE));
02320     $build['fields'][$id]['#class'][] = drupal_clean_css_identifier($display->id . '-' . $type . '-' . $id);
02321     if (!empty($view->changed_sections[$display->id . '-' . $type . '-' . $id])) {
02322       // @TODO: #changed is no longer being used?
02323       $build['fields'][$id]['#changed'] = TRUE;
02324     }
02325 
02326     if ($display->handler->use_group_by() && $handler->use_group_by()) {
02327       $build['fields'][$id]['#settings_links'][] = l('<span class="label">' . t('Aggregation settings') . '</span>', "admin/structure/views/nojs/config-item-group/$view->name/$display->id/$type/$id", array('attributes' => array('class' => 'views-button-configure views-ajax-link', 'title' => t('Aggregation settings')), 'html' => true));
02328     }
02329 
02330     if ($handler->has_extra_options()) {
02331       $build['fields'][$id]['#settings_links'][] = l('<span class="label">' . t('Settings') . '</span>', "admin/structure/views/nojs/config-item-extra/$view->name/$display->id/$type/$id", array('attributes' => array('class' => array('views-button-configure', 'views-ajax-link'), 'title' => t('Settings')), 'html' => true));
02332     }
02333 
02334     if ($grouping) {
02335       $gid = $handler->options['group'];
02336 
02337       // Show in default group if the group does not exist.
02338       if (empty($group_info['groups'][$gid])) {
02339         $gid = 0;
02340       }
02341       $groups[$gid][] = $id;
02342     }
02343   }
02344 
02345   // If using grouping, re-order fields so that they show up properly in the list.
02346   if ($type == 'filter' && $grouping) {
02347     $store = $build['fields'];
02348     $build['fields'] = array();
02349     foreach ($groups as $gid => $contents) {
02350       // Display an operator between each group.
02351       if (!empty($build['fields'])) {
02352         $build['fields'][] = array(
02353           '#theme' => 'views_ui_display_tab_setting',
02354           '#class' => array('views-group-text'),
02355           '#link' => ($group_info['operator'] == 'OR' ? t('OR') : t('AND')),
02356         );
02357       }
02358       // Display an operator between each pair of filters within the group.
02359       $keys = array_keys($contents);
02360       $last = end($keys);
02361       foreach ($contents as $key => $pid) {
02362         if ($key != $last) {
02363           $store[$pid]['#link'] .= '&nbsp;&nbsp;' . ($group_info['groups'][$gid] == 'OR' ? t('OR') : t('AND'));
02364         }
02365         $build['fields'][$pid] = $store[$pid];
02366       }
02367     }
02368   }
02369 
02370   return $build;
02371 }
02372 
02376 function views_ui_regenerate_tab(&$view, &$output, $display_id) {
02377   if (!$view->set_display('default')) {
02378     return;
02379   }
02380 
02381   // Regenerate the main display area.
02382   $build = views_ui_get_display_tab($view, $display_id);
02383   views_ui_add_microweights($build);
02384   $output[] = ajax_command_html('#views-tab-' . $display_id, drupal_render($build));
02385 
02386   // Regenerate the top area so changes to display names and order will appear.
02387   $build = views_ui_render_display_top($view, $display_id);
02388   views_ui_add_microweights($build);
02389   $output[] = ajax_command_replace('#views-display-top', drupal_render($build));
02390 }
02391 
02398 function views_ui_add_microweights(&$build) {
02399   $count = 0;
02400   foreach (element_children($build) as $key) {
02401     if (!isset($build[$key]['#weight'])) {
02402       $build[$key]['#weight'] = $count/1000;
02403     }
02404     views_ui_add_microweights($build[$key]);
02405     $count++;
02406   }
02407 }
02408 
02417 function views_ui_standard_form_buttons(&$form, &$form_state, $form_id, $name = NULL, $third = NULL, $submit = NULL) {
02418   $form['buttons'] = array(
02419     '#prefix' => '<div class="clearfix"><div class="form-buttons">',
02420     '#suffix' => '</div></div>',
02421   );
02422 
02423   if (empty($name)) {
02424     $name = t('Apply');
02425     $view = $form_state['view'];
02426     if (!empty($view->stack) && count($view->stack) > 1) {
02427       $name = t('Apply and continue');
02428     }
02429     $names = array(t('Apply'), t('Apply and continue'));
02430   }
02431 
02432   // Forms that are purely informational set an ok_button flag, so we know not
02433   // to create an "Apply" button for them.
02434   if (empty($form_state['ok_button'])) {
02435     $form['buttons']['submit'] = array(
02436       '#type' => 'submit',
02437       '#value' => $name,
02438       // The regular submit handler ($form_id . '_submit') does not apply if
02439       // we're updating the default display. It does apply if we're updating
02440       // the current display. Since we have no way of knowing at this point
02441       // which display the user wants to update, views_ui_standard_submit will
02442       // take care of running the regular submit handler as appropriate.
02443       '#submit' => array('views_ui_standard_submit'),
02444     );
02445     // Form API button click detection requires the button's #value to be the
02446     // same between the form build of the initial page request, and the initial
02447     // form build of the request processing the form submission. Ideally, the
02448     // button's #value shouldn't change until the form rebuild step. However,
02449     // views_ui_ajax_form() implements a different multistep form workflow than
02450     // the Form API does, and adjusts $view->stack prior to form processing, so
02451     // we compensate by extending button click detection code to support any of
02452     // the possible button labels.
02453     if (isset($names)) {
02454       $form['buttons']['submit']['#values'] = $names;
02455       $form['buttons']['submit']['#process'] = array_merge(array('views_ui_form_button_was_clicked'), element_info_property($form['buttons']['submit']['#type'], '#process', array()));
02456     }
02457     // If a validation handler exists for the form, assign it to this button.
02458     if (function_exists($form_id . '_validate')) {
02459       $form['buttons']['submit']['#validate'][] = $form_id . '_validate';
02460     }
02461   }
02462 
02463   // Create a "Cancel" button. For purely informational forms, label it "OK".
02464   $cancel_submit = function_exists($form_id . '_cancel') ? $form_id . '_cancel' : 'views_ui_standard_cancel';
02465   $form['buttons']['cancel'] = array(
02466     '#type' => 'submit',
02467     '#value' => empty($form_state['ok_button']) ? t('Cancel') : t('Ok'),
02468     '#submit' => array($cancel_submit),
02469     '#validate' => array(),
02470   );
02471 
02472   // Some forms specify a third button, with a name and submit handler.
02473   if ($third) {
02474     if (empty($submit)) {
02475       $submit = 'third';
02476     }
02477     $third_submit = function_exists($form_id . '_' . $submit) ? $form_id . '_' . $submit : 'views_ui_standard_cancel';
02478 
02479     $form['buttons'][$submit] = array(
02480       '#type' => 'submit',
02481       '#value' => $third,
02482       '#validate' => array(),
02483       '#submit' => array($third_submit),
02484     );
02485   }
02486 
02487   // Compatibility, to be removed later: // TODO: When is "later"?
02488   // We used to set these items on the form, but now we want them on the $form_state:
02489   if (isset($form['#title'])) {
02490     $form_state['title'] = $form['#title'];
02491   }
02492   if (isset($form['#help_topic'])) {
02493     $form_state['help_topic'] = $form['#help_topic'];
02494   }
02495   if (isset($form['#help_module'])) {
02496     $form_state['help_module'] = $form['#help_module'];
02497   }
02498   if (isset($form['#url'])) {
02499     $form_state['url'] = $form['#url'];
02500   }
02501   if (isset($form['#section'])) {
02502     $form_state['#section'] = $form['#section'];
02503   }
02504   // Finally, we never want these cached -- our object cache does that for us.
02505   $form['#no_cache'] = TRUE;
02506 
02507   // If this isn't an ajaxy form, then we want to set the title.
02508   if (!empty($form['#title'])) {
02509     drupal_set_title($form['#title']);
02510   }
02511 }
02512 
02520 function views_ui_standard_submit($form, &$form_state) {
02521   // Determine whether the values the user entered are intended to apply to
02522   // the current display or the default display.
02523 
02524   list($was_defaulted, $is_defaulted, $revert) = views_ui_standard_override_values($form, $form_state);
02525 
02526   // Mark the changed section of the view as changed.
02527   // TODO: Document why we are doing this, and see if we still need it.
02528   if (!empty($form['#section'])) {
02529     $form_state['view']->changed_sections[$form['#section']] = TRUE;
02530   }
02531 
02532   // Based on the user's choice in the display dropdown, determine which display
02533   // these changes apply to.
02534   if ($revert) {
02535     // If it's revert just change the override and return.
02536     $display = &$form_state['view']->display[$form_state['display_id']];
02537     $display->handler->options_override($form, $form_state);
02538 
02539     // Don't execute the normal submit handling but still store the changed view into cache.
02540     views_ui_cache_set($form_state['view']);
02541     return;
02542   }
02543   elseif ($was_defaulted === $is_defaulted) {
02544     // We're not changing which display these form values apply to.
02545     // Run the regular submit handler for this form.
02546   }
02547   elseif ($was_defaulted && !$is_defaulted) {
02548     // We were using the default display's values, but we're now overriding
02549     // the default display and saving values specific to this display.
02550     $display = &$form_state['view']->display[$form_state['display_id']];
02551     // options_override toggles the override of this section.
02552     $display->handler->options_override($form, $form_state);
02553     $display->handler->options_submit($form, $form_state);
02554   }
02555   elseif (!$was_defaulted && $is_defaulted) {
02556     // We used to have an override for this display, but the user now wants
02557     // to go back to the default display.
02558     // Overwrite the default display with the current form values, and make
02559     // the current display use the new default values.
02560     $display = &$form_state['view']->display[$form_state['display_id']];
02561     // options_override toggles the override of this section.
02562     $display->handler->options_override($form, $form_state);
02563     $display->handler->options_submit($form, $form_state);
02564   }
02565 
02566   $submit_handler = $form['#form_id'] . '_submit';
02567   if (function_exists($submit_handler)) {
02568     $submit_handler($form, $form_state);
02569   }
02570 }
02571 
02575 function views_ui_standard_override_values($form, $form_state) {
02576   // Make sure the dropdown exists in the first place.
02577   if (isset($form_state['values']['override']['dropdown'])) {
02578     // #default_value is used to determine whether it was the default value or not.
02579     // So the available options are: $display, 'default' and 'default_revert', not 'defaults'.
02580     $was_defaulted = ($form['override']['dropdown']['#default_value'] === 'defaults');
02581     $is_defaulted = ($form_state['values']['override']['dropdown'] === 'default');
02582     $revert = ($form_state['values']['override']['dropdown'] === 'default_revert');
02583 
02584     if ($was_defaulted !== $is_defaulted && isset($form['#section'])) {
02585       // We're changing which display these values apply to.
02586       // Update the #section so it knows what to mark changed.
02587       $form['#section'] = str_replace('default-', $form_state['display_id'] . '-', $form['#section']);
02588     }
02589   }
02590   else {
02591     // The user didn't get the dropdown for overriding the default display.
02592     $was_defaulted = FALSE;
02593     $is_defaulted = FALSE;
02594     $revert = FALSE;
02595   }
02596 
02597   return array($was_defaulted, $is_defaulted, $revert);
02598 }
02599 
02603 function views_ui_standard_cancel($form, &$form_state) {
02604   if (!empty($form_state['view']->changed) && isset($form_state['view']->form_cache)) {
02605     unset($form_state['view']->form_cache);
02606     views_ui_cache_set($form_state['view']);
02607   }
02608 
02609   $form_state['redirect'] = 'admin/structure/views/view/' . $form_state['view']->name . '/edit';
02610 }
02611 
02617 function views_ui_standard_display_dropdown(&$form, &$form_state, $section) {
02618   $view = &$form_state['view'];
02619   $display_id = $form_state['display_id'];
02620   $displays = $view->display;
02621   $current_display = $view->display[$display_id];
02622 
02623   // Add the "2 of 3" progress indicator.
02624   // @TODO: Move this to a separate function if it's needed on any forms that
02625   // don't have the display dropdown.
02626   if ($form_progress = views_ui_get_form_progress($view)) {
02627     $form['progress']['#markup'] = '<div id="views-progress-indicator">' . t('@current of @total', array('@current' => $form_progress['current'], '@total' => $form_progress['total'])) . '</div>';
02628     $form['progress']['#weight'] = -1001;
02629   }
02630 
02631   if ($current_display->handler->is_default_display()) {
02632     return;
02633   }
02634 
02635   // Determine whether any other displays have overrides for this section.
02636   $section_overrides = FALSE;
02637   $section_defaulted = $current_display->handler->is_defaulted($section);
02638   foreach ($displays as $id => $display) {
02639     if ($id === 'default' || $id === $display_id) {
02640       continue;
02641     }
02642     if ($display->handler && !$display->handler->is_defaulted($section)) {
02643       $section_overrides = TRUE;
02644     }
02645   }
02646 
02647   $display_dropdown['default'] = ($section_overrides ? t('All displays (except overridden)') : t('All displays'));
02648   $display_dropdown[$display_id] = t('This @display_type (override)', array('@display_type' => $current_display->display_plugin));
02649   // Only display the revert option if we are in a overridden section.
02650   if (!$section_defaulted) {
02651     $display_dropdown['default_revert'] = t('Revert to default');
02652   }
02653 
02654   $form['override'] = array(
02655     '#prefix' => '<div class="views-override clearfix container-inline">',
02656     '#suffix' => '</div>',
02657     '#weight' => -1000,
02658     '#tree' => TRUE,
02659   );
02660   $form['override']['dropdown'] = array(
02661     '#type' => 'select',
02662     '#title' => t('For'), // @TODO: Translators may need more context than this.
02663     '#options' => $display_dropdown,
02664   );
02665   if ($current_display->handler->is_defaulted($section)) {
02666     $form['override']['dropdown']['#default_value'] = 'defaults';
02667   }
02668   else {
02669     $form['override']['dropdown']['#default_value'] = $display_id;
02670   }
02671 
02672 }
02673 
02686 function views_ui_get_form_progress($view) {
02687   $progress = FALSE;
02688   if (!empty($view->stack)) {
02689     $stack = $view->stack;
02690     // The forms on the stack have integer keys that don't change as the forms
02691     // are completed, so we can see which ones are still left.
02692     $keys = array_keys($view->stack);
02693     // Add 1 to the array keys for the benefit of humans, who start counting
02694     // from 1 and not 0.
02695     $current = reset($keys) + 1;
02696     $total = end($keys) + 1;
02697     if ($total > 1) {
02698       $progress = array();
02699       $progress['current'] = $current;
02700       $progress['total'] = $total;
02701     }
02702   }
02703   return $progress;
02704 }
02705 
02706 
02707 // --------------------------------------------------------------------------
02708 // Various subforms for editing the pieces of a view.
02709 
02710 function views_ui_ajax_forms($key = NULL) {
02711   $forms = array(
02712     'display' => array(
02713       'form_id' => 'views_ui_edit_display_form',
02714       'args' => array('section'),
02715     ),
02716     'remove-display' => array(
02717       'form_id' => 'views_ui_remove_display_form',
02718       'args' => array(),
02719     ),
02720     'config-type' => array(
02721       'form_id' => 'views_ui_config_type_form',
02722       'args' => array('type'),
02723     ),
02724     'rearrange' => array(
02725       'form_id' => 'views_ui_rearrange_form',
02726       'args' => array('type'),
02727     ),
02728     'rearrange-filter' => array(
02729       'form_id' => 'views_ui_rearrange_filter_form',
02730       'args' => array('type'),
02731     ),
02732     'reorder-displays' => array(
02733       'form_id' => 'views_ui_reorder_displays_form',
02734       'args' => array(),
02735     ),
02736     'add-item' => array(
02737       'form_id' => 'views_ui_add_item_form',
02738       'args' => array('type'),
02739     ),
02740     'config-item' => array(
02741       'form_id' => 'views_ui_config_item_form',
02742       'args' => array('type', 'id'),
02743     ),
02744     'config-item-extra' => array(
02745       'form_id' => 'views_ui_config_item_extra_form',
02746       'args' => array('type', 'id'),
02747     ),
02748     'config-item-group' => array(
02749       'form_id' => 'views_ui_config_item_group_form',
02750       'args' => array('type', 'id'),
02751     ),
02752     'config-style' => array(
02753       'form_id' => 'views_ui_config_style_form',
02754       'args' => array('type', 'id'),
02755     ),
02756     'edit-details' => array(
02757       'form_id' => 'views_ui_edit_details_form',
02758       'args' => array(),
02759     ),
02760     'analyze' => array(
02761       'form_id' => 'views_ui_analyze_view_form',
02762       'args' => array(),
02763     ),
02764   );
02765 
02766   if ($key) {
02767     return !empty($forms[$key]) ? $forms[$key] : NULL;
02768   }
02769 
02770   return $forms;
02771 }
02772 
02778 function views_ui_build_identifier($key, $view, $display_id, $args) {
02779   $form = views_ui_ajax_forms($key);
02780   // Automatically remove the single-form cache if it exists and
02781   // does not match the key.
02782   $identifier = implode('-', array($key, $view->name, $display_id));
02783 
02784   foreach ($form['args'] as $id) {
02785     $arg = (!empty($args)) ? array_shift($args) : NULL;
02786     $identifier .= '-' . $arg;
02787   }
02788   return $identifier;
02789 }
02790 
02795 function views_ui_build_form_state($js, $key, &$view, $display_id, $args) {
02796   $form = views_ui_ajax_forms($key);
02797   // Build up form state
02798   $form_state = array(
02799     'form_key' => $key,
02800     'form_id' => $form['form_id'],
02801     'view' => &$view,
02802     'ajax' => $js,
02803     'display_id' => $display_id,
02804     'no_redirect' => TRUE,
02805   );
02806 
02807   foreach ($form['args'] as $id) {
02808     $form_state[$id] = (!empty($args)) ? array_shift($args) : NULL;
02809   }
02810 
02811   return $form_state;
02812 }
02813 
02818 function views_ui_build_form_url($form_state) {
02819   $form = views_ui_ajax_forms($form_state['form_key']);
02820   $ajax = empty($form_state['ajax']) ? 'nojs' : 'ajax';
02821   $name = $form_state['view']->name;
02822   $url = "admin/structure/views/$ajax/$form_state[form_key]/$name/$form_state[display_id]";
02823   foreach ($form['args'] as $arg) {
02824     $url .= '/' . $form_state[$arg];
02825   }
02826   return $url;
02827 }
02828 
02833 function views_ui_add_form_to_stack($key, &$view, $display_id, $args, $top = FALSE) {
02834   if (empty($view->stack)) {
02835     $view->stack = array();
02836   }
02837 
02838   $stack = array(views_ui_build_identifier($key, $view, $display_id, $args), $key, &$view, $display_id, $args);
02839   // If we're being asked to add this form to the bottom of the stack, no
02840   // special logic is required. Our work is equally easy if we were asked to add
02841   // to the top of the stack, but there's nothing in it yet.
02842   if (!$top || empty($view->stack)) {
02843     $view->stack[] = $stack;
02844   }
02845   // If we're adding to the top of an existing stack, we have to maintain the
02846   // existing integer keys, so they can be used for the "2 of 3" progress
02847   // indicator (which will now read "2 of 4").
02848   else {
02849     $keys = array_keys($view->stack);
02850     $first = current($keys);
02851     $last = end($keys);
02852     for ($i = $last; $i >= $first; $i--) {
02853       if (!isset($view->stack[$i])) {
02854         continue;
02855       }
02856       // Move form number $i to the next position in the stack.
02857       $view->stack[$i + 1] = $view->stack[$i];
02858       unset($view->stack[$i]);
02859     }
02860     // Now that the previously $first slot is free, move the new form into it.
02861     $view->stack[$first] = $stack;
02862     ksort($view->stack);
02863   }
02864 }
02865 
02872 function views_ui_ajax_form($js, $key, $view, $display_id = '') {
02873   // Reset the cache of IDs. Drupal rather aggressively prevents id duplication
02874   // but this causes it to remember IDs that are no longer even being used.
02875   if (isset($_POST['ajax_html_ids'])) {
02876     unset($_POST['ajax_html_ids']);
02877   }
02878 
02879   $form = views_ui_ajax_forms($key);
02880   if (empty($form)) {
02881     return MENU_NOT_FOUND;
02882   }
02883 
02884   views_include('ajax');
02885   $args = func_get_args();
02886   // Remove the known args
02887   array_splice($args, 0, 4);
02888 
02889   $form_state = views_ui_build_form_state($js, $key, $view, $display_id, $args);
02890   // check to see if this is the top form of the stack. If it is, pop
02891   // it off; if it isn't, the user clicked somewhere else and the stack is
02892   // now irrelevant.
02893   if (!empty($view->stack)) {
02894     $identifier = views_ui_build_identifier($key, $view, $display_id, $args);
02895     // Retrieve the first form from the stack without changing the integer keys,
02896     // as they're being used for the "2 of 3" progress indicator.
02897     reset($view->stack);
02898     list($key, $top) = each($view->stack);
02899     unset($view->stack[$key]);
02900 
02901     if (array_shift($top) != $identifier) {
02902       $view->stack = array();
02903     }
02904   }
02905 
02906   // Automatically remove the form cache if it is set and the key does
02907   // not match. This way navigating away from the form without hitting
02908   // update will work.
02909   if (isset($view->form_cache) && $view->form_cache['key'] != $key) {
02910     unset($view->form_cache);
02911   }
02912 
02913   // With the below logic, we may end up rendering a form twice (or two forms
02914   // each sharing the same element ids), potentially resulting in
02915   // drupal_add_js() being called twice to add the same setting. drupal_get_js()
02916   // is ok with that, but until ajax_render() is (http://drupal.org/node/208611),
02917   // reset the drupal_add_js() static before rendering the second time.
02918   $drupal_add_js_original = drupal_add_js();
02919   $drupal_add_js = &drupal_static('drupal_add_js');
02920   $output = views_ajax_form_wrapper($form_state['form_id'], $form_state);
02921   if ($form_state['submitted'] && empty($form_state['rerender'])) {
02922     // Sometimes we need to re-generate the form for multi-step type operations.
02923     $object = NULL;
02924     if (!empty($view->stack)) {
02925       $drupal_add_js = $drupal_add_js_original;
02926       $stack = $view->stack;
02927       $top = array_shift($stack);
02928       $top[0] = $js;
02929       $form_state = call_user_func_array('views_ui_build_form_state', $top);
02930       $form_state['input'] = array();
02931       $form_state['url'] = url(views_ui_build_form_url($form_state));
02932       if (!$js) {
02933         return drupal_goto(views_ui_build_form_url($form_state));
02934       }
02935       $output = views_ajax_form_wrapper($form_state['form_id'], $form_state);
02936     }
02937     elseif (!$js) {
02938       // if nothing on the stack, non-js forms just go back to the main view editor.
02939       return drupal_goto("admin/structure/views/view/$view->name/edit");
02940     }
02941     else {
02942       $output = array();
02943       $output[] = views_ajax_command_dismiss_form();
02944       $output[] = views_ajax_command_show_buttons();
02945       $output[] = views_ajax_command_trigger_preview();
02946       if (!empty($form_state['#page_title'])) {
02947         $output[] = views_ajax_command_replace_title($form_state['#page_title']);
02948       }
02949     }
02950     // If this form was for view-wide changes, there's no need to regenerate
02951     // the display section of the form.
02952     if ($display_id !== '') {
02953       views_ui_regenerate_tab($view, $output, $display_id);
02954     }
02955   }
02956 
02957   return $js ? array('#type' => 'ajax', '#commands' => $output) : $output;
02958 }
02959 
02963 function views_ui_remove_display_form_restore($form, &$form_state) {
02964   // Create the new display
02965   $id = $form_state['display_id'];
02966   $form_state['view']->display[$id]->deleted = FALSE;
02967 
02968   // Store in cache
02969   views_ui_cache_set($form_state['view']);
02970 }
02971 
02975 function views_ui_analyze_view_form($form, &$form_state) {
02976   $view = &$form_state['view'];
02977 
02978   $form['#title'] = t('View analysis');
02979   $form['#section'] = 'analyze';
02980 
02981   views_include('analyze');
02982   $messages = views_analyze_view($view);
02983 
02984   $form['analysis'] = array(
02985     '#prefix' => '<div class="form-item">',
02986     '#suffix' => '</div>',
02987     '#markup' => views_analyze_format_result($view, $messages),
02988   );
02989 
02990   // Inform the standard button function that we want an OK button.
02991   $form_state['ok_button'] = TRUE;
02992   views_ui_standard_form_buttons($form, $form_state, 'views_ui_analyze_view_form');
02993   return $form;
02994 }
02995 
02999 function views_ui_analyze_view_form_submit($form, &$form_state) {
03000   $form_state['redirect'] = 'admin/structure/views/view/' . $form_state['view']->name . '/edit';
03001 }
03002 
03006 function views_ui_reorder_displays_form($form, &$form_state) {
03007   $view = &$form_state['view'];
03008   $display_id = $form_state['display_id'];
03009 
03010   $form['view'] = array('#type' => 'value', '#value' => $view);
03011 
03012   $form['#tree'] = TRUE;
03013 
03014   $last_display = end($view->display);
03015 
03016   foreach ($view->display as $display) {
03017     $form[$display->id] = array(
03018       'title'  => array('#markup' => $display->display_title),
03019       'weight' => array(
03020         '#type' => 'weight',
03021         '#value' => $display->position,
03022         '#delta' => $last_display->position,
03023         '#title' => t('Weight for @display', array('@display' => $display->display_title)),
03024         '#title_display' => 'invisible',
03025       ),
03026       '#tree' => TRUE,
03027       '#display' => $display,
03028       'removed' => array(
03029         '#type' => 'checkbox',
03030         '#id' => 'display-removed-' . $display->id,
03031         '#attributes' => array('class' => array('views-remove-checkbox')),
03032         '#default_value' => isset($display->deleted),
03033       ),
03034     );
03035 
03036     if (isset($display->deleted) && $display->deleted) {
03037       $form[$display->id]['deleted'] = array('#type' => 'value', '#value' => TRUE);
03038     }
03039     if ($display->id === 'default') {
03040       unset($form[$display->id]['weight']);
03041       unset($form[$display->id]['removed']);
03042     }
03043 
03044   }
03045 
03046   $form['#title'] = t('Displays Reorder');
03047   $form['#section'] = 'reorder';
03048 
03049   // Add javascript settings that will be added via $.extend for tabledragging
03050   $form['#js']['tableDrag']['reorder-displays']['weight'][0] = array(
03051     'target' => 'weight',
03052     'source' => NULL,
03053     'relationship' => 'sibling',
03054     'action' => 'order',
03055     'hidden' => TRUE,
03056     'limit' => 0,
03057   );
03058 
03059   $form['#action'] = url('admin/structure/views/nojs/reorder-displays/' . $view->name . '/' . $display_id);
03060 
03061   views_ui_standard_form_buttons($form, $form_state, 'views_ui_reorder_displays_form');
03062 
03063   return $form;
03064 }
03065 
03069 function _views_position_sort($display1, $display2) {
03070   if ($display1->position != $display2->position) {
03071     return $display1->position < $display2->position ? -1 : 1;
03072   }
03073 
03074   return 0;
03075 }
03076 
03080 function views_ui_reorder_displays_form_submit($form, &$form_state) {
03081   foreach($form_state['input'] as $display => $info) {
03082     // add each value that is a field with a weight to our list, but only if
03083     // it has had its 'removed' checkbox checked.
03084     if (is_array($info) && isset($info['weight']) && empty($info['removed'])) {
03085       $order[$display] = $info['weight'];
03086     }
03087   }
03088 
03089   // Sort the order array
03090   asort($order);
03091 
03092   // Fixing up positions
03093   $position = 2;
03094 
03095   foreach(array_keys($order) as $display) {
03096     $order[$display] = $position++;
03097   }
03098 
03099   // Setting up position and removing deleted displays
03100   $displays = $form_state['view']->display;
03101   foreach($displays as $display_id => $display) {
03102     // Don't touch the default !!!
03103     if ($display_id === 'default') {
03104       continue;
03105     }
03106     if (isset($order[$display_id])) {
03107       $form_state['view']->display[$display_id]->position = $order[$display_id];
03108     }
03109     else {
03110       $form_state['view']->display[$display_id]->deleted = TRUE;
03111     }
03112   }
03113 
03114   // Sorting back the display array as the position is not enough
03115   uasort($form_state['view']->display, '_views_position_sort');
03116 
03117   // Store in cache
03118   views_ui_cache_set($form_state['view']);
03119   $form_state['redirect'] = array('admin/structure/views/view/' . $form_state['view']->name . '/edit', array('fragment' => 'views-tab-default'));
03120 }
03121 
03125 function theme_views_ui_reorder_displays_form($vars) {
03126   $form = $vars['form'];
03127   $rows = array();
03128   foreach (element_children($form) as $key) {
03129     if (isset($form[$key]['#display'])) {
03130       $display = &$form[$key];
03131 
03132       $row = array();
03133       $row[] = drupal_render($display['title']);
03134       $form[$key]['weight']['#attributes']['class'] = array('weight');
03135       $row[] = drupal_render($form[$key]['weight']);
03136       if (isset($display['removed'])) {
03137         $row[] = drupal_render($form[$key]['removed']) .
03138           l('<span>' . t('Remove') . '</span>',
03139             'javascript:void()',
03140             array(
03141               'attributes' => array(
03142                 'id' => 'display-remove-link-' . $key,
03143                 'class' => array('views-button-remove display-remove-link'),
03144                 'alt' => t('Remove this display'),
03145                 'title' => t('Remove this display')),
03146               'html' => TRUE));
03147       }
03148       else {
03149         $row[] = '';
03150       }
03151       $class = array();
03152       $styles = array();
03153       if (isset($form[$key]['weight']['#type'])) {
03154         $class[] = 'draggable';
03155       }
03156       if (isset($form[$key]['deleted']['#value']) && $form[$key]['deleted']['#value']) {
03157         $styles[] = 'display: none;';
03158       }
03159       $rows[] = array('data' => $row, 'class' => $class, 'id' => 'display-row-' . $key, 'style' => $styles);
03160     }
03161   }
03162 
03163   $header = array(t('Display'), t('Weight'), t('Remove'));
03164   $output = '';
03165   drupal_add_tabledrag('reorder-displays', 'order', 'sibling', 'weight');
03166 
03167   $output = drupal_render($form['override']);
03168   $output .= theme('table',
03169     array('header' => $header,
03170     'rows' => $rows,
03171     'attributes' => array('id' => 'reorder-displays'),
03172   ));
03173   $output .= drupal_render_children($form);
03174 
03175   return $output;
03176 }
03177 
03181 function views_ui_edit_details_form($form, &$form_state) {
03182   $view = &$form_state['view'];
03183 
03184   $form['#title'] = t('View name and description');
03185   $form['#section'] = 'details';
03186 
03187   $form['details'] = array(
03188     '#theme_wrappers' => array('container'),
03189     '#attributes' => array('class' => array('scroll')),
03190   );
03191   $form['details']['human_name'] = array(
03192     '#type' => 'textfield',
03193     '#title' => t('Human-readable name'),
03194     '#description' => t('A descriptive human-readable name for this view. Spaces are allowed'),
03195     '#default_value' => $view->get_human_name(),
03196   );
03197   $form['details']['tag'] = array(
03198     '#type' => 'textfield',
03199     '#title' => t('View tag'),
03200     '#description' => t('Optionally, enter a comma delimited list of tags for this view to use in filtering and sorting views on the administrative page.'),
03201     '#default_value' => $view->tag,
03202     '#autocomplete_path' => 'admin/views/ajax/autocomplete/tag',
03203   );
03204   $form['details']['description'] = array(
03205     '#type' => 'textfield',
03206     '#title' => t('View description'),
03207     '#description' => t('This description will appear on the Views administrative UI to tell you what the view is about.'),
03208     '#default_value' => $view->description,
03209   );
03210 
03211   views_ui_standard_form_buttons($form, $form_state, 'views_ui_edit_details_form');
03212   return $form;
03213 }
03214 
03218 function views_ui_edit_details_form_submit($form, &$form_state) {
03219   $view = $form_state['view'];
03220   foreach ($form_state['values'] as $key => $value) {
03221     // Only save values onto the view if they're actual view properties
03222     // (as opposed to 'op' or 'form_build_id').
03223     if (isset($form['details'][$key])) {
03224       $view->$key = $value;
03225     }
03226   }
03227   $form_state['#page_title'] = views_ui_edit_page_title($view);
03228   views_ui_cache_set($view);
03229 }
03230 
03234 function views_ui_edit_display_form($form, &$form_state) {
03235   $view = &$form_state['view'];
03236   $display_id = $form_state['display_id'];
03237   $section = $form_state['section'];
03238 
03239   if (!$view->set_display($display_id)) {
03240     views_ajax_error(t('Invalid display id @display', array('@display' => $display_id)));
03241   }
03242   $display = &$view->display[$display_id];
03243 
03244   // Get form from the handler.
03245   $form['options'] = array(
03246     '#theme_wrappers' => array('container'),
03247     '#attributes' => array('class' => array('scroll')),
03248   );
03249   $display->handler->options_form($form['options'], $form_state);
03250 
03251   // The handler options form sets $form['#title'], which we need on the entire
03252   // $form instead of just the ['options'] section.
03253   $form['#title'] = $form['options']['#title'];
03254   unset($form['options']['#title']);
03255 
03256   // Move the override dropdown out of the scrollable section of the form.
03257   if (isset($form['options']['override'])) {
03258     $form['override'] = $form['options']['override'];
03259     unset($form['options']['override']);
03260   }
03261 
03262   $name = NULL;
03263   if (isset($form_state['update_name'])) {
03264     $name = $form_state['update_name'];
03265   }
03266 
03267   views_ui_standard_form_buttons($form, $form_state, 'views_ui_edit_display_form', $name);
03268   return $form;
03269 }
03270 
03274 function views_ui_edit_display_form_validate($form, &$form_state) {
03275   $display = &$form_state['view']->display[$form_state['display_id']];
03276   $display->handler->options_validate($form['options'], $form_state);
03277 
03278   if (form_get_errors()) {
03279     $form_state['rerender'] = TRUE;
03280   }
03281 }
03282 
03286 function views_ui_edit_display_form_submit($form, &$form_state) {
03287   $display = &$form_state['view']->display[$form_state['display_id']];
03288   $display->handler->options_submit($form, $form_state);
03289 
03290   views_ui_cache_set($form_state['view']);
03291 }
03292 
03298 function views_ui_edit_display_form_override($form, &$form_state) {
03299   $display = &$form_state['view']->display[$form_state['display_id']];
03300   $display->handler->options_override($form, $form_state);
03301 
03302   views_ui_cache_set($form_state['view']);
03303   $form_state['rerender'] = TRUE;
03304   $form_state['rebuild'] = TRUE;
03305 }
03306 
03310 function views_ui_config_type_form($form, &$form_state) {
03311   $view = &$form_state['view'];
03312   $display_id = $form_state['display_id'];
03313   $type = $form_state['type'];
03314 
03315   $types = views_object_types();
03316   if (!$view->set_display($display_id)) {
03317     views_ajax_error(t('Invalid display id @display', array('@display' => $display_id)));
03318   }
03319   $display = &$view->display[$display_id];
03320   $form['#title'] = t('Configure @type', array('@type' => $types[$type]['ltitle']));
03321   $form['#section'] = $display_id . 'config-item';
03322 
03323   if ($display->handler->defaultable_sections($types[$type]['plural'])) {
03324     $form_state['section'] = $types[$type]['plural'];
03325     views_ui_standard_display_dropdown($form, $form_state, $form_state['section']);
03326   }
03327 
03328   if (!empty($types[$type]['options']) && function_exists($types[$type]['options'])) {
03329     $options = $type . '_options';
03330     $form[$options] = array('#tree' => TRUE);
03331     $types[$type]['options']($form, $form_state);
03332   }
03333 
03334   $name = NULL;
03335   if (isset($form_state['update_name'])) {
03336     $name = $form_state['update_name'];
03337   }
03338 
03339   views_ui_standard_form_buttons($form, $form_state, 'views_ui_config_type_form', $name);
03340   return $form;
03341 }
03342 
03346 function views_ui_config_type_form_submit($form, &$form_state) {
03347   $types = views_object_types();
03348   $display = &$form_state['view']->display[$form_state['display_id']];
03349 
03350   // Store in cache
03351   views_ui_cache_set($form_state['view']);
03352 }
03353 
03357 function views_ui_rearrange_form($form, &$form_state) {
03358   $view = &$form_state['view'];
03359   $display_id = $form_state['display_id'];
03360   $type = $form_state['type'];
03361 
03362   $types = views_object_types();
03363   if (!$view->set_display($display_id)) {
03364     views_ajax_error(t('Invalid display id @display', array('@display' => $display_id)));
03365   }
03366   $display = &$view->display[$display_id];
03367   $form['#title'] = t('Rearrange @type', array('@type' => $types[$type]['ltitle']));
03368   $form['#section'] = $display_id . 'rearrange-item';
03369 
03370   if ($display->handler->defaultable_sections($types[$type]['plural'])) {
03371     $form_state['section'] = $types[$type]['plural'];
03372     views_ui_standard_display_dropdown($form, $form_state, $form_state['section']);
03373   }
03374 
03375   $count = 0;
03376 
03377   // Get relationship labels
03378   $relationships = array();
03379   foreach ($display->handler->get_handlers('relationship') as $id => $handler) {
03380     $relationships[$id] = $handler->label();
03381     $handlers = $display->handler->get_option('relationships');
03382     if ($handlers) {
03383       foreach ($handlers as $id => $info) {
03384         $handler = $display->handler->get_handler('relationship', $id);
03385         $relationships[$id] = $handler->label();
03386       }
03387     }
03388   }
03389 
03390   // Filters can now be grouped so we do a little bit extra:
03391   $groups = array();
03392   $grouping = FALSE;
03393   if ($type == 'filter') {
03394     $group_info = $view->display_handler->get_option('filter_groups');
03395     if (!empty($group_info['groups']) && count($group_info['groups']) > 1) {
03396       $grouping = TRUE;
03397       $groups = array(0 => array());
03398     }
03399   }
03400 
03401   foreach ($display->handler->get_option($types[$type]['plural']) as $id => $field) {
03402     $form['fields'][$id] = array('#tree' => TRUE);
03403     $form['fields'][$id]['weight'] = array(
03404       '#type' => 'textfield',
03405       '#default_value' => ++$count,
03406     );
03407     $handler = $display->handler->get_handler($type, $id);
03408     if ($handler) {
03409       $name = $handler->ui_name() . ' ' . $handler->admin_summary();
03410       if (!empty($field['relationship']) && !empty($relationships[$field['relationship']])) {
03411         $name = '(' . $relationships[$field['relationship']] . ') ' . $name;
03412       }
03413 
03414       $form['fields'][$id]['name'] = array(
03415         '#markup' => $name,
03416       );
03417     }
03418     else {
03419       $form['fields'][$id]['name'] = array('#markup' => t('Broken field @id', array('@id' => $id)));
03420     }
03421     $form['fields'][$id]['removed'] = array(
03422       '#type' => 'checkbox',
03423       '#id' => 'views-removed-' . $id,
03424       '#attributes' => array('class' => array('views-remove-checkbox')),
03425       '#default_value' => 0,
03426     );
03427   }
03428 
03429   // Add javascript settings that will be added via $.extend for tabledragging
03430   $form['#js']['tableDrag']['arrange']['weight'][0] = array(
03431     'target' => 'weight',
03432     'source' => NULL,
03433     'relationship' => 'sibling',
03434     'action' => 'order',
03435     'hidden' => TRUE,
03436     'limit' => 0,
03437   );
03438 
03439   $name = NULL;
03440   if (isset($form_state['update_name'])) {
03441     $name = $form_state['update_name'];
03442   }
03443 
03444   views_ui_standard_form_buttons($form, $form_state, 'views_ui_rearrange_form');
03445   return $form;
03446 }
03447 
03451 function theme_views_ui_rearrange_form($variables) {
03452   $form = $variables['form'];
03453 
03454   $rows = array();
03455   foreach (element_children($form['fields']) as $id) {
03456     if (isset($form['fields'][$id]['name'])) {
03457       $row = array();
03458       $row[] = drupal_render($form['fields'][$id]['name']);
03459       $form['fields'][$id]['weight']['#attributes']['class'] = array('weight');
03460       $row[] = drupal_render($form['fields'][$id]['weight']);
03461       $row[] = drupal_render($form['fields'][$id]['removed']) . l('<span>' . t('Remove') . '</span>', 'javascript:void()', array('attributes' => array('id' => 'views-remove-link-' . $id, 'class' => array('views-hidden', 'views-button-remove', 'views-remove-link'), 'alt' => t('Remove this item'), 'title' => t('Remove this item')), 'html' => TRUE));
03462       $rows[] = array('data' => $row, 'class' => array('draggable'), 'id' => 'views-row-' . $id);
03463     }
03464   }
03465   if (empty($rows)) {
03466     $rows[] = array(array('data' => t('No fields available.'), 'colspan' => '2'));
03467   }
03468 
03469   $header = array('', t('Weight'), t('Remove'));
03470   $output = drupal_render($form['override']);
03471   $output .= '<div class="scroll">';
03472   $output .= theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'arrange')));
03473   $output .= '</div>';
03474   $output .= drupal_render_children($form);
03475   drupal_add_tabledrag('arrange', 'order', 'sibling', 'weight');
03476 
03477   return $output;
03478 }
03479 
03483 function theme_views_ui_expose_filter_form($variables) {
03484   $form = $variables['form'];
03485   $more = drupal_render($form['more']);
03486 
03487   $output = drupal_render($form['form_description']);
03488   $output .= drupal_render($form['expose_button']);
03489   if (isset($form['required'])) {
03490     $output .= drupal_render($form['required']);
03491   }
03492   $output .= drupal_render($form['label']);
03493 
03494   $output .= drupal_render($form['operator']);
03495   $output .= drupal_render($form['value']);
03496 
03497   if (isset($form['use_operator'])) {
03498     $output .= '<div class="views-left-40">';
03499     $output .= drupal_render($form['use_operator']);
03500     $output .= '</div>';
03501   }
03502 
03503   // Only output the right column markup if there's a left column to begin with
03504   if (!empty($form['operator']['#type'])) {
03505     $output .= '<div class="views-right-60">';
03506     $output .= drupal_render_children($form);
03507     $output .= '</div>';
03508   }
03509   else {
03510     $output .= drupal_render_children($form);
03511   }
03512 
03513   $output .= $more;
03514 
03515   return $output;
03516 }
03517 
03521 function views_ui_rearrange_form_submit($form, &$form_state) {
03522   $types = views_object_types();
03523   $display = &$form_state['view']->display[$form_state['display_id']];
03524 
03525   $old_fields = $display->handler->get_option($types[$form_state['type']]['plural']);
03526   $new_fields = $order = array();
03527 
03528   // Make an array with the weights
03529   foreach ($form_state['values'] as $field => $info) {
03530     // add each value that is a field with a weight to our list, but only if
03531     // it has had its 'removed' checkbox checked.
03532     if (is_array($info) && isset($info['weight']) && empty($info['removed'])) {
03533       $order[$field] = $info['weight'];
03534     }
03535   }
03536 
03537   // Sort the array
03538   asort($order);
03539 
03540   // Create a new list of fields in the new order.
03541   foreach (array_keys($order) as $field) {
03542     $new_fields[$field] = $old_fields[$field];
03543   }
03544   $display->handler->set_option($types[$form_state['type']]['plural'], $new_fields);
03545 
03546   // Store in cache
03547   views_ui_cache_set($form_state['view']);
03548 }
03549 
03553 function views_ui_rearrange_filter_form($form, &$form_state) {
03554   $view = &$form_state['view'];
03555   $display_id = $form_state['display_id'];
03556   $type = $form_state['type'];
03557 
03558   $types = views_object_types();
03559   if (!$view->set_display($display_id)) {
03560     views_ajax_render(t('Invalid display id @display', array('@display' => $display_id)));
03561   }
03562   $display = &$view->display[$display_id];
03563   $form['#title'] = check_plain($display->display_title) . ': ';
03564   $form['#title'] .= t('Rearrange @type', array('@type' => $types[$type]['ltitle']));
03565   $form['#section'] = $display_id . 'rearrange-item';
03566 
03567   if ($display->handler->defaultable_sections($types[$type]['plural'])) {
03568     $form_state['section'] = $types[$type]['plural'];
03569     views_ui_standard_display_dropdown($form, $form_state, $form_state['section']);
03570   }
03571 
03572   if (!empty($view->form_cache)) {
03573     $groups = $view->form_cache['groups'];
03574     $handlers = $view->form_cache['handlers'];
03575   }
03576   else {
03577     $groups = $display->handler->get_option('filter_groups');
03578     $handlers = $display->handler->get_option($types[$type]['plural']);
03579   }
03580   $count = 0;
03581 
03582   // Get relationship labels
03583   $relationships = array();
03584   foreach ($display->handler->get_handlers('relationship') as $id => $handler) {
03585     $relationships[$id] = $handler->label();
03586   }
03587 
03588   $group_options = array();
03589 
03600   $grouping = count(array_keys($groups['groups'])) > 1;
03601 
03602   $form['filter_groups']['#tree'] = TRUE;
03603   $form['filter_groups']['operator'] = array(
03604     '#type' => 'select',
03605     '#options' => array (
03606       'AND' => t('And'),
03607       'OR' => t('Or'),
03608     ),
03609     '#default_value' => $groups['operator'],
03610     '#attributes' => array(
03611       'class' => array('warning-on-change'),
03612     ),
03613     '#title' => t('Operator to use on all groups'),
03614     '#description' => t('Either "group 0 AND group 1 AND group 2" or "group 0 OR group 1 OR group 2", etc'),
03615     '#access' => $grouping,
03616   );
03617 
03618   $form['remove_groups']['#tree'] = TRUE;
03619 
03620   foreach ($groups['groups'] as $id => $group) {
03621     $form['filter_groups']['groups'][$id] = array(
03622       '#title' => t('Operator'),
03623       '#type' => 'select',
03624       '#options' => array(
03625         'AND' => t('And'),
03626         'OR' => t('Or'),
03627       ),
03628       '#default_value' => $group,
03629       '#attributes' => array(
03630         'class' => array('warning-on-change'),
03631       ),
03632     );
03633 
03634     $form['remove_groups'][$id] = array(); // to prevent a notice
03635     if ($id != 1) {
03636       $form['remove_groups'][$id] = array(
03637         '#type' => 'submit',
03638         '#value' => t('Remove group @group', array('@group' => $id)),
03639         '#id' => "views-remove-group-$id",
03640         '#attributes' => array(
03641           'class' => array('views-remove-group'),
03642         ),
03643         '#group' => $id,
03644       );
03645     }
03646     $group_options[$id] = $id == 1 ? t('Default group') : t('Group @group', array('@group' => $id));
03647     $form['#group_renders'][$id] = array();
03648   }
03649 
03650   $form['#group_options'] = $group_options;
03651   $form['#groups'] = $groups;
03652   // We don't use get_handlers() because we want items without handlers to
03653   // appear and show up as 'broken' so that the user can see them.
03654   $form['filters'] = array('#tree' => TRUE);
03655   foreach ($handlers as $id => $field) {
03656     // If the group does not exist, move the filters to the default group.
03657     if (empty($field['group']) || empty($groups['groups'][$field['group']])) {
03658       $field['group'] = 1;
03659     }
03660 
03661     $handler = $display->handler->get_handler($type, $id);
03662     if ($grouping && $handler && !$handler->can_group()) {
03663       $field['group'] = 'ungroupable';
03664     }
03665 
03666     // If not grouping and the handler is set ungroupable, move it back to
03667     // the default group to prevent weird errors from having it be in its
03668     // own group:
03669     if (!$grouping && $field['group'] == 'ungroupable') {
03670       $field['group'] = 1;
03671     }
03672 
03673     // Place this item into the proper group for rendering.
03674     $form['#group_renders'][$field['group']][] = $id;
03675 
03676     $form['filters'][$id]['weight'] = array(
03677       '#type' => 'textfield',
03678       '#default_value' => ++$count,
03679       '#size' => 8,
03680     );
03681     $form['filters'][$id]['group'] = array(
03682       '#type' => 'select',
03683       '#options' => $group_options,
03684       '#default_value' => $field['group'],
03685       '#attributes' => array(
03686         'class' => array('views-region-select', 'views-region-' . $id),
03687       ),
03688       '#access' => $field['group'] !== 'ungroupable',
03689     );
03690 
03691     if ($handler) {
03692       $name = $handler->ui_name() . ' ' . $handler->admin_summary();
03693       if (!empty($field['relationship']) && !empty($relationships[$field['relationship']])) {
03694         $name = '(' . $relationships[$field['relationship']] . ') ' . $name;
03695       }
03696 
03697       $form['filters'][$id]['name'] = array(
03698         '#markup' => $name,
03699       );
03700     }
03701     else {
03702       $form['filters'][$id]['name'] = array('#markup' => t('Broken field @id', array('@id' => $id)));
03703     }
03704     $form['filters'][$id]['removed'] = array(
03705       '#type' => 'checkbox',
03706       '#id' => 'views-removed-' . $id,
03707       '#attributes' => array('class' => array('views-remove-checkbox')),
03708       '#default_value' => 0,
03709     );
03710   }
03711 
03712   if (isset($form_state['update_name'])) {
03713     $name = $form_state['update_name'];
03714   }
03715 
03716   views_ui_standard_form_buttons($form, $form_state, 'views_ui_rearrange_filter_form');
03717   $form['buttons']['add_group'] = array(
03718     '#type' => 'submit',
03719     '#value' => t('Create new filter group'),
03720     '#id' => 'views-add-group',
03721     '#group' => 'add',
03722   );
03723 
03724   return $form;
03725 }
03726 
03730 function theme_views_ui_rearrange_filter_form(&$vars) {
03731   $form = $vars['form'];
03732   $rows = $ungroupable_rows = array();
03733   // Enable grouping only if > 1 group.
03734   $grouping = count(array_keys($form['#group_options'])) > 1;
03735 
03736   foreach ($form['#group_renders'] as $group_id => $contents) {
03737     // Header row for the group.
03738     if ($group_id !== 'ungroupable') {
03739       // Set up tabledrag so that it changes the group dropdown when rows are
03740       // dragged between groups.
03741       drupal_add_tabledrag('views-rearrange-filters', 'match', 'sibling', 'views-group-select', 'views-group-select-' . $group_id);
03742 
03743       // Title row, spanning all columns.
03744       $row = array();
03745       // Add a cell to the first row, containing the group operator.
03746       $row[] = array('class' => array('group', 'group-operator', 'container-inline'), 'data' => drupal_render($form['filter_groups']['groups'][$group_id]), 'rowspan' => max(array(2, count($contents) + 1)));
03747       // Title.
03748       $row[] = array('class' => array('group', 'group-title'), 'data' => '<span>' . $form['#group_options'][$group_id] . '</span>', 'colspan' => 4);
03749       $rows[] = array('class' => array('views-group-title'), 'data' => $row, 'id' => 'views-group-title-' . $group_id);
03750 
03751       // Row which will only appear if the group has nothing in it.
03752       $row = array();
03753       $class = 'group-' . (count($contents) ? 'populated' : 'empty');
03754       $instructions = '<span>' . t('No filters have been added.') . '</span> <span class="js-only">' . t('Drag to add filters.') . '</span>';
03755       // When JavaScript is enabled, the button for removing the group (if it's
03756       // present) should be hidden, since it will be replaced by a link on the
03757       // client side.
03758       if (!empty($form['remove_groups'][$group_id]['#type']) && $form['remove_groups'][$group_id]['#type'] == 'submit') {
03759         $form['remove_groups'][$group_id]['#attributes']['class'][] = 'js-hide';
03760       }
03761       $row[] = array('colspan' => 5, 'data' => $instructions . drupal_render($form['remove_groups'][$group_id]));
03762       $rows[] = array('class' => array("group-message", "group-$group_id-message", $class), 'data' => $row, 'id' => 'views-group-' . $group_id);
03763     }
03764 
03765     foreach ($contents as $id) {
03766       if (isset($form['filters'][$id]['name'])) {
03767         $row = array();
03768         $row[] = drupal_render($form['filters'][$id]['name']);
03769         $form['filters'][$id]['weight']['#attributes']['class'] = array('weight');
03770         $row[] = drupal_render($form['filters'][$id]['weight']);
03771         $form['filters'][$id]['group']['#attributes']['class'] = array('views-group-select views-group-select-' . $group_id);
03772         $row[] = drupal_render($form['filters'][$id]['group']);
03773         $form['filters'][$id]['removed']['#attributes']['class'][] = 'js-hide';
03774         $row[] = drupal_render($form['filters'][$id]['removed']) . l('<span>' . t('Remove') . '</span>', 'javascript:void()', array('attributes' => array('id' => 'views-remove-link-' . $id, 'class' => array('views-hidden', 'views-button-remove', 'views-groups-remove-link', 'views-remove-link'), 'alt' => t('Remove this item'), 'title' => t('Remove this item')), 'html' => true));
03775 
03776         $row = array('data' => $row, 'class' => array('draggable'), 'id' => 'views-row-' . $id);
03777         if ($group_id !== 'ungroupable') {
03778           $rows[] = $row;
03779         }
03780         else {
03781           $ungroupable_rows[] = $row;
03782         }
03783       }
03784     }
03785   }
03786   if (empty($rows)) {
03787     $rows[] = array(array('data' => t('No fields available.'), 'colspan' => '2'));
03788   }
03789 
03790   $output = drupal_render($form['override']);
03791   $output .= '<div class="scroll">';
03792   if ($grouping) {
03793     $output .= drupal_render($form['filter_groups']['operator']);
03794   }
03795   else {
03796     $form['filter_groups']['groups'][0]['#title'] = t('Operator');
03797     $output .= drupal_render($form['filter_groups']['groups'][0]);
03798   }
03799 
03800   if (!empty($ungroupable_rows)) {
03801     drupal_add_tabledrag('views-rearrange-filters-ungroupable', 'order', 'sibling', 'weight');
03802     $header = array(t('Ungroupable filters'), t('Weight'), array('class' => array('views-hide-label'), 'data' => t('Group')), array('class' => array('views-hide-label'), 'data' => t('Remove')));
03803     $output .= theme('table', array('header' => $header, 'rows' => $ungroupable_rows, 'attributes' => array('id' => 'views-rearrange-filters-ungroupable', 'class' => array('arrange'))));
03804   }
03805 
03806   // Set up tabledrag so that the weights are changed when rows are dragged.
03807   drupal_add_tabledrag('views-rearrange-filters', 'order', 'sibling', 'weight');
03808   $output .= theme('table', array('rows' => $rows, 'attributes' => array('id' => 'views-rearrange-filters', 'class' => array('arrange'))));
03809   $output .= '</div>';
03810 
03811   // When JavaScript is enabled, the button for adding a new group should be
03812   // hidden, since it will be replaced by a link on the client side.
03813   $form['buttons']['add_group']['#attributes']['class'][] = 'js-hide';
03814 
03815   // Render the rest of the form and return.
03816   $output .= drupal_render_children($form);
03817   return $output;
03818 }
03819 
03823 function views_ui_rearrange_filter_form_submit($form, &$form_state) {
03824   $types = views_object_types();
03825   $display = &$form_state['view']->display[$form_state['display_id']];
03826   $remember_groups = array();
03827 
03828   if (!empty($form_state['view']->form_cache)) {
03829     $old_fields = $form_state['view']->form_cache['handlers'];
03830   }
03831   else {
03832     $old_fields = $display->handler->get_option($types[$form_state['type']]['plural']);
03833   }
03834   $count = 0;
03835 
03836   $groups = $form_state['values']['filter_groups'];
03837   // Whatever button was clicked, re-calculate field information.
03838   $new_fields = $order = array();
03839 
03840   // Make an array with the weights
03841   foreach ($form_state['values']['filters'] as $field => $info) {
03842     // add each value that is a field with a weight to our list, but only if
03843     // it has had its 'removed' checkbox checked.
03844     if (is_array($info) && empty($info['removed'])) {
03845       if (isset($info['weight'])) {
03846         $order[$field] = $info['weight'];
03847       }
03848 
03849       if (isset($info['group'])) {
03850         $old_fields[$field]['group'] = $info['group'];
03851         $remember_groups[$info['group']][] = $field;
03852       }
03853     }
03854   }
03855 
03856   // Sort the array
03857   asort($order);
03858 
03859   // Create a new list of fields in the new order.
03860   foreach (array_keys($order) as $field) {
03861     $new_fields[$field] = $old_fields[$field];
03862   }
03863 
03864   // If the #group property is set on the clicked button, that means we are
03865   // either adding or removing a group, not actually updating the filters.
03866   if (!empty($form_state['clicked_button']['#group'])) {
03867     if ($form_state['clicked_button']['#group'] == 'add') {
03868       // Add a new group
03869       $groups['groups'][] = 'AND';
03870     }
03871     else {
03872       // Renumber groups above the removed one down.
03873       foreach (array_keys($groups['groups']) as $group_id) {
03874         if ($group_id >= $form_state['clicked_button']['#group']) {
03875           $old_group = $group_id + 1;
03876           if (isset($groups['groups'][$old_group])) {
03877             $groups['groups'][$group_id] = $groups['groups'][$old_group];
03878             if (isset($remember_groups[$old_group])) {
03879               foreach ($remember_groups[$old_group] as $id) {
03880                 $new_fields[$id]['group'] = $group_id;
03881               }
03882             }
03883           }
03884           else {
03885             // If this is the last one, just unset it.
03886             unset($groups['groups'][$group_id]);
03887           }
03888         }
03889       }
03890     }
03891     // Update our cache with values so that cancel still works the way
03892     // people expect.
03893     $form_state['view']->form_cache = array(
03894       'key' => 'rearrange-filter',
03895       'groups' => $groups,
03896       'handlers' => $new_fields,
03897     );
03898 
03899     // Return to this form except on actual Update.
03900     views_ui_add_form_to_stack('rearrange-filter', $form_state['view'], $form_state['display_id'], array($form_state['type']));
03901   }
03902   else {
03903     // The actual update button was clicked. Remove the empty groups, and
03904     // renumber them sequentially.
03905     ksort($remember_groups);
03906     $groups['groups'] = views_array_key_plus(array_values(array_intersect_key($groups['groups'], $remember_groups)));
03907     // Change the 'group' key on each field to match. Here, $mapping is an
03908     // array whose keys are the old group numbers and whose values are the new
03909     // (sequentially numbered) ones.
03910     $mapping = array_flip(views_array_key_plus(array_keys($remember_groups)));
03911     foreach ($new_fields as &$new_field) {
03912       $new_field['group'] = $mapping[$new_field['group']];
03913     }
03914 
03915     // Write the changed handler values.
03916     $display->handler->set_option($types[$form_state['type']]['plural'], $new_fields);
03917     $display->handler->set_option('filter_groups', $groups);
03918     if (isset($form_state['view']->form_cache)) {
03919       unset($form_state['view']->form_cache);
03920     }
03921   }
03922 
03923   // Store in cache.
03924   views_ui_cache_set($form_state['view']);
03925 }
03926 
03930 function views_ui_add_item_form($form, &$form_state) {
03931   $view = &$form_state['view'];
03932   $display_id = $form_state['display_id'];
03933   $type = $form_state['type'];
03934 
03935   $form = array(
03936     'options' => array(
03937       '#theme_wrappers' => array('container'),
03938       '#attributes' => array('class' => array('scroll')),
03939     ),
03940   );
03941 
03942   ctools_add_js('dependent');
03943 
03944   if (!$view->set_display($display_id)) {
03945     views_ajax_error(t('Invalid display id @display', array('@display' => $display_id)));
03946   }
03947   $display = &$view->display[$display_id];
03948 
03949   $types = views_object_types();
03950   $ltitle = $types[$type]['ltitle'];
03951   $section = $types[$type]['plural'];
03952 
03953   if (!empty($types[$type]['type'])) {
03954     $type = $types[$type]['type'];
03955   }
03956 
03957   $form['#title'] = t('Add @type', array('@type' => $ltitle));
03958   $form['#section'] = $display_id . 'add-item';
03959 
03960 
03961   // Add the display override dropdown.
03962   views_ui_standard_display_dropdown($form, $form_state, $section);
03963 
03964   // Figure out all the base tables allowed based upon what the relationships provide.
03965   $base_tables = $view->get_base_tables();
03966   $options = views_fetch_fields(array_keys($base_tables), $type, $display->handler->use_group_by());
03967 
03968   if (!empty($options)) {
03969     $form['options']['controls'] = array(
03970       '#theme_wrappers' => array('container'),
03971       '#id' => 'views-filterable-options-controls',
03972       '#attributes' => array('class' => array('container-inline')),
03973     );
03974     $form['options']['controls']['options_search'] = array(
03975       '#type' => 'textfield',
03976       '#title' => t('Search'),
03977     );
03978 
03979     $groups = array('all' => t('- All -'));
03980     $form['options']['controls']['group'] = array(
03981       '#type' => 'select',
03982       '#title' => t('Filter'),
03983       '#options' => array(),
03984       '#attributes' => array('class' => array('ctools-master-dependent')),
03985     );
03986 
03987     $form['options']['name'] = array(
03988       '#prefix' => '<div class="views-radio-box form-checkboxes views-filterable-options">',
03989       '#suffix' => '</div>',
03990       '#tree' => TRUE,
03991       '#default_value' => 'all',
03992     );
03993 
03994     // Group options first to simplify the DOM objects that Views
03995     // dependent JS will act upon.
03996     $grouped_options = array();
03997     foreach ($options as $key => $option) {
03998       $group = preg_replace('/[^a-z0-9]/', '-', strtolower($option['group']));
03999       $groups[$group] = $option['group'];
04000       $grouped_options[$group][$key] = $option;
04001       if (!empty($option['aliases']) && is_array($option['aliases'])) {
04002         foreach ($option['aliases'] as $id => $alias) {
04003           if (empty($alias['base']) || !empty($base_tables[$alias['base']])) {
04004             $copy = $option;
04005             $copy['group'] = $alias['group'];
04006             $copy['title'] = $alias['title'];
04007             if (isset($alias['help'])) {
04008               $copy['help'] = $alias['help'];
04009             }
04010 
04011             $group = preg_replace('/[^a-z0-9]/', '-', strtolower($copy['group']));
04012             $groups[$group] = $copy['group'];
04013             $grouped_options[$group][$key . '$' . $id] = $copy;
04014           }
04015         }
04016       }
04017     }
04018 
04019     foreach ($grouped_options as $group => $group_options) {
04020       $form['options']['name'][$group . '_start']['#markup'] = '<div class="ctools-dependent-all ctools-dependent-' . $group . '">';
04021       $zebra = 0;
04022       foreach ($group_options as $key => $option) {
04023         $zebra_class = ($zebra % 2) ? 'odd' : 'even';
04024         $form['options']['name'][$key] = array(
04025           '#type' => 'checkbox',
04026           '#title' => t('!group: !field', array('!group' => $option['group'], '!field' => $option['title'])),
04027           '#description' => $option['help'],
04028           '#return_value' => $key,
04029           '#prefix' => "<div class='$zebra_class filterable-option'>",
04030           '#suffix' => '</div>',
04031         );
04032         $zebra++;
04033       }
04034       $form['options']['name'][$group . '_end']['#markup'] = '</div>';
04035     }
04036 
04037     $form['options']['controls']['group']['#options'] = $groups;
04038   }
04039   else {
04040     $form['options']['markup'] = array(
04041       '#markup' => '<div class="form-item">' . t('There are no @types available to add.', array('@types' =>  $ltitle)) . '</div>',
04042     );
04043   }
04044   // Add a div to show the selected items
04045   $form['selected'] = array(
04046     '#type' => 'item',
04047     '#markup' => '<div class="views-selected-options"></div>',
04048     '#title' => t('Selected') . ':',
04049     '#theme_wrappers' => array('form_element', 'views_container'),
04050     '#attributes' => array('class' => array('container-inline', 'views-add-form-selected')),
04051   );
04052   ctools_include('dependent');
04053   views_ui_standard_form_buttons($form, $form_state, 'views_ui_add_item_form', t('Add and configure @types', array('@types' => $ltitle)));
04054 
04055   // Remove the default submit function.
04056   $form['buttons']['submit']['#submit'] = array_diff($form['buttons']['submit']['#submit'], array('views_ui_standard_submit'));
04057   $form['buttons']['submit']['#submit'][] = 'views_ui_add_item_form_submit';
04058 
04059   return $form;
04060 }
04061 
04065 function views_ui_add_item_form_submit($form, &$form_state) {
04066   $type = $form_state['type'];
04067   $types = views_object_types();
04068   $section = $types[$type]['plural'];
04069 
04070   // Handle the override select.
04071   list($was_defaulted, $is_defaulted) = views_ui_standard_override_values($form, $form_state);
04072   if ($was_defaulted && !$is_defaulted) {
04073     // We were using the default display's values, but we're now overriding
04074     // the default display and saving values specific to this display.
04075     $display = &$form_state['view']->display[$form_state['display_id']];
04076     // set_override toggles the override of this section.
04077     $display->handler->set_override($section);
04078   }
04079   elseif (!$was_defaulted && $is_defaulted) {
04080     // We used to have an override for this display, but the user now wants
04081     // to go back to the default display.
04082     // Overwrite the default display with the current form values, and make
04083     // the current display use the new default values.
04084     $display = &$form_state['view']->display[$form_state['display_id']];
04085     // options_override toggles the override of this section.
04086     $display->handler->set_override($section);
04087   }
04088 
04089   if (!empty($form_state['values']['name']) && is_array($form_state['values']['name'])) {
04090     // Loop through each of the items that were checked and add them to the view.
04091     foreach (array_keys(array_filter($form_state['values']['name'])) as $field) {
04092       list($table, $field) = explode('.', $field, 2);
04093 
04094       if ($cut = strpos($field, '$')) {
04095         $field = substr($field, 0, $cut);
04096       }
04097       $id = $form_state['view']->add_item($form_state['display_id'], $type, $table, $field);
04098 
04099       // check to see if we have group by settings
04100       $key = $type;
04101       // Footer,header and empty text have a different internal handler type(area).
04102       if (isset($types[$type]['type'])) {
04103         $key = $types[$type]['type'];
04104       }
04105       $handler = views_get_handler($table, $field, $key);
04106       if ($form_state['view']->display_handler->use_group_by() && $handler->use_group_by()) {
04107         views_ui_add_form_to_stack('config-item-group', $form_state['view'], $form_state['display_id'], array($type, $id));
04108       }
04109 
04110       // check to see if this type has settings, if so add the settings form first
04111       if ($handler && $handler->has_extra_options()) {
04112         views_ui_add_form_to_stack('config-item-extra', $form_state['view'], $form_state['display_id'], array($type, $id));
04113       }
04114       // Then add the form to the stack
04115       views_ui_add_form_to_stack('config-item', $form_state['view'], $form_state['display_id'], array($type, $id));
04116     }
04117   }
04118 
04119   if (isset($form_state['view']->form_cache)) {
04120     unset($form_state['view']->form_cache);
04121   }
04122 
04123   // Store in cache
04124   views_ui_cache_set($form_state['view']);
04125 }
04126 
04127 
04131 function views_ui_config_item_form($form, &$form_state) {
04132   $view = &$form_state['view'];
04133   $display_id = $form_state['display_id'];
04134   $type = $form_state['type'];
04135   $id = $form_state['id'];
04136 
04137   $form = array(
04138     'options' => array(
04139       '#tree' => TRUE,
04140       '#theme_wrappers' => array('container'),
04141       '#attributes' => array('class' => array('scroll')),
04142     ),
04143   );
04144   if (!$view->set_display($display_id)) {
04145     views_ajax_error(t('Invalid display id @display', array('@display' => $display_id)));
04146   }
04147   $item = $view->get_item($display_id, $type, $id);
04148 
04149   if ($item) {
04150     $handler = $view->display_handler->get_handler($type, $id);
04151     if (empty($handler)) {
04152       $form['markup'] = array('#markup' => t("Error: handler for @table > @field doesn't exist!", array('@table' => $item['table'], '@field' => $item['field'])));
04153     }
04154     else {
04155       $types = views_object_types();
04156 
04157       // If this item can come from the default display, show a dropdown
04158       // that lets the user choose which display the changes should apply to.
04159       if ($view->display_handler->defaultable_sections($types[$type]['plural'])) {
04160         $form_state['section'] = $types[$type]['plural'];
04161         views_ui_standard_display_dropdown($form, $form_state, $form_state['section']);
04162       }
04163 
04164       // A whole bunch of code to figure out what relationships are valid for
04165       // this item.
04166       $relationships = $view->display_handler->get_option('relationships');
04167       $relationship_options = array();
04168 
04169       foreach ($relationships as $relationship) {
04170         // relationships can't link back to self. But also, due to ordering,
04171         // relationships can only link to prior relationships.
04172         if ($type == 'relationship' && $id == $relationship['id']) {
04173           break;
04174         }
04175         $relationship_handler = views_get_handler($relationship['table'], $relationship['field'], 'relationship');
04176         // ignore invalid/broken relationships.
04177         if (empty($relationship_handler)) {
04178           continue;
04179         }
04180 
04181         // If this relationship is valid for this type, add it to the list.
04182         $data = views_fetch_data($relationship['table']);
04183         $base = $data[$relationship['field']]['relationship']['base'];
04184         $base_fields = views_fetch_fields($base, $form_state['type'], $view->display_handler->use_group_by());
04185         if (isset($base_fields[$item['table'] . '.' . $item['field']])) {
04186           $relationship_handler->init($view, $relationship);
04187           $relationship_options[$relationship['id']] = $relationship_handler->label();
04188         }
04189       }
04190 
04191       if (!empty($relationship_options)) {
04192         // Make sure the existing relationship is even valid. If not, force
04193         // it to none.
04194         $base_fields = views_fetch_fields($view->base_table, $form_state['type'], $view->display_handler->use_group_by());
04195         if (isset($base_fields[$item['table'] . '.' . $item['field']])) {
04196           $relationship_options = array_merge(array('none' => t('Do not use a relationship')), $relationship_options);
04197         }
04198         $rel = empty($item['relationship']) ? 'none' : $item['relationship'];
04199         if (empty($relationship_options[$rel])) {
04200           // Pick the first relationship.
04201           $rel = key($relationship_options);
04202           // We want this relationship option to get saved even if the user
04203           // skips submitting the form.
04204           $view->set_item_option($display_id, $type, $id, 'relationship', $rel);
04205           $temp_view = $view->clone_view();
04206           views_ui_cache_set($temp_view);
04207         }
04208 
04209         $form['options']['relationship'] = array(
04210           '#type' => 'select',
04211           '#title' => t('Relationship'),
04212           '#options' => $relationship_options,
04213           '#default_value' => $rel,
04214           '#weight' => -500,
04215         );
04216       }
04217       else {
04218         $form['options']['relationship'] = array(
04219           '#type' => 'value',
04220           '#value' => 'none',
04221         );
04222       }
04223 
04224       $form['#title'] = t('Configure @type: @item', array('@type' => $types[$type]['lstitle'], '@item' => $handler->ui_name()));
04225 
04226       if (!empty($handler->definition['help'])) {
04227         $form['options']['form_description'] = array(
04228           '#markup' => $handler->definition['help'],
04229           '#theme_wrappers' => array('container'),
04230           '#attributes' => array('class' => array('form-item description')),
04231           '#weight' => -1000,
04232         );
04233       }
04234 
04235       $form['#section'] = $display_id . '-' . $type . '-' . $id;
04236 
04237       // Get form from the handler.
04238       $handler->options_form($form['options'], $form_state);
04239       $form_state['handler'] = &$handler;
04240     }
04241 
04242     $name = NULL;
04243     if (isset($form_state['update_name'])) {
04244       $name = $form_state['update_name'];
04245     }
04246 
04247     views_ui_standard_form_buttons($form, $form_state, 'views_ui_config_item_form', $name, t('Remove'), 'remove');
04248     // Only validate the override values, because this values are required for
04249     // the override selection.
04250     $form['buttons']['remove']['#limit_validation_errors'] = array(array('override'));
04251   }
04252 
04253   return $form;
04254 }
04255 
04259 function views_ui_config_item_form_validate($form, &$form_state) {
04260   $form_state['handler']->options_validate($form['options'], $form_state);
04261 
04262   if (form_get_errors()) {
04263     $form_state['rerender'] = TRUE;
04264   }
04265 }
04266 
04271 function views_ui_config_item_form_submit_temporary($form, &$form_state) {
04272   // Run it through the handler's submit function.
04273   $form_state['handler']->options_submit($form['options'], $form_state);
04274   $item = $form_state['handler']->options;
04275   $types = views_object_types();
04276 
04277   // For footer/header $handler_type is area but $type is footer/header.
04278   // For all other handle types it's the same.
04279   $handler_type = $type = $form_state['type'];
04280   if (!empty($types[$type]['type'])) {
04281     $handler_type = $types[$type]['type'];
04282   }
04283 
04284   $override = NULL;
04285   if ($form_state['view']->display_handler->use_group_by() && !empty($item['group_type'])) {
04286     if (empty($form_state['view']->query)) {
04287       $form_state['view']->init_query();
04288     }
04289     $aggregate = $form_state['view']->query->get_aggregation_info();
04290     if (!empty($aggregate[$item['group_type']]['handler'][$type])) {
04291       $override = $aggregate[$item['group_type']]['handler'][$type];
04292     }
04293   }
04294 
04295   // Create a new handler and unpack the options from the form onto it. We
04296   // can use that for storage.
04297   $handler = views_get_handler($item['table'], $item['field'], $handler_type, $override);
04298   $handler->init($form_state['view'], $item);
04299 
04300 
04301   // Add the incoming options to existing options because items using
04302   // the extra form may not have everything in the form here.
04303   $options = $form_state['values']['options'] + $form_state['handler']->options;
04304 
04305   // This unpacks only options that are in the definition, ensuring random
04306   // extra stuff on the form is not sent through.
04307   $handler->unpack_options($handler->options, $options, NULL, FALSE);
04308 
04309   // Store the item back on the view
04310   $form_state['view']->temporary_options[$type][$form_state['id']] = $handler->options;
04311 
04312   // @todo: Figure out whether views_ui_ajax_form is perhaps the better place to fix the issue.
04313   // views_ui_ajax_form() drops the current form from the stack, even if it's an #ajax.
04314   // So add the item back to the top of the stack.
04315   views_ui_add_form_to_stack($form_state['form_key'], $form_state['view'], $form_state['display_id'], array($type, $item['id']), TRUE);
04316 
04317   $form_state['rerender'] = TRUE;
04318   $form_state['rebuild'] = TRUE;
04319   // Write to cache
04320   views_ui_cache_set($form_state['view']);
04321 }
04322 
04326 function views_ui_config_item_form_submit($form, &$form_state) {
04327   // Run it through the handler's submit function.
04328   $form_state['handler']->options_submit($form['options'], $form_state);
04329   $item = $form_state['handler']->options;
04330   $types = views_object_types();
04331 
04332   // For footer/header $handler_type is area but $type is footer/header.
04333   // For all other handle types it's the same.
04334   $handler_type = $type = $form_state['type'];
04335   if (!empty($types[$type]['type'])) {
04336     $handler_type = $types[$type]['type'];
04337   }
04338 
04339   $override = NULL;
04340   if ($form_state['view']->display_handler->use_group_by() && !empty($item['group_type'])) {
04341     if (empty($form_state['view']->query)) {
04342       $form_state['view']->init_query();
04343     }
04344     $aggregate = $form_state['view']->query->get_aggregation_info();
04345     if (!empty($aggregate[$item['group_type']]['handler'][$type])) {
04346       $override = $aggregate[$item['group_type']]['handler'][$type];
04347     }
04348   }
04349 
04350   // Create a new handler and unpack the options from the form onto it. We
04351   // can use that for storage.
04352   $handler = views_get_handler($item['table'], $item['field'], $handler_type, $override);
04353   $handler->init($form_state['view'], $item);
04354 
04355 
04356   // Add the incoming options to existing options because items using
04357   // the extra form may not have everything in the form here.
04358   $options = $form_state['values']['options'] + $form_state['handler']->options;
04359 
04360   // This unpacks only options that are in the definition, ensuring random
04361   // extra stuff on the form is not sent through.
04362   $handler->unpack_options($handler->options, $options, NULL, FALSE);
04363 
04364   // Store the item back on the view
04365   $form_state['view']->set_item($form_state['display_id'], $form_state['type'], $form_state['id'], $handler->options);
04366 
04367   // Ensure any temporary options are removed.
04368   if (isset($form_state['view']->temporary_options[$type][$form_state['id']])) {
04369     unset($form_state['view']->temporary_options[$type][$form_state['id']]);
04370   }
04371 
04372   // Write to cache
04373   views_ui_cache_set($form_state['view']);
04374 }
04375 
04379 function views_ui_config_item_group_form($type, &$form_state) {
04380   $view = &$form_state['view'];
04381   $display_id = $form_state['display_id'];
04382   $type = $form_state['type'];
04383   $id = $form_state['id'];
04384 
04385   $form = array(
04386     'options' => array(
04387       '#tree' => TRUE,
04388       '#theme_wrappers' => array('container'),
04389       '#attributes' => array('class' => array('scroll')),
04390     ),
04391   );
04392   if (!$view->set_display($display_id)) {
04393     views_ajax_render(t('Invalid display id @display', array('@display' => $display_id)));
04394   }
04395 
04396   $view->init_query();
04397 
04398   $item = $view->get_item($display_id, $type, $id);
04399 
04400   if ($item) {
04401     $handler = $view->display_handler->get_handler($type, $id);
04402     if (empty($handler)) {
04403       $form['markup'] = array('#markup' => t("Error: handler for @table > @field doesn't exist!", array('@table' => $item['table'], '@field' => $item['field'])));
04404     }
04405     else {
04406       $handler->init($view, $item);
04407       $types = views_object_types();
04408 
04409       $form['#title'] = t('Configure group settings for @type %item', array('@type' => $types[$type]['lstitle'], '%item' => $handler->ui_name()));
04410 
04411       $handler->groupby_form($form['options'], $form_state);
04412       $form_state['handler'] = &$handler;
04413     }
04414 
04415     views_ui_standard_form_buttons($form, $form_state, 'views_ui_config_item_group_form');
04416   }
04417   return $form;
04418 }
04419 
04423 function views_ui_config_item_group_form_submit($form, &$form_state) {
04424   $item =& $form_state['handler']->options;
04425   $type = $form_state['type'];
04426   $id = $form_state['id'];
04427 
04428   $handler = views_get_handler($item['table'], $item['field'], $type);
04429   $handler->init($form_state['view'], $item);
04430 
04431   $handler->groupby_form_submit($form, $form_state);
04432 
04433   // Store the item back on the view
04434   $form_state['view']->set_item($form_state['display_id'], $form_state['type'], $form_state['id'], $item);
04435 
04436   // Write to cache
04437   views_ui_cache_set($form_state['view']);
04438 }
04439 
04443 function views_ui_config_item_form_remove($form, &$form_state) {
04444   // Store the item back on the view
04445   list($was_defaulted, $is_defaulted) = views_ui_standard_override_values($form, $form_state);
04446   // If the display selection was changed toggle the override value.
04447   if ($was_defaulted != $is_defaulted) {
04448     $display =& $form_state['view']->display[$form_state['display_id']];
04449     $display->handler->options_override($form, $form_state);
04450   }
04451   $form_state['view']->set_item($form_state['display_id'], $form_state['type'], $form_state['id'], NULL);
04452 
04453   // Write to cache
04454   views_ui_cache_set($form_state['view']);
04455 }
04456 
04460 function views_ui_config_item_form_expose($form, &$form_state) {
04461   $item = &$form_state['handler']->options;
04462   // flip
04463   $item['exposed'] = empty($item['exposed']);
04464 
04465   // If necessary, set new defaults:
04466   if ($item['exposed']) {
04467     $form_state['handler']->expose_options();
04468   }
04469 
04470   $form_state['view']->set_item($form_state['display_id'], $form_state['type'], $form_state['id'], $item);
04471 
04472   views_ui_cache_set($form_state['view']);
04473   $form_state['rerender'] = TRUE;
04474   $form_state['rebuild'] = TRUE;
04475   $form_state['force_expose_options'] = TRUE;
04476 }
04477 
04481 function views_ui_config_item_extra_form($form, &$form_state) {
04482   $view = &$form_state['view'];
04483   $display_id = $form_state['display_id'];
04484   $type = $form_state['type'];
04485   $id = $form_state['id'];
04486 
04487   $form = array(
04488     'options' => array(
04489       '#tree' => TRUE,
04490       '#theme_wrappers' => array('container'),
04491       '#attributes' => array('class' => array('scroll')),
04492     ),
04493   );
04494   if (!$view->set_display($display_id)) {
04495     views_ajax_error(t('Invalid display id @display', array('@display' => $display_id)));
04496   }
04497   $item = $view->get_item($display_id, $type, $id);
04498 
04499   if ($item) {
04500     $handler = $view->display_handler->get_handler($type, $id);
04501     if (empty($handler)) {
04502       $form['markup'] = array('#markup' => t("Error: handler for @table > @field doesn't exist!", array('@table' => $item['table'], '@field' => $item['field'])));
04503     }
04504     else {
04505       $handler->init($view, $item);
04506       $types = views_object_types();
04507 
04508       $form['#title'] = t('Configure extra settings for @type %item', array('@type' => $types[$type]['lstitle'], '%item' => $handler->ui_name()));
04509 
04510       $form['#section'] = $display_id . '-' . $type . '-' . $id;
04511 
04512       // Get form from the handler.
04513       $handler->extra_options_form($form['options'], $form_state);
04514       $form_state['handler'] = &$handler;
04515     }
04516 
04517     views_ui_standard_form_buttons($form, $form_state, 'views_ui_config_item_extra_form');
04518   }
04519   return $form;
04520 }
04521 
04525 function views_ui_config_item_extra_form_validate($form, &$form_state) {
04526   $form_state['handler']->extra_options_validate($form['options'], $form_state);
04527 }
04528 
04532 function views_ui_config_item_extra_form_submit($form, &$form_state) {
04533   // Run it through the handler's submit function.
04534   $form_state['handler']->extra_options_submit($form['options'], $form_state);
04535   $item = $form_state['handler']->options;
04536 
04537   // Store the data we're given.
04538   foreach ($form_state['values']['options'] as $key => $value) {
04539     $item[$key] = $value;
04540   }
04541 
04542   // Store the item back on the view
04543   $form_state['view']->set_item($form_state['display_id'], $form_state['type'], $form_state['id'], $item);
04544 
04545   // Write to cache
04546   views_ui_cache_set($form_state['view']);
04547 }
04548 
04552 function views_ui_config_style_form($form, &$form_state) {
04553   $view = &$form_state['view'];
04554   $display_id = $form_state['display_id'];
04555   $type = $form_state['type'];
04556   $id = $form_state['id'];
04557 
04558   $form = array(
04559     'options' => array(
04560       '#tree' => TRUE,
04561       '#theme_wrappers' => array('container'),
04562       '#attributes' => array('class' => array('scroll')),
04563     ),
04564   );
04565   if (!$view->set_display($display_id)) {
04566     views_ajax_error(t('Invalid display id @display', array('@display' => $display_id)));
04567   }
04568   $item = $view->get_item($display_id, $type, $id);
04569 
04570   if ($item) {
04571     $handler = views_get_handler($item['table'], $item['field'], $type);
04572     if (empty($handler)) {
04573       $form['markup'] = array('#markup' => t("Error: handler for @table > @field doesn't exist!", array('@table' => $item['table'], '@field' => $item['field'])));
04574     }
04575     else {
04576       $handler->init($view, $item);
04577       $types = views_object_types();
04578 
04579       $form['#title'] = t('Configure summary style for @type %item', array('@type' => $types[$type]['lstitle'], '%item' => $handler->ui_name()));
04580 
04581       $form['#section'] = $display_id . '-' . $type . '-style-options';
04582 
04583       $plugin = views_get_plugin('style', $handler->options['style_plugin']);
04584       if ($plugin) {
04585         $form['style_options'] = array(
04586           '#tree' => TRUE,
04587         );
04588         $plugin->init($view, $view->display[$display_id], $handler->options['style_options']);
04589 
04590         $plugin->options_form($form['style_options'], $form_state);
04591       }
04592 
04593       $form_state['handler'] = &$handler;
04594     }
04595 
04596     views_ui_standard_form_buttons($form, $form_state, 'views_ui_config_style_form');
04597   }
04598   return $form;
04599 }
04600 
04604 function views_ui_config_style_form_submit($form, &$form_state) {
04605   // Run it through the handler's submit function.
04606   $form_state['handler']->options_submit($form['style_options'], $form_state);
04607   $item = $form_state['handler']->options;
04608 
04609   // Store the data we're given.
04610   $item['style_options'] = $form_state['values']['style_options'];
04611 
04612   // Store the item back on the view
04613   $form_state['view']->set_item($form_state['display_id'], $form_state['type'], $form_state['id'], $item);
04614 
04615   // Write to cache
04616   views_ui_cache_set($form_state['view']);
04617 }
04618 
04622 function views_ui_get_roles() {
04623   static $roles = NULL;
04624   if (!isset($roles)) {
04625     $roles = array();
04626     $result = db_query("SELECT r.rid, r.name FROM {role} r ORDER BY r.name");
04627     foreach ($result as $obj) {
04628       $roles[$obj->rid] = $obj->name;
04629     }
04630   }
04631 
04632   return $roles;
04633 }
04634 
04638 function views_ui_admin_settings_basic() {
04639   $form = array();
04640   $form['#attached']['css'] = views_ui_get_admin_css();
04641 
04642   $options = array();
04643   foreach (list_themes() as $name => $theme) {
04644     if ($theme->status) {
04645       $options[$name] = $theme->info['name'];
04646     }
04647   }
04648 
04649   // This is not currently a fieldset but we may want it to be later,
04650   // so this will make it easier to change if we do.
04651   $form['basic'] = array();
04652 
04653   $form['basic']['views_ui_show_listing_filters'] = array(
04654     '#type' => 'checkbox',
04655     '#title' => t('Show filters on the list of views'),
04656     '#default_value' => variable_get('views_ui_show_listing_filters', FALSE),
04657   );
04658   $form['basic']['views_ui_show_advanced_help_warning'] = array(
04659     '#type' => 'checkbox',
04660     '#title' => t('Show advanced help warning'),
04661     '#default_value' => variable_get('views_ui_show_advanced_help_warning', TRUE),
04662   );
04663 
04664   $form['basic']['views_ui_show_master_display'] = array(
04665     '#type' => 'checkbox',
04666     '#title' => t('Always show the master display'),
04667     '#description' => t('Advanced users of views may choose to see the master (i.e. default) display.'),
04668     '#default_value' => variable_get('views_ui_show_master_display', FALSE),
04669   );
04670 
04671   $form['basic']['views_ui_show_advanced_column'] = array(
04672     '#type' => 'checkbox',
04673     '#title' => t('Always show advanced display settings'),
04674     '#description' => t('Default to showing advanced display settings, such as relationships and contextual filters.'),
04675     '#default_value' => variable_get('views_ui_show_advanced_column', FALSE),
04676   );
04677 
04678   $form['basic']['views_ui_display_embed'] = array(
04679     '#type' => 'checkbox',
04680     '#title' => t('Show the embed display in the ui.'),
04681     '#description' => t('Allow advanced user to use the embed view display. The plugin itself works if it\'s not visible in the ui'),
04682     '#default_value' => variable_get('views_ui_display_embed', FALSE),
04683   );
04684 
04685   $form['basic']['views_ui_custom_theme'] = array(
04686     '#type' => 'select',
04687     '#title' => t('Custom admin theme for the Views UI'),
04688     '#options' => array('_default' => t('- Use default -')) + $options,
04689     '#default_value' => variable_get('views_ui_custom_theme', '_default'),
04690     '#description' => t('In some cases you might want to select a different admin theme for the Views UI.')
04691   );
04692 
04693   $form['basic']['views_exposed_filter_any_label'] = array(
04694     '#type' => 'select',
04695     '#title' => t('Label for "Any" value on non-required single-select exposed filters'),
04696     '#options' => array('old_any' => '<Any>', 'new_any' => t('- Any -')),
04697     '#default_value' => variable_get('views_exposed_filter_any_label', 'new_any'),
04698   );
04699 
04700   $form['live_preview'] = array(
04701     '#type' => 'fieldset',
04702     '#title' => t('Live preview settings'),
04703   );
04704 
04705   $form['live_preview']['views_ui_always_live_preview'] = array(
04706     '#type' => 'checkbox',
04707     '#title' => t('Automatically update preview on changes'),
04708     '#default_value' => variable_get('views_ui_always_live_preview', TRUE),
04709   );
04710 
04711 //  $form['live_preview']['views_ui_always_live_preview_button'] = array(
04712 //    '#type' => 'checkbox',
04713 //    '#title' => t('Always show the preview button, even when the automatically update option is checked'),
04714 //    '#default_value' => variable_get('views_ui_always_live_preview_button', FALSE),
04715 //  );
04716 
04717   $form['live_preview']['views_ui_show_preview_information'] = array(
04718     '#type' => 'checkbox',
04719     '#title' => t('Show information and statistics about the view during live preview'),
04720     '#default_value' => variable_get('views_ui_show_preview_information', TRUE),
04721   );
04722 
04723   $form['live_preview']['views_ui_show_sql_query_where'] = array(
04724     '#type' => 'radios',
04725     '#options' => array(
04726       'above' => t('Above the preview'),
04727       'below' => t('Below the preview'),
04728     ),
04729     '#id' => 'edit-show-sql',
04730     '#default_value' => variable_get('views_ui_show_sql_query_where', 'above'),
04731     '#dependency' => array('edit-views-ui-show-preview-information' => array(TRUE)),
04732     '#prefix' => '<div id="edit-show-sql-wrapper" class="views-dependent">',
04733     '#suffix' => '</div>',
04734   );
04735 
04736   $form['live_preview']['views_ui_show_sql_query'] = array(
04737     '#type' => 'checkbox',
04738     '#title' => t('Show the SQL query'),
04739     '#default_value' => variable_get('views_ui_show_sql_query', FALSE),
04740     '#dependency' => array('edit-views-ui-show-preview-information' => array(TRUE)),
04741   );
04742   $form['live_preview']['views_ui_show_performance_statistics'] = array(
04743     '#type' => 'checkbox',
04744     '#title' => t('Show performance statistics'),
04745     '#default_value' => variable_get('views_ui_show_performance_statistics', FALSE),
04746     '#dependency' => array('edit-views-ui-show-preview-information' => array(TRUE)),
04747   );
04748 
04749   $form['live_preview']['views_show_additional_queries'] = array(
04750     '#type' => 'checkbox',
04751     '#title' => t('Show other queries run during render during live preview'),
04752     '#description' => t("Drupal has the potential to run many queries while a view is being rendered. Checking this box will display every query run during view render as part of the live preview."),
04753     '#default_value' => variable_get('views_show_additional_queries', FALSE),
04754     '#dependency' => array('edit-views-ui-show-preview-information' => array(TRUE)),
04755   );
04756 
04757 //  $form['live_preview']['views_ui_show_performance_statistics_where'] = array(
04758 
04759   return system_settings_form($form);
04760 }
04761 
04765 function views_ui_admin_settings_advanced() {
04766   $form = array();
04767   $form['#attached']['css'] = views_ui_get_admin_css();
04768 
04769   $form['cache'] = array(
04770     '#type' => 'fieldset',
04771     '#title' => t('Caching'),
04772   );
04773 
04774   $form['cache']['views_skip_cache'] = array(
04775     '#type' => 'checkbox',
04776     '#title' => t('Disable views data caching'),
04777     '#description' => t("Views caches data about tables, modules and views available, to increase performance. By checking this box, Views will skip this cache and always rebuild this data when needed. This can have a serious performance impact on your site."),
04778     '#default_value' => variable_get('views_skip_cache', FALSE),
04779   );
04780 
04781   $form['cache']['clear_cache'] = array(
04782     '#type' => 'submit',
04783     '#value' => t("Clear Views' cache"),
04784     '#submit' => array('views_ui_tools_clear_cache'),
04785   );
04786 
04787   $form['debug'] = array(
04788     '#type' => 'fieldset',
04789     '#title' => t('Debugging'),
04790   );
04791 
04792   $form['debug']['views_sql_signature'] = array(
04793     '#type' => 'checkbox',
04794     '#title' => t('Add Views signature to all SQL queries'),
04795     '#description' => t("All Views-generated queries will include the name of the views and display 'view-name:display-name' as a string  at the end of the SELECT clause. This makes identifying Views queries in database server logs simpler, but should only be used when troubleshooting."),
04796 
04797     '#default_value' => variable_get('views_sql_signature', FALSE),
04798   );
04799 
04800   $form['debug']['views_no_javascript'] = array(
04801     '#type' => 'checkbox',
04802     '#title' => t('Disable JavaScript with Views'),
04803     '#description' => t("If you are having problems with the JavaScript, you can disable it here. The Views UI should degrade and still be usable without javascript; it's just not as good."),
04804     '#default_value' => variable_get('views_no_javascript', FALSE),
04805   );
04806 
04807   $form['debug']['views_devel_output'] = array(
04808     '#type' => 'checkbox',
04809     '#title' => t('Enable views performance statistics/debug messages via the Devel module'),
04810     '#description' => t("Check this to enable some Views query and performance statistics/debug messages <em>if Devel is installed</em>."),
04811     '#default_value' => variable_get('views_devel_output', FALSE),
04812   );
04813 
04814   $form['locale'] = array(
04815     '#type' => 'fieldset',
04816     '#title' => t('Localization'),
04817   );
04818 
04819   $form['locale']['views_localization_plugin'] =  array(
04820     '#type' => 'radios',
04821     '#title' => t('Translation method'),
04822     '#options' => views_fetch_plugin_names('localization', NULL, array()),
04823     '#default_value' => views_get_localization_plugin(),
04824     '#description' => t('Select a translation method to use for Views data like header, footer, and empty text.'),
04825   );
04826 
04827   $regions = array();
04828   $regions['watchdog'] = t('Watchdog');
04829   if (module_exists('devel')) {
04830     $regions['message'] = t('Devel message(dpm)');
04831     $regions['drupal_debug'] = t('Devel logging (tmp://drupal_debug.txt)');
04832   }
04833 
04834   $form['debug']['views_devel_region'] = array(
04835     '#type' => 'select',
04836     '#title' => t('Page region to output performance statistics/debug messages'),
04837     '#default_value' => variable_get('views_devel_region', 'footer'),
04838     '#options' => $regions,
04839     '#dependency' => array('edit-views-devel-output' => array(1)),
04840   );
04841 
04842   $options = views_fetch_plugin_names('display_extender');
04843   if (!empty($options)) {
04844     $form['extenders'] = array(
04845       '#type' => 'fieldset',
04846     );
04847     ;
04848     $form['extenders']['views_display_extenders'] = array(
04849       '#title' => t('Display extenders'),
04850       '#default_value' => views_get_enabled_display_extenders(),
04851       '#options' => $options,
04852       '#type' => 'checkboxes',
04853       '#description' => t('Select extensions of the views interface.')
04854     );
04855   }
04856 
04857   return system_settings_form($form);
04858 }
04859 
04863 function views_ui_tools_clear_cache() {
04864   views_invalidate_cache();
04865   drupal_set_message(t('The cache has been cleared.'));
04866 }
04867 
04872 function views_ui_config_item_form_rescan($form, &$form_state) {
04873   drupal_theme_rebuild();
04874 
04875   // The 'Theme: Information' page is about to be shown again. That page
04876   // analyzes the output of theme_get_registry(). However, this latter
04877   // function uses an internal cache (which was initialized before we
04878   // called drupal_theme_rebuild()) so it won't reflect the
04879   // current state of our theme registry. The only way to clear that cache
04880   // is to re-initialize the theme system:
04881   unset($GLOBALS['theme']);
04882   drupal_theme_initialize();
04883 
04884   $form_state['rerender'] = TRUE;
04885   $form_state['rebuild'] = TRUE;
04886 }
04887 
04891 function views_ui_edit_display_form_change_theme($form, &$form_state) {
04892   // This is just a temporary variable.
04893   $form_state['view']->theme = $form_state['values']['theme'];
04894 
04895   views_ui_cache_set($form_state['view']);
04896   $form_state['rerender'] = TRUE;
04897   $form_state['rebuild'] = TRUE;
04898 }
04899 
04903 function views_ui_autocomplete_tag($string = '') {
04904   $matches = array();
04905   // get matches from default views:
04906   views_include('view');
04907   $views = views_get_all_views();
04908   foreach ($views as $view) {
04909     if (!empty($view->tag) && strpos($view->tag, $string) === 0) {
04910       $matches[$view->tag] = $view->tag;
04911       if (count($matches) >= 10) {
04912         break;
04913       }
04914     }
04915   }
04916 
04917   drupal_json_output($matches);
04918 }
04919 
04920 // ------------------------------------------------------------------
04921 // Get information from the Views data
04922 
04923 function _views_weight_sort($a, $b) {
04924   if ($a['weight'] != $b['weight']) {
04925     return $a['weight'] < $b['weight'] ? -1 : 1;
04926   }
04927   if ($a['title'] != $b['title']) {
04928     return $a['title'] < $b['title'] ? -1 : 1;
04929   }
04930 
04931   return 0;
04932 }
04933 
04940 function views_fetch_base_tables() {
04941   static $base_tables = array();
04942   if (empty($base_tables)) {
04943     $weights = array();
04944     $tables = array();
04945     $data = views_fetch_data();
04946     foreach ($data as $table => $info) {
04947       if (!empty($info['table']['base'])) {
04948         $tables[$table] = array(
04949           'title' => $info['table']['base']['title'],
04950           'description' => !empty($info['table']['base']['help']) ? $info['table']['base']['help'] : '',
04951           'weight' => !empty($info['table']['base']['weight']) ? $info['table']['base']['weight'] : 0,
04952         );
04953       }
04954     }
04955     uasort($tables, '_views_weight_sort');
04956     $base_tables = $tables;
04957   }
04958 
04959   return $base_tables;
04960 }
04961 
04962 function _views_sort_types($a, $b) {
04963   $a_group = drupal_strtolower($a['group']);
04964   $b_group = drupal_strtolower($b['group']);
04965   if ($a_group != $b_group) {
04966     return $a_group < $b_group ? -1 : 1;
04967   }
04968 
04969   $a_title = drupal_strtolower($a['title']);
04970   $b_title = drupal_strtolower($b['title']);
04971   if ($a_title != $b_title) {
04972     return $a_title < $b_title ? -1 : 1;
04973   }
04974 
04975   return 0;
04976 }
04977 
04984 function views_fetch_fields($base, $type, $grouping = FALSE) {
04985   static $fields = array();
04986   if (empty($fields)) {
04987     $data = views_fetch_data();
04988     $start = microtime(TRUE);
04989     // This constructs this ginormous multi dimensional array to
04990     // collect the important data about fields. In the end,
04991     // the structure looks a bit like this (using nid as an example)
04992     // $strings['nid']['filter']['title'] = 'string'.
04993     //
04994     // This is constructed this way because the above referenced strings
04995     // can appear in different places in the actual data structure so that
04996     // the data doesn't have to be repeated a lot. This essentially lets
04997     // each field have a cheap kind of inheritance.
04998 
04999     foreach ($data as $table => $table_data) {
05000       $bases = array();
05001       $strings = array();
05002       $skip_bases = array();
05003       foreach ($table_data as $field => $info) {
05004         // Collect table data from this table
05005         if ($field == 'table') {
05006           // calculate what tables this table can join to.
05007           if (!empty($info['join'])) {
05008             $bases = array_keys($info['join']);
05009           }
05010           // And it obviously joins to itself.
05011           $bases[] = $table;
05012           continue;
05013         }
05014         foreach (array('field', 'sort', 'filter', 'argument', 'relationship', 'area') as $key) {
05015           if (!empty($info[$key])) {
05016             if ($grouping && !empty($info[$key]['no group by'])) {
05017               continue;
05018             }
05019             if (!empty($info[$key]['skip base'])) {
05020               foreach ((array) $info[$key]['skip base'] as $base_name) {
05021                 $skip_bases[$field][$key][$base_name] = TRUE;
05022               }
05023             }
05024             elseif (!empty($info['skip base'])) {
05025               foreach ((array) $info['skip base'] as $base_name) {
05026                 $skip_bases[$field][$key][$base_name] = TRUE;
05027               }
05028             }
05029             // Don't show old fields. The real field will be added right.
05030             if (isset($info[$key]['moved to'])) {
05031               continue;
05032             }
05033             foreach (array('title', 'group', 'help', 'base', 'aliases') as $string) {
05034               // First, try the lowest possible level
05035               if (!empty($info[$key][$string])) {
05036                 $strings[$field][$key][$string] = $info[$key][$string];
05037               }
05038               // Then try the field level
05039               elseif (!empty($info[$string])) {
05040                 $strings[$field][$key][$string] = $info[$string];
05041               }
05042               // Finally, try the table level
05043               elseif (!empty($table_data['table'][$string])) {
05044                 $strings[$field][$key][$string] = $table_data['table'][$string];
05045               }
05046               else {
05047                 if ($string != 'base' && $string != 'base') {
05048                   $strings[$field][$key][$string] = t("Error: missing @component", array('@component' => $string));
05049                 }
05050               }
05051             }
05052           }
05053         }
05054       }
05055       foreach ($bases as $base_name) {
05056         foreach ($strings as $field => $field_strings) {
05057           foreach ($field_strings as $type_name => $type_strings) {
05058             if (empty($skip_bases[$field][$type_name][$base_name])) {
05059               $fields[$base_name][$type_name]["$table.$field"] = $type_strings;
05060             }
05061           }
05062         }
05063       }
05064     }
05065 //    vsm('Views UI data build time: ' . (views_microtime() - $start) * 1000 . ' ms');
05066   }
05067 
05068   // If we have an array of base tables available, go through them
05069   // all and add them together. Duplicate keys will be lost and that's
05070   // Just Fine.
05071   if (is_array($base)) {
05072     $strings = array();
05073     foreach ($base as $base_table) {
05074       if (isset($fields[$base_table][$type])) {
05075         $strings += $fields[$base_table][$type];
05076       }
05077     }
05078     uasort($strings, '_views_sort_types');
05079     return $strings;
05080   }
05081 
05082   if (isset($fields[$base][$type])) {
05083     uasort($fields[$base][$type], '_views_sort_types');
05084     return $fields[$base][$type];
05085   }
05086   return array();
05087 }
05088 
05089 
05093 function theme_views_ui_style_plugin_table($variables) {
05094   $form = $variables['form'];
05095 
05096   $output = drupal_render($form['description_markup']);
05097 
05098   $header = array(
05099     t('Field'),
05100     t('Column'),
05101     t('Align'),
05102     t('Separator'),
05103     array(
05104       'data' => t('Sortable'),
05105       'align' => 'center',
05106     ),
05107     array(
05108       'data' => t('Default order'),
05109       'align' => 'center',
05110     ),
05111     array(
05112       'data' => t('Default sort'),
05113       'align' => 'center',
05114     ),
05115     array(
05116       'data' => t('Hide empty column'),
05117       'align' => 'center',
05118     ),
05119   );
05120   $rows = array();
05121   foreach (element_children($form['columns']) as $id) {
05122     $row = array();
05123     $row[] = drupal_render($form['info'][$id]['name']);
05124     $row[] = drupal_render($form['columns'][$id]);
05125     $row[] = drupal_render($form['info'][$id]['align']);
05126     $row[] = drupal_render($form['info'][$id]['separator']);
05127     if (!empty($form['info'][$id]['sortable'])) {
05128       $row[] = array(
05129         'data' => drupal_render($form['info'][$id]['sortable']),
05130         'align' => 'center',
05131       );
05132       $row[] = array(
05133         'data' => drupal_render($form['info'][$id]['default_sort_order']),
05134         'align' => 'center',
05135       );
05136       $row[] = array(
05137         'data' => drupal_render($form['default'][$id]),
05138         'align' => 'center',
05139       );
05140     }
05141     else {
05142       $row[] = '';
05143       $row[] = '';
05144       $row[] = '';
05145     }
05146     $row[] = array(
05147       'data' => drupal_render($form['info'][$id]['empty_column']),
05148       'align' => 'center',
05149     );
05150     $rows[] = $row;
05151   }
05152 
05153   // Add the special 'None' row.
05154   $rows[] = array(t('None'), '', '', '', '', '', array('align' => 'center', 'data' => drupal_render($form['default'][-1])), '');
05155 
05156   $output .= theme('table', array('header' => $header, 'rows' => $rows));
05157   $output .= drupal_render_children($form);
05158   return $output;
05159 }
05160 
05166 function views_ui_get_display_label($view, $display_id, $check_changed = TRUE) {
05167   $title = $display_id == 'default' ? t('Master') : $view->display[$display_id]->display_title;
05168   $title = views_ui_truncate($title, 25);
05169 
05170   if ($check_changed && !empty($view->changed_display[$display_id])) {
05171     $changed = '*';
05172     $title = $title . $changed;
05173   }
05174 
05175   return $title;
05176 }
05177 
05178 function views_ui_add_template_page() {
05179   $templates = views_get_all_templates();
05180 
05181   if (empty($templates)) {
05182     return t('There are no templates available.');
05183   }
05184 
05185   $header = array(
05186     t('Name'),
05187     t('Description'),
05188     t('Operation'),
05189   );
05190 
05191   $rows = array();
05192   foreach ($templates as $name => $template) {
05193     $rows[] = array(
05194       array('data' => check_plain($template->get_human_name())),
05195       array('data' => check_plain($template->description)),
05196       array('data' => l('add', 'admin/structure/views/template/' . $template->name . '/add')),
05197     );
05198   }
05199 
05200   $output = theme('table', array('header' => $header, 'rows' => $rows));
05201   return $output;
05202 }
05203 
05219 function views_ui_form_button_was_clicked($element, &$form_state) {
05220   $process_input = empty($element['#disabled']) && ($form_state['programmed'] || ($form_state['process_input'] && (!isset($element['#access']) || $element['#access'])));
05221   if ($process_input && !isset($form_state['triggering_element']) && isset($element['#button_type']) && isset($form_state['input'][$element['#name']]) && isset($element['#values']) && in_array($form_state['input'][$element['#name']], $element['#values'], TRUE)) {
05222     $form_state['triggering_element'] = $element;
05223   }
05224   return $element;
05225 }
05226 
05232 function views_ui_default_button($element, &$form_state, $form) {
05233   $setting['viewsImplicitFormSubmission'][$form['#id']]['defaultButton'] = $element['#id'];
05234   $element['#attached']['js'][] = array('type' => 'setting', 'data' => $setting);
05235   return $element;
05236 }
05237 
05245 function views_ui_field_list() {
05246   $views = views_get_all_views();
05247 
05248   // Fetch all fieldapi fields which are used in views
05249   // Therefore search in all views, displays and handler-types.
05250   $fields = array();
05251   foreach ($views as $view) {
05252     foreach ($view->display as $display_id => $display) {
05253       if ($view->set_display($display_id)) {
05254         foreach (views_object_types() as $type => $info) {
05255           foreach ($view->get_items($type, $display_id) as $item) {
05256             $data = views_fetch_data($item['table']);
05257             if (isset($data[$item['field']]) && isset($data[$item['field']][$type])
05258               && $data = $data[$item['field']][$type]) {
05259               // The final check that we have a fieldapi field now.
05260               if (isset($data['field_name'])) {
05261                 $fields[$data['field_name']][$view->name] = $view->name;
05262               }
05263             }
05264           }
05265         }
05266       }
05267     }
05268   }
05269 
05270   $header = array(t('Field name'), t('Used in'));
05271   $rows = array();
05272   foreach ($fields as $field_name => $views) {
05273 
05274     $rows[$field_name]['data'][0] = check_plain($field_name);
05275     foreach ($views as $view) {
05276       $rows[$field_name]['data'][1][] = l($view, "admin/structure/views/view/$view");
05277     }
05278     $rows[$field_name]['data'][1] = implode(', ', $rows[$field_name]['data'][1]);
05279   }
05280 
05281   // Sort rows by field name.
05282   ksort($rows);
05283   $output = array(
05284     '#theme' => 'table',
05285     '#header' => $header,
05286     '#rows' => $rows,
05287     '#empty' => t('No fields have been used in views yet.'),
05288   );
05289 
05290   return $output;
05291 }

Generated on Sun Feb 26 2012 12:52:51 for Views by  doxygen 1.7.1