[ Index ]

PHP Cross Reference of Drupal 6 (yi-drupal)

title

Body

[close]

/sites/all/modules/views/includes/ -> admin.inc (source)

   1  <?php
   2  /**
   3   * @file admin.inc
   4   * Provides the Views' administrative interface.
   5   */
   6  
   7  /**
   8   * Page callback to list views in the system.
   9   */
  10  function views_ui_list_views($arg = NULL) {
  11    if ($arg != NULL) {
  12      return drupal_not_found();
  13    }
  14  
  15    $output = theme('views_ui_list_views');
  16    views_ui_check_advanced_help();
  17    return $output;
  18  }
  19  
  20  /**
  21   * Check to see if the advanced help module is installed, and if not put up
  22   * a message.
  23   *
  24   * Only call this function if the user is already in a position for this to
  25   * be useful.
  26   */
  27  function views_ui_check_advanced_help() {
  28    if (variable_get('views_hide_help_message', FALSE)) {
  29      return;
  30    }
  31  
  32    if (!module_exists('advanced_help')) {
  33      $filename = db_result(db_query("SELECT filename FROM {system} WHERE type = 'module' AND name = 'advanced_help'"));
  34      if ($filename && file_exists($filename)) {
  35        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/build/modules'),'@hide' => url('admin/build/views/tools'))));
  36      }
  37      else {
  38        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/build/views/tools'))));
  39      }
  40    }
  41  }
  42  
  43  /**
  44   * Preprocess the list views theme
  45   */
  46  function template_preprocess_views_ui_list_views(&$vars) {
  47    $items = array();
  48    $sorts = array();
  49  
  50    // Add some js for easier gui.
  51    views_add_js('view-list');
  52  
  53    $views = views_get_all_views();
  54  
  55    $token_enable = drupal_get_token('views-enable');
  56    $token_disable = drupal_get_token('views-disable');
  57  
  58    // Respond to a reset command by clearing session and doing a drupal goto
  59    // back to the base URL.
  60    if (isset($_GET['op']) && $_GET['op'] == t('Reset')) {
  61      unset($_SESSION['views']['#admin']);
  62      drupal_goto('admin/build/views');
  63    }
  64    if (count($_GET) <= 1) {
  65      if (isset($_SESSION['views']['#admin']) && is_array($_SESSION['views']['#admin'])) {
  66        $_GET += $_SESSION['views']['#admin'];
  67      }
  68    }
  69    else {
  70      $_SESSION['views']['#admin'] = $_GET;
  71      unset($_SESSION['views']['#admin']['q']);
  72    }
  73  
  74    $form_state = array(
  75      'views' => $views,
  76      'input' => $_GET,
  77      'method' => 'get',
  78      'rerender' => TRUE,
  79      'no_redirect' => TRUE,
  80    );
  81  
  82    $vars['widgets'] = drupal_build_form('views_ui_list_views_form', $form_state);
  83  
  84    $vars['help_type_icon'] = theme('advanced_help_topic', 'views', 'view-type');
  85  
  86    $base_tables = views_fetch_base_tables();
  87  
  88    foreach ($views as $view) {
  89      if ($form_state['values']['tag'] != 'all') {
  90        if ($form_state['values']['tag'] == 'none') {
  91          if (!empty($view->tag)) {
  92            continue;
  93          }
  94        }
  95        else if ($form_state['values']['tag'] != $view->tag) {
  96          continue;
  97        }
  98      }
  99      if ($form_state['values']['type'] != 'all' && $form_state['values']['type'] != $view->type) {
 100        continue;
 101      }
 102  
 103      if ($form_state['values']['base'] != 'all' && $form_state['values']['base'] != $view->base_table) {
 104        continue;
 105      }
 106  
 107      if ($form_state['values']['display'] != 'all' && empty($view->display[$form_state['values']['display']])) {
 108        continue;
 109      }
 110  
 111      $item = new stdClass();
 112      $item->ops = array();
 113      if (empty($view->disabled)) {
 114        $item->ops[] = l(t('Edit'), "admin/build/views/edit/$view->name");
 115        $item->ops[] = l(t('Export'), "admin/build/views/export/$view->name");
 116        $item->ops[] = l(t('Clone'), "admin/build/views/clone/$view->name");
 117      }
 118      if ($view->type != t('Default')) {
 119        $text = $view->type == t('Overridden') ? t('Revert') : t('Delete');
 120        $item->ops[] = l($text, "admin/build/views/delete/$view->name");
 121      }
 122      else {
 123        if (empty($view->disabled)) {
 124          $item->ops[] = l(t('Disable'), "admin/build/views/disable/$view->name", array('query' => drupal_get_destination() . '&token=' . $token_disable));
 125        }
 126        else {
 127          $item->ops[] = l(t('Enable'), "admin/build/views/enable/$view->name", array('query' => drupal_get_destination() . '&token=' . $token_enable));
 128        }
 129      }
 130  
 131      $item->ops = implode(' | ', $item->ops);
 132      if (empty($view->display)) {
 133        $item->path = t('Warning! Broken view!');
 134      }
 135      else {
 136        $item->path = $raw_path = $view->get_path();
 137        $item->path = $item->path && empty($view->disabled) && strpos($item->path, '%') === FALSE ? l($item->path, $item->path) : check_plain($item->path);
 138      }
 139  
 140      $item->type = $view->type;
 141      $item->name = $view->name;
 142  
 143      if (!empty($view->tag)) {
 144        $item->tag = check_plain($view->tag);
 145      }
 146  
 147      $item->title = $view->get_title();
 148      $item->base = !empty($base_tables[$view->base_table]['title']) ? $base_tables[$view->base_table]['title'] : t('Broken');
 149  
 150      $item->displays = array();
 151      foreach ($view->display as $display) {
 152        if (!empty($display->handler->definition['admin'])) {
 153          $item->displays[$display->handler->definition['admin']] = TRUE;
 154        }
 155      }
 156  
 157      if ($item->displays) {
 158        ksort($item->displays);
 159        $item->displays = implode(', ', array_keys($item->displays));
 160      }
 161  
 162      $item->description = check_plain($view->description);
 163      $item->classes = empty($view->disabled) ? 'view-enabled' : 'view-disabled';
 164      $items[] = $item;
 165  
 166      $sort = intval(empty($view->disabled) xor $form_state['values']['sort'] == 'asc');
 167  
 168      switch ($form_state['values']['order']) {
 169        case 'name':
 170        default:
 171          $sort .= strtolower($view->name);
 172          break;
 173        case 'title':
 174          $sort .= strtolower($item->title);
 175          break;
 176        case 'path':
 177          $sort .= strtolower($raw_path); // $path;
 178          break;
 179        case 'type':
 180          $sort .= $view->type . $view->name;
 181          break;
 182        case 'tag':
 183          $sort .= strtolower($view->tag);
 184          break;
 185        case 'desc':
 186          $sort .= strtolower($view->description);
 187          break;
 188      }
 189  
 190      $sorts[] = $sort;
 191    }
 192  
 193    if ($form_state['values']['sort'] == 'desc') {
 194      arsort($sorts);
 195    }
 196    else {
 197      asort($sorts);
 198    }
 199  
 200    $i = array();
 201    foreach ($sorts as $id => $title) {
 202      $i[] = $items[$id];
 203    }
 204  
 205    views_add_css('views-list');
 206    $vars['views'] = $i;
 207  
 208    $getting_started = theme('advanced_help_topic', 'views', 'getting-started', 'title');
 209    if (!$getting_started) {
 210      $getting_started = t('Install the advanced help module for the getting started');
 211    }
 212  
 213    $vars['help'] = t('Not sure what to do? Try the "!getting-started" page.', array('!getting-started' => $getting_started));
 214  }
 215  
 216  /**
 217   * Provide a form for sorting and filtering the list of views.
 218   */
 219  function views_ui_list_views_form(&$form_state) {
 220    if (!variable_get('clean_url', FALSE)) {
 221      $form['q'] = array(
 222        '#type' => 'hidden',
 223        '#value' => $_GET['q'],
 224      );
 225    }
 226  
 227    $all = array('all' => t('- All -'));
 228    $none = array('none' => t('- None -'));
 229  
 230    $form['type'] = array(
 231      '#type' => 'select',
 232      '#title' => t('Storage'),
 233      '#options' => array(
 234        'all' => t('- All -'),
 235        t('Normal') => t('Normal'),
 236        t('Default') => t('Default'),
 237        t('Overridden') => t('Overridden'),
 238      ),
 239      '#default_value' => 'all',
 240    );
 241  
 242    $bases = array();
 243    foreach (views_fetch_base_tables() as $table => $info) {
 244      $bases[$table] = $info['title'];
 245    }
 246  
 247    $form['base'] = array(
 248      '#type' => 'select',
 249      '#title' => t('Type'),
 250      '#options' => array_merge($all, $bases),
 251      '#default_value' => 'all',
 252    );
 253  
 254    $tags = array();
 255  
 256    $extras = array();
 257    foreach ($form_state['views'] as $name => $view) {
 258      if (!empty($view->tag)) {
 259        $tags[$view->tag] = $view->tag;
 260      }
 261    }
 262  
 263    asort($tags);
 264  
 265    $form['tag'] = array(
 266      '#type' => 'select',
 267      '#title' => t('Tag'),
 268      '#options' => array_merge($all, $none, $tags),
 269      '#default_value' => 'all',
 270    );
 271  
 272    $displays = array();
 273    foreach (views_fetch_plugin_data('display') as $id => $info) {
 274      if (!empty($info['admin'])) {
 275        $displays[$id] = $info['admin'];
 276      }
 277    }
 278  
 279    asort($displays);
 280  
 281    $form['display'] = array(
 282      '#type' => 'select',
 283      '#title' => t('Displays'),
 284      '#options' => array_merge($all, $displays),
 285      '#default_value' => 'all',
 286    );
 287  
 288    $form['order'] = array(
 289      '#type' => 'select',
 290      '#title' => t('Sort by'),
 291      '#options' => array(
 292        'name' => t('Name'),
 293        'title' => t('Title'),
 294        'tag' => t('Tag'),
 295        'path' => t('Path'),
 296        'type' => t('Type'),
 297        'desc' => t('Description'),
 298      ),
 299      '#default_value' => 'name',
 300    );
 301  
 302    $form['sort'] = array(
 303      '#type' => 'select',
 304      '#title' => t('Order'),
 305      '#options' => array(
 306        'asc' => t('Up'),
 307        'desc' => t('Down'),
 308      ),
 309      '#default_value' => 'asc',
 310    );
 311  
 312    $form['submit'] = array(
 313      '#name' => '', // so it won't in the $_GET args
 314      '#type' => 'submit',
 315      '#id' => 'edit-views-apply',
 316      '#value' => t('Apply'),
 317    );
 318  
 319    if (!empty($_SESSION['views']['#admin'])) {
 320      $form['reset'] = array(
 321        '#type' => 'submit',
 322        '#id' => 'edit-views-reset',
 323        '#value' => t('Reset'),
 324      );
 325    }
 326  
 327    $form['#theme'] = array('views_ui_list_views_form');
 328    return $form;
 329  }
 330  
 331  function theme_views_ui_list_views_form($form) {
 332    // Don't render these:
 333    unset($form['form_id']);
 334    unset($form['form_build_id']);
 335    unset($form['form_token']);
 336    return drupal_render($form);
 337  }
 338  
 339  /**
 340   * Page callback for the live preview.
 341   *
 342   * @todo make this use a template
 343   */
 344  function views_ui_preview($js, $view) {
 345    // Take off the items we know so that we can have just the args passed
 346    // in for later use.
 347    $func_args = func_get_args();
 348    array_shift($func_args); // $js
 349    array_shift($func_args); // $view
 350    $display_id = (count($func_args)) ? array_shift($func_args) : 'default';
 351  
 352    $form_state = array(
 353      'display_id' => $display_id,
 354      'view_args' => $func_args ? implode('/', $func_args) : '',
 355      'rerender' => TRUE,
 356      'no_redirect' => TRUE,
 357      'view' => &$view,
 358      'ajax' => $js
 359    );
 360  
 361    $output = drupal_build_form('views_ui_preview_form', $form_state);
 362    $args = array();
 363    if (isset($form_state['view_args']) && $form_state['view_args'] !== '') {
 364      $args = explode('/', $form_state['view_args']);
 365    }
 366  
 367    $errors = $view->validate();
 368    if ($errors === TRUE) {
 369      $view->ajax = $js;
 370      $view->live_preview = TRUE;
 371  
 372      // Store the current view URL for later use:
 373      $view->set_display($form_state['display_id']);
 374      $view->set_arguments($args);
 375  
 376      if ($view->display_handler->get_option('path')) {
 377        $path = $view->get_url();
 378      }
 379  
 380      // Make view links come back to preview.
 381      $view->override_path = 'admin/build/views/nojs/preview/' . $view->name . '/' . $form_state['display_id'];
 382  
 383      // also override $_GET['q'] so we get the pager
 384      $_GET['q'] = $view->override_path;
 385      if ($form_state['view_args']) {
 386        $_GET['q'] .= '/' . $form_state['view_args'];
 387      }
 388  
 389      $preview = $view->preview($form_state['display_id'], $args);
 390  
 391      // Get information from the preview for display.
 392      if (!empty($view->build_info['query'])) {
 393        $rows = array();
 394        $query = db_prefix_tables($view->build_info['query']);
 395        if ($view->build_info['query_args']) {
 396          _db_query_callback($view->build_info['query_args'], TRUE);
 397          $query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query);
 398        }
 399        $rows[] = array('<strong>' . t('Query') . '</strong>', '<pre>' . check_plain($query) . '</pre>');
 400        if (!empty($view->additional_queries)) {
 401          $queries = '<strong>' . t('These queries were run during view rendering:') . '</strong>';
 402          foreach ($view->additional_queries as $query) {
 403            if ($queries) {
 404              $queries .= "\n";
 405            }
 406            $queries .= t('[@time ms]', array('@time' => intval($query[1] * 100000) / 100)) . ' ' . check_plain($query[0]);
 407          }
 408  
 409          $rows[] = array('<strong>' . t('Other queries') . '</strong>', '<pre>' . $queries . '</pre>');
 410        }
 411  
 412        $rows[] = array('<strong>' . t('Title') . '</strong>', filter_xss_admin($view->get_title()));
 413        if (isset($path)) {
 414          $path = l($path, $path);
 415        }
 416        else {
 417          $path = t('This display has no path.');
 418        }
 419  
 420        $rows[] = array('<strong>' . t('Path') . '</strong>', $path);
 421  
 422        $rows[] = array('<strong>' . t('Query build time') . '</strong>', t('@time ms', array('@time' => intval($view->build_time * 100000) / 100)));
 423        $rows[] = array('<strong>' . t('Query execute time') . '</strong>', t('@time ms', array('@time' => intval($view->execute_time * 100000) / 100)));
 424        $rows[] = array('<strong>' . t('View render time') . '</strong>', t('@time ms', array('@time' => intval($view->render_time * 100000) / 100)));
 425        drupal_alter('views_preview_info', $rows, $view);
 426  
 427        $info = theme('table', array(), $rows);
 428      }
 429      else {
 430        $info = theme('table', array(), array(array('<strong>' . t('Query') . '</strong>', t('No query was run'))));
 431      }
 432    }
 433    else {
 434      foreach ($errors as $error) {
 435        drupal_set_message($error, 'error');
 436      }
 437      $preview = t('Unable to preview due to validation errors.');
 438      $info = '';
 439    }
 440  
 441    $info = '<div class="views-query-info">' . $info . '</div>';
 442  
 443    if (variable_get('views_ui_query_on_top', FALSE)) {
 444      $output .= $info . $preview;
 445    }
 446    else {
 447      $output .= $preview . $info;
 448    }
 449  
 450    if (!$js) {
 451      views_add_css('views-admin');
 452      drupal_set_title($view->get_title());
 453      return $output;
 454    }
 455    else {
 456      views_include('ajax');
 457      $object = new stdClass();
 458      if (!empty($view->js_settings)) {
 459        $object->js = $view->js_settings;
 460      }
 461      $object->display = '';
 462      if ($messages = theme('status_messages')) {
 463        $object->display = '<div class="views-messages">' . $messages . '</div>';
 464      }
 465      $object->display .= $output;
 466      $object->title = $view->get_title();
 467      views_ajax_render($object);
 468    }
 469  }
 470  
 471  /**
 472   * Form for generating argument information for the live preview.
 473   */
 474  function views_ui_preview_form(&$form_state) {
 475    $view = &$form_state['view'];
 476    $view->init_display();
 477    $options = array();
 478    foreach ($view->display as $id => $display) {
 479      $options[$id] = $display->display_title;
 480    }
 481  
 482    $form['#attributes'] = array(
 483      'class' => 'clear-block',
 484    );
 485  
 486    $form['display_id'] = array(
 487      '#type' => 'select',
 488      '#title' => t('Display'),
 489      '#options' => $options,
 490      '#default_value' => $form_state['display_id'],
 491      '#id' => 'preview-display-id',
 492    );
 493  
 494    $form['args'] = array(
 495      '#type' => 'textfield',
 496      '#title' => t('Arguments'),
 497      '#default_value' => $form_state['view_args'],
 498      '#description' => t('Separate arguments with a / as though they were a URL path.'),
 499      '#id' => 'preview-args',
 500    );
 501  
 502    $form['preview'] = array(
 503      '#type' => 'submit',
 504      '#value' => t('Preview'),
 505      '#id' => 'preview-submit',
 506    );
 507  
 508  
 509    $form['live_preview'] = array(
 510      '#type' => 'checkbox',
 511      '#title' => t('Automatic live preview'),
 512      '#default_value' => !variable_get('views_ui_disable_live_preview', 0),
 513    );
 514  
 515    $form['#action'] = url("admin/build/views/nojs/preview/$view->name");
 516    return $form;
 517  }
 518  
 519  /**
 520   * Submit the preview form.
 521   *
 522   * This just takes the data and stores it on the form state in a
 523   * known location. The caller will be responsible for using it.
 524   */
 525  function views_ui_preview_form_submit(&$form, &$form_state) {
 526    $form_state['display_id'] = $form_state['values']['display_id'];
 527    $form_state['view_args'] = $form_state['values']['args'];
 528  }
 529  
 530  /**
 531   * Page callback to add a new view.
 532   */
 533  function views_ui_add_page() {
 534    $form_state = array(
 535      'view' => NULL
 536    );
 537  
 538    return drupal_build_form('views_ui_add_form', $form_state);
 539  }
 540  
 541  /**
 542   * Page callback to add a new view.
 543   */
 544  function views_ui_clone_page($view) {
 545    $form_state = array(
 546      'view' => $view->copy(),
 547    );
 548  
 549    drupal_set_title(t('Clone view %view', array('%view' => $view->name)));
 550    return drupal_build_form('views_ui_add_form', $form_state);
 551  }
 552  
 553  /**
 554   * Form constructor callback to create the views Add Form, phase 1.
 555   */
 556  function views_ui_add_form(&$form_state) {
 557    $view = $form_state['view'];
 558    $form = array();
 559  
 560    $form['name'] = array(
 561      '#type' => 'textfield',
 562      '#title' => t('View name'),
 563      '#description' => t('This is the unique name of the view. It must contain only alphanumeric characters and underscores; it is used to identify the view internally and to generate unique theming template names for this view. If overriding a module provided view, the name must not be changed or instead a new view will be created.'),
 564      '#required' => TRUE,
 565      '#maxlength' => 32,
 566      '#default_value' => $view ? $view->name : '',
 567      '#attributes' => array('dir'=>'ltr'),
 568    );
 569  
 570    $form['description'] = array(
 571      '#type' => 'textfield',
 572      '#title' => t('View description'),
 573      '#description' => t('This description will appear on the Views administrative UI to tell you what the view is about.'),
 574      '#default_value' => $view ? $view->description : '',
 575      '#maxlength' => 255,
 576    );
 577  
 578    $form['tag'] = array(
 579      '#type' => 'textfield',
 580      '#title' => t('View tag'),
 581      '#description' => t('Enter an optional tag for this view; it is used only to help sort views on the administrative page.'),
 582      '#default_value' => $view ? $view->tag : '',
 583      '#autocomplete_path' => 'admin/views/ajax/autocomplete/tag',
 584    );
 585  
 586    $base_tables = array();
 587    foreach (views_fetch_base_tables() as $table => $info) {
 588      $base_tables[$table] = $info['title'] . '<div class="description">' . $info['description'] . '</div>';
 589    }
 590  
 591    $form['base_table'] = array(
 592      '#type' => 'radios',
 593      '#title' => t('View type'),
 594      '#description' => t('The view type is the primary table for which information is being retrieved. The view type controls what arguments, fields, sort criteria and filters are available, so once this is set it <strong>cannot be changed</strong>.'),
 595      '#default_value' => $view ? $view->base_table : 'node',
 596      '#options' => $base_tables,
 597    );
 598  
 599    if ($view) {
 600      $form['base_table']['#disabled'] = TRUE;
 601    }
 602  
 603    $form['submit'] = array(
 604      '#type' => 'submit',
 605      '#value' => t('Next'),
 606      '#validate' => array('views_ui_add_form_validate'),
 607      '#submit' => array('views_ui_add_form_submit'),
 608    );
 609  
 610    return $form;
 611  }
 612  
 613  /**
 614   * Validate the add view form.
 615   */
 616  function views_ui_add_form_validate($form, &$form_state) {
 617    $name = $form_state['values']['name'];
 618  
 619    // View name must be alphanumeric or underscores, no other punctuation.
 620    if (preg_match('/[^a-zA-Z0-9_]/', $name) || is_numeric($name)) {
 621      form_error($form['name'], t('View name must be alphanumeric or underscores only, but cannot be numeric.'));
 622    }
 623  
 624    // View name must already exist.
 625    $view = views_get_view($form_state['values']['name']);
 626    if ($view && $view->type != t('Default')) {
 627      form_error($form['name'], t('You must use a unique name for this view.'));
 628    }
 629  }
 630  
 631  /**
 632   * Process the add view form
 633   */
 634  function views_ui_add_form_submit($form, &$form_state) {
 635    $view = $form_state['view'] ? $form_state['view'] : views_new_view();
 636    $view->name = $form_state['values']['name'];
 637    $view->description = $form_state['values']['description'];
 638    $view->tag = $form_state['values']['tag'];
 639    $view->core = VERSION;
 640    if (empty($form['base_table']['#disabled'])) {
 641      $view->base_table = $form_state['values']['base_table'];
 642    }
 643  
 644    views_ui_cache_set($view);
 645    $form_state['redirect'] ='admin/build/views/edit/' . $view->name;
 646  }
 647  
 648  /**
 649   * Page to delete a view.
 650   */
 651  function views_ui_delete_confirm(&$form_state, $view) {
 652    $form_state['view'] = &$view;
 653    $form = array();
 654  
 655    $cancel = 'admin/build/views';
 656    if (!empty($_REQUEST['cancel'])) {
 657      $cancel = $_REQUEST['cancel'];
 658    }
 659  
 660    if ($view->type == t('Overridden')) {
 661      $title = t('Are you sure you want to revert the view %name?', array('%name' => $view->name));
 662      $desc = t('Reverting the view will delete the view that is in the database, reverting it to the original default view. Any changes you have made will be lost and cannot be recovered.');
 663      $button = t('Revert');
 664    }
 665    else {
 666      $title = t('Are you sure you want to delete the view %name?', array('%name' => $view->name));
 667      $desc = t('Deleting a view cannot be undone.');
 668      $button = t('Delete');
 669    }
 670  
 671    return confirm_form($form,
 672                    $title,
 673                    $cancel,
 674                    $desc,
 675                    $button,
 676                    t('Cancel'));
 677  }
 678  
 679  /**
 680   * Submit handler to delete a view.
 681   */
 682  function views_ui_delete_confirm_submit(&$form, &$form_state) {
 683    $form_state['view']->delete();
 684    views_object_cache_clear('view', $form_state['view']->name);
 685    drupal_set_message(t('The view has been deleted.'));
 686    $form_state['redirect'] = 'admin/build/views';
 687  }
 688  
 689  /**
 690   * Page to delete a view.
 691   */
 692  function views_ui_break_lock_confirm(&$form_state, $view) {
 693    $form_state['view'] = &$view;
 694    $form = array();
 695  
 696    if (empty($view->locked)) {
 697      return t('There is no lock on view %view to break.', array('%name' => $view->name));
 698    }
 699  
 700    $cancel = 'admin/build/views/edit/' . $view->name;
 701    if (!empty($_REQUEST['cancel'])) {
 702      $cancel = $_REQUEST['cancel'];
 703    }
 704  
 705    $account = user_load($view->locked->uid);
 706    return confirm_form($form,
 707                    t('Are you sure you want to break the lock on view %name?',
 708                    array('%name' => $view->name)),
 709                    $cancel,
 710                    t('By breaking this lock, any unsaved changes made by !user will be lost!', array('!user' => theme('username', $account))),
 711                    t('Break lock'),
 712                    t('Cancel'));
 713  }
 714  
 715  /**
 716   * Submit handler to break_lock a view.
 717   */
 718  function views_ui_break_lock_confirm_submit(&$form, &$form_state) {
 719    db_query("DELETE FROM {views_object_cache} WHERE obj = 'view' AND name = '%s'", $form_state['view']->name);
 720    $form_state['redirect'] = 'admin/build/views/edit/' . $form_state['view']->name;
 721    drupal_set_message(t('The lock has been broken and you may now edit this view.'));
 722  }
 723  
 724  /**
 725   * The main view edit page
 726   */
 727  function views_ui_edit_page($view) {
 728    drupal_set_title(t('Edit view %view', array('%view' => $view->name)));
 729    $output = theme('views_ui_edit_view', $view);
 730    views_ui_check_advanced_help();
 731    return $output;
 732  }
 733  
 734  /**
 735   * Export a view for cut & paste.
 736   */
 737  function views_ui_export_page(&$form_state, $view) {
 738    $code = $view->export();
 739    $lines = substr_count($code, "\n");
 740    $form['code'] = array(
 741      '#type' => 'textarea',
 742      '#title' => $view->name,
 743      '#default_value' => $code,
 744      '#rows' => $lines,
 745    );
 746    return $form;
 747  }
 748  
 749  /**
 750   * Import a view from cut & paste
 751   */
 752  function views_ui_import_page(&$form_state) {
 753    $form['name'] = array(
 754      '#type' => 'textfield',
 755      '#title' => t('View name'),
 756      '#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.'),
 757    );
 758  
 759    $form['view'] = array(
 760      '#type' => 'textarea',
 761      '#title' => t('Paste view code here'),
 762      '#required' => TRUE,
 763    );
 764  
 765    $form['submit'] = array(
 766      '#type' => 'submit',
 767      '#value' => t('Import'),
 768      '#submit' => array('views_ui_import_submit'),
 769      '#validate' => array('views_ui_import_validate'),
 770    );
 771    return $form;
 772  }
 773  
 774  /**
 775   * Validate handler to import a view
 776   */
 777  function views_ui_import_validate($form, &$form_state) {
 778    $view = '';
 779    views_include('view');
 780    // Be forgiving if someone pastes views code that starts with '<?php'.
 781    if (substr($form_state['values']['view'], 0, 5) == '<?php') {
 782      $form_state['values']['view'] = substr($form_state['values']['view'], 5);
 783    }
 784    ob_start();
 785    eval($form_state['values']['view']);
 786    ob_end_clean();
 787  
 788    if (!is_object($view)) {
 789      return form_error($form['view'], t('Unable to interpret view code.'));
 790    }
 791  
 792    if (empty($view->api_version) || $view->api_version < 2) {
 793      // Check for some value that would only exist on a Views 1 view.
 794      if (isset($view->url) || isset($view->page) || isset($view->block)) {
 795        views_include('convert');
 796        $view = views1_import($view);
 797        drupal_set_message(t('You are importing a view created in Views version 1. You may need to adjust some parameters to work correctly in version 2.'), 'warning');
 798      }
 799      else {
 800        form_error($form['view'], t('That view is not compatible with this version of Views.'));
 801      }
 802    }
 803    elseif ($view->api_version > views_api_version()) {
 804      form_error($form['view'], t('That view is created for the version @import_version of views, but you only have @api_version', array(
 805        '@import_version' => $view->api_version,
 806        '@api_version' => views_api_version())));
 807    }
 808  
 809    // View name must be alphanumeric or underscores, no other punctuation.
 810    if (!empty($form_state['values']['name']) && preg_match('/[^a-zA-Z0-9_]/', $form_state['values']['name'])) {
 811      form_error($form['name'], t('View name must be alphanumeric or underscores only.'));
 812    }
 813  
 814    if ($form_state['values']['name']) {
 815      $view->name = $form_state['values']['name'];
 816    }
 817  
 818    $test = views_get_view($view->name);
 819    if ($test && $test->type != t('Default')) {
 820      form_set_error('', t('A view by that name already exists; please choose a different name'));
 821    }
 822  
 823    $view->init_display();
 824  
 825    $broken = FALSE;
 826    // Make sure that all plugins and handlers needed by this view actually exist.
 827    foreach ($view->display as $id => $display) {
 828      if (empty($display->handler) || !empty($display->handler->broken)) {
 829        drupal_set_message(t('Display plugin @plugin is not available.', array('@plugin' => $display->display_plugin)), 'error');
 830        $broken = TRUE;
 831        continue;
 832      }
 833  
 834      $plugin = views_get_plugin('style', $display->handler->get_option('style_plugin'));
 835      if (!$plugin) {
 836        drupal_set_message(t('Style plugin @plugin is not available.', array('@plugin' => $display->handler->get_option('style_plugin'))), 'error');
 837        $broken = TRUE;
 838      }
 839      else if ($plugin->uses_row_plugin()) {
 840        $plugin = views_get_plugin('row', $display->handler->get_option('row_plugin'));
 841        if (!$plugin) {
 842          drupal_set_message(t('Row plugin @plugin is not available.', array('@plugin' => $display->handler->get_option('row_plugin'))), 'error');
 843          $broken = TRUE;
 844        }
 845      }
 846  
 847      foreach (views_object_types() as $type => $info) {
 848        $handlers = $display->handler->get_handlers($type);
 849        if ($handlers) {
 850          foreach ($handlers as $id => $handler) {
 851            if ($handler->broken()) {
 852              drupal_set_message(t('@type handler @table.@field is not available.', array(
 853                '@type' => $info['stitle'],
 854                '@table' => $handler->table,
 855                '@field' => $handler->field,
 856              )), 'error');
 857              $broken = TRUE;
 858            }
 859          }
 860        }
 861      }
 862    }
 863  
 864    if ($broken) {
 865      form_set_error('', t('Unable to import view.'));
 866    }
 867  
 868    $form_state['view'] = &$view;
 869  }
 870  
 871  /**
 872   * Submit handler for view import
 873   */
 874  function views_ui_import_submit($form, &$form_state) {
 875    // Store in cache and then go to edit.
 876    views_ui_cache_set($form_state['view']);
 877    $form_state['redirect'] = 'admin/build/views/edit/' . $form_state['view']->name;
 878  }
 879  
 880  /**
 881   * The main edit view form, which is really just a save/cancel/delete button.
 882   */
 883  function views_ui_edit_view_form(&$form_state, $view) {
 884    $form['buttons']['save'] = array(
 885      '#type' => 'submit',
 886      '#value' => t('Save'),
 887      '#validate' => array('views_ui_edit_view_form_validate'),
 888      '#submit' => array('views_ui_edit_view_form_submit'),
 889    );
 890  
 891    $form['buttons']['cancel'] = array(
 892      '#type' => 'submit',
 893      '#value' => t('Cancel'),
 894      '#submit' => array('views_ui_edit_view_form_cancel'),
 895    );
 896  
 897    if (is_numeric($view->vid)) {
 898      $form['buttons']['delete'] = array(
 899        '#type' => 'submit',
 900        '#value' => $view->type == t('Overridden') ? t('Revert') : t('Delete'),
 901        '#submit' => array('views_ui_edit_view_form_delete'),
 902      );
 903    }
 904  
 905    $form_state['view'] = &$view;
 906    return $form;
 907  }
 908  
 909  /**
 910   * Validate that a view is complete and whole.
 911   */
 912  function views_ui_edit_view_form_validate($form, &$form_state) {
 913    // Do not validate cancel or delete or revert.
 914    if (empty($form_state['clicked_button']['#value']) || $form_state['clicked_button']['#value'] != t('Save')) {
 915      return;
 916    }
 917  
 918    $errors = $form_state['view']->validate();
 919    if ($errors !== TRUE) {
 920      foreach ($errors as $error) {
 921        form_set_error('', $error);
 922      }
 923    }
 924  }
 925  
 926  /**
 927   * Submit handler for the edit view form.
 928   */
 929  function views_ui_edit_view_form_submit($form, &$form_state) {
 930    // Go through and remove displayed scheduled for removal.
 931    foreach ($form_state['view']->display as $id => $display) {
 932      if (!empty($display->deleted)) {
 933        unset($form_state['view']->display[$id]);
 934      }
 935    }
 936  
 937    $form_state['view']->save();
 938    drupal_set_message(t('The view %name has been saved.', array('%name' => $form_state['view']->name)));
 939  
 940    // Make sure menu items get rebuilt as neces
 941    menu_rebuild();
 942  
 943    // Clear the views cache.
 944    cache_clear_all('*', 'cache_views');
 945  
 946    // Clear the page cache.
 947    cache_clear_all();
 948  
 949    // Remove this view from cache so we can edit it properly.
 950    views_object_cache_clear('view', $form_state['view']->name);
 951  }
 952  
 953  /**
 954   * Submit handler for the edit view form.
 955   */
 956  function views_ui_edit_view_form_cancel($form, &$form_state) {
 957    // Remove this view from cache so edits will be lost.
 958    views_object_cache_clear('view', $form_state['view']->name);
 959    if (empty($form['view']->vid)) {
 960      // I seem to have to drupal_goto here because I can't get fapi to
 961      // honor the redirect target. Not sure what I screwed up here.
 962      drupal_goto('admin/build/views');
 963    }
 964  }
 965  
 966  function views_ui_edit_view_form_delete($form, &$form_state) {
 967    unset($_REQUEST['destination']);
 968    // Redirect to the delete confirm page
 969    $form_state['redirect'] = array('admin/build/views/delete/' . $form_state['view']->name, 'cancel=admin/build/views/edit/' . $form_state['view']->name . '&' . drupal_get_destination());
 970  }
 971  
 972  /**
 973   * Preprocess the view edit page.
 974   */
 975  function template_preprocess_views_ui_edit_view(&$vars) {
 976    $view = &$vars['view'];
 977  
 978    $vars['save_button'] = drupal_get_form('views_ui_edit_view_form', $view);
 979  
 980    $table = views_fetch_data($view->base_table);
 981    $vars['base_table'] = !empty($table['table']['base']['title']) ?
 982      $table['table']['base']['title'] : t('Unknown or missing table name');
 983  
 984    views_include('tabs');
 985    $tabs = new views_tabset;
 986  
 987    $vars['message'] = '<div class="message">' . t("Click on an item to edit that item's details.") . '</div>';
 988  
 989    if (!$view->set_display('default')) {
 990      drupal_set_message(t('This view has a broken default display and cannot be used.'), 'error');
 991    }
 992  
 993    foreach ($view->display as $display) {
 994      list($title, $body) = views_ui_display_tab($view, $display);
 995      // The first display is the default.
 996      $tabs->set($display->id, $title, $body);
 997    }
 998  
 999    // This is the area that will render beneath the links
1000    $form_state = array(
1001      'view' => &$view,
1002      'ajax' => FALSE,
1003    );
1004  
1005    $display_button = drupal_build_form('views_ui_add_display_form', $form_state);
1006    $analyze_button = drupal_get_form('views_ui_analyze_view_button', $view);
1007    $tabs->add_extra($display_button . $analyze_button);
1008  
1009    $vars['tabs'] = $tabs->render();
1010  
1011    $form_state = array(
1012      'display_id' => 'default',
1013      'view_args' => '',
1014      'rerender' => FALSE,
1015      'no_redirect' => TRUE,
1016      'view' => &$view,
1017      'input' => array(),
1018    );
1019    $vars['preview'] = drupal_build_form('views_ui_preview_form', $form_state);
1020  
1021    $vars['locked'] = NULL;
1022    if (isset($view->locked) && is_object($view->locked)) {
1023      $account = user_load($view->locked->uid);
1024      $vars['locked'] = theme('username', $account);
1025      $vars['lock_age'] = format_interval(time() - $view->locked->updated);
1026      $vars['break'] = url('admin/build/views/break-lock/' . $view->name);
1027    }
1028  
1029    $vars['quick_links_raw'] = array(
1030      array(
1031        'title' => t('Export'),
1032        'alt' => t("Export this view"),
1033        'href' => "admin/build/views/export/$view->name",
1034      ),
1035      array(
1036        'title' => t('Clone'),
1037        'alt' => t("Create a copy of this view"),
1038        'href' => "admin/build/views/clone/$view->name",
1039      ),
1040    );
1041  
1042    $paths = array();
1043    foreach ($view->display as $id => $display) {
1044      if (!empty($display->handler) && $display->handler->has_path()) {
1045        $path = $display->handler->get_path();
1046        if (strpos($path, '%') === FALSE && !isset($paths[$path])) {
1047          $vars['quick_links_raw'][] = array(
1048            'title' => t('View "@display"', array('@display' => $display->display_title)),
1049            'alt' => t("Go to the real page for this display"),
1050            'href' => $path,
1051          );
1052          // Displays can have the same path; no point in showing more than one link.
1053          $paths[$path] = TRUE;
1054        }
1055      }
1056    }
1057  
1058    $vars['quick_links'] = theme('links', $vars['quick_links_raw']);
1059    views_add_css('views-admin');
1060    views_add_js('ajax');
1061    drupal_add_js('misc/jquery.form.js');
1062  
1063    // Also add any js files required by plugins:
1064    $plugins = views_fetch_plugin_data();
1065    foreach ($plugins as $type => $type_plugins) {
1066      foreach ($type_plugins as $name => $plugin) {
1067        if (!empty($plugin['js'])) {
1068          foreach ($plugin['js'] as $file) {
1069            drupal_add_js($file);
1070          }
1071        }
1072      }
1073    }
1074  
1075    $settings = array('views' => array('ajax' => array(
1076      'id' => '#views-ajax-pad',
1077      'title' => '#views-ajax-title',
1078      'defaultForm' => $vars['message'],
1079    )));
1080  
1081    drupal_add_js($settings, 'setting');
1082  }
1083  
1084  function template_preprocess_views_ui_edit_tab(&$vars) {
1085    $view = $vars['view'];
1086    $display = $vars['display'];
1087    $plugin = $display->handler->definition;
1088  
1089    $top = $left = $middle = $right = '';
1090  
1091    // If this form was submitted it was already handled, so force it not to
1092    // submit again.
1093  
1094    $vars['remove'] = '';
1095    $vars['clone'] = '';
1096    if (empty($plugin['no remove'])) {
1097      if (!empty($_POST['form_id']) && in_array($_POST['form_id'], array('views_ui_remove_display_form', 'views_ui_clone_display_form'))) {
1098        unset($_POST['form_id']);
1099      }
1100      $form_state = array('view' => &$view, 'display_id' => $display->id, 'ajax' => FALSE);
1101      $vars['remove'] = drupal_build_form('views_ui_remove_display_form', $form_state);
1102      $vars['clone'] = drupal_build_form('views_ui_clone_display_form', $form_state);
1103    }
1104  
1105    // basic fields
1106    $vars['title'] = check_plain($display->display_title);
1107    $vars['description'] = check_plain($plugin['help']);
1108  
1109    // Special fields if tihs is the default display.
1110    $vars['default'] = ($display->id == 'default');
1111    $vars['details_class'] = views_ui_item_css('details');
1112    if (!empty($view->changed_sections['details'])) {
1113      $vars['details_changed'] = TRUE;
1114    }
1115  
1116    $tag = empty($view->tag) ? t('None') : $view->tag;
1117    $vars['details'] = t('Description') . '/' . t('Tag') . ': ' . l($tag, "admin/build/views/nojs/details/$view->name", array('attributes' => array('class' => 'views-ajax-link')));
1118  
1119    // Calculate options from display plugin.
1120    $options = $categories = array();
1121    $display->handler->options_summary($categories, $options);
1122  
1123    // Build all of the options we were returned and put them into the
1124    // category data fields.
1125    foreach ($options as $id => $option) {
1126      if (empty($categories[$option['category']]['data'])) {
1127        $categories[$option['category']]['data'] = array();
1128      }
1129      $categories[$option['category']]['data'][$id] = array();
1130      $data = &$categories[$option['category']]['data'][$id];
1131      $data['content'] = '';
1132      $data['links'] = '';
1133      $data['overridden'] = FALSE;
1134      $data['defaulted'] = FALSE;
1135  
1136      // If there are optional links, build them first so they float properly.
1137      if (!empty($option['links'])) {
1138        foreach ($option['links'] as $link_id => $link_value) {
1139          $data['links'] .= $display->handler->option_link($link_value, $link_id, 'views-button-configure');
1140        }
1141      }
1142      if (!empty($option['title'])) {
1143        $data['content'] .= $option['title'] . ': ';
1144      }
1145  
1146      $data['content'] .= $display->handler->option_link($option['value'], $id, '', empty($option['desc']) ? '' : $option['desc']);
1147      if (!empty($display->handler->options['defaults'][$id])) {
1148        $display_id = 'default';
1149        $data['defaulted'] = TRUE;
1150      }
1151      else {
1152        $display_id = $display->id;
1153        if (!$display->handler->is_default_display()) {
1154          if ($display->handler->defaultable_sections($id)) {
1155            $data['overridden'] = TRUE;
1156          }
1157        }
1158      }
1159      $data['class'] = views_ui_item_css($display_id . '-' . $id);
1160      if (!empty($view->changed_sections[$display_id . '-' . $id])) {
1161        $data['changed'] = TRUE;
1162      }
1163    }
1164  
1165    $vars['categories'] = $categories;
1166  
1167    // Add a help icon
1168    if (isset($plugin['help topic'])) {
1169      $vars['display_help_icon'] = theme('advanced_help_topic', $plugin['module'], $plugin['help topic']);
1170    }
1171    else {
1172      $vars['display_help_icon'] = '';
1173    }
1174  
1175    // Fetch style plugin info because it has some effect on how/what we render.
1176    $style_plugin = $display->handler->get_plugin();
1177  
1178    $vars['fields'] = '';
1179    $vars['fields'] = theme('views_ui_edit_item', 'field', $view, $display, !($style_plugin && $style_plugin->uses_fields()));
1180    $vars['relationships'] = theme('views_ui_edit_item', 'relationship', $view, $display);
1181    $vars['arguments'] = theme('views_ui_edit_item', 'argument', $view, $display);
1182    $vars['filters'] = theme('views_ui_edit_item', 'filter', $view, $display);
1183    $vars['sorts'] = theme('views_ui_edit_item', 'sort', $view, $display);
1184  }
1185  
1186  /**
1187   * Generate the summary output for a single display to render in a tab.
1188   */
1189  function views_ui_display_tab($view, $display) {
1190    if (isset($display->handler)) {
1191      $plugin = $display->handler->definition;
1192    }
1193    if (empty($plugin)) {
1194      $title = isset($display->display_title) ? $display->display_title : t('Invalid');
1195      return array($title, t("Error: Display @display refers to a plugin named '@plugin', but that plugin doesn't exist!", array('@display' => $display->id, '@plugin' => $display->display_plugin)));
1196  
1197      // @todo We can do a better 'plugin does not exist' tab.
1198    }
1199  
1200    // The display should always be initialized prior to this call.
1201    if (empty($display->handler)) {
1202      return FALSE;
1203    }
1204  
1205    $body = theme('views_ui_edit_tab', $view, $display);
1206    return array($display->display_title, $body);
1207  }
1208  
1209  /**
1210   * Add information about a section to a display.
1211   */
1212  function template_preprocess_views_ui_edit_item(&$vars) {
1213    $type = $vars['type'];
1214    $view = $vars['view'];
1215    $display = $vars['display'];
1216  
1217    $types = views_object_types();
1218  
1219    $vars['overridden'] = FALSE;
1220    $vars['defaulted'] = FALSE;
1221  
1222    if ($vars['no_fields']) {
1223      $vars['title'] = $types[$type]['title'];
1224      $vars['item_help_icon'] = theme('advanced_help_topic', 'views', $type);
1225      $vars['rearrange'] = NULL;
1226      $vars['add'] = NULL;
1227      return;
1228    }
1229  
1230    $vars['rearrange'] = l('<span>' . t('Rearrange') . '</span>', "admin/build/views/nojs/rearrange/$view->name/$display->id/$type", array('attributes' => array('class' => 'views-button-rearrange views-ajax-link', 'title' => t('Rearrange')), 'html' => true));
1231  
1232    $vars['add'] = l('<span>' . t('Add') . '</span>', "admin/build/views/nojs/add-item/$view->name/$display->id/$type", array('attributes' => array('class' => 'views-button-add views-ajax-link', 'title' => t('Add')), 'html' => true));
1233  
1234    if (!$display->handler->is_default_display()) {
1235      if (!$display->handler->is_defaulted($types[$type]['plural'])) {
1236        $vars['overridden'] = TRUE;
1237      }
1238      else {
1239        $vars['defaulted'] = TRUE;
1240      }
1241    }
1242  
1243    if ($display->display_plugin != 'default') {
1244      $vars['title'] = l($types[$type]['title'], "admin/build/views/nojs/config-type/$view->name/$display->id/$type", array('attributes' => array('class' => 'views-ajax-link')));
1245    }
1246    else {
1247      $vars['title'] = $types[$type]['title'];
1248    }
1249  
1250    $fields = array();
1251  
1252    static $relationships = NULL;
1253    if (!isset($relationships)) {
1254      // Get relationship labels
1255      $relationships = array();
1256      // @todo: get_handlers()
1257      $handlers = $display->handler->get_option('relationships');
1258      if ($handlers) {
1259        foreach ($handlers as $id => $relationship) {
1260          $handler = views_get_handler($relationship['table'], $relationship['field'], 'relationship');
1261          if (empty($handler)) {
1262            continue;
1263          }
1264          $handler->init($view, $relationship);
1265          $relationships[$id] = $handler->label();
1266        }
1267      }
1268    }
1269  
1270    // @todo: get_handlers()
1271    foreach ($display->handler->get_option($types[$type]['plural']) as $id => $field) {
1272      $fields[$id] = array();
1273  
1274      $handler = views_get_handler($field['table'], $field['field'], $type);
1275      if (empty($handler)) {
1276        $fields[$id]['class'] = 'broken';
1277        $field_name = t('Broken/missing handler: @table > @field', array('@table' => $field['table'], '@field' => $field['field']));
1278        $fields[$id]['title'] = l($field_name, "admin/build/views/nojs/config-item/$view->name/$display->id/$type/$id", array('attributes' => array('class' => 'views-ajax-link'), 'html' => TRUE));
1279        $fields[$id]['info'] = '';
1280        continue;
1281      }
1282      $handler->init($view, $field);
1283  
1284      $field_name = $handler->ui_name(TRUE);
1285      if (!empty($field['relationship']) && !empty($relationships[$field['relationship']])) {
1286        $field_name = '(' . $relationships[$field['relationship']] . ') ' . $field_name;
1287      }
1288  
1289      $fields[$id]['title'] = l($field_name, "admin/build/views/nojs/config-item/$view->name/$display->id/$type/$id", array('attributes' => array('class' => 'views-ajax-link'), 'html' => TRUE));
1290      $fields[$id]['class'] = views_ui_item_css($display->id . '-' . $type . '-' . $id);
1291      if (!empty($view->changed_sections[$display->id . '-' . $type . '-' . $id])) {
1292        $fields[$id]['changed'] = TRUE;
1293      }
1294      $fields[$id]['info'] = $handler->admin_summary();
1295  
1296      if ($handler->has_extra_options()) {
1297        $fields[$id]['links'] = l('<span>' . t('Settings') . '</span>', "admin/build/views/nojs/config-item-extra/$view->name/$display->id/$type/$id", array('attributes' => array('class' => 'views-button-configure views-ajax-link', 'title' => t('Settings')), 'html' => true));
1298      }
1299  
1300      if ($handler->needs_style_plugin()) {
1301        $style_plugin = views_fetch_plugin_data('style', $handler->options['style_plugin']);
1302        $style_title = empty($style_plugin['title']) ? t('Missing style plugin') : $style_plugin['title'];
1303        $pid = $id . '-style-plugin';
1304  
1305        if (!empty($style_plugin['uses options'])) {
1306          $fields[$pid]['links'] = l('<span>' . t('Change settings for this style') . '</span>', "admin/build/views/nojs/config-style/$view->name/$display->id/$type/$id", array('attributes' => array('class' => 'views-button-configure views-ajax-link', 'title' => t('Settings')), 'html' => true));
1307        }
1308  
1309        $fields[$pid]['title'] = ' ' . t('&nbsp; Style: !style', array('!style' => l($style_title, "admin/build/views/nojs/change-style/$view->name/$display->id/$type/$id", array('attributes' => array('class' => 'views-ajax-link')))));
1310        $fields[$pid]['class'] = views_ui_item_css($display->id . '-' . $type . '-' . $pid);
1311        if (!empty($view->changed_sections[$display->id . '-' . $type . '-' . $pid])) {
1312          $fields[$pid]['changed'] = TRUE;
1313        }
1314        $fields[$pid]['info'] = '';
1315      }
1316    }
1317  
1318    $vars['fields'] = $fields;
1319    $vars['item_help_icon'] = theme('advanced_help_topic', 'views', $type);
1320  }
1321  
1322  /**
1323   * Regenerate the tabs for AJAX updates.
1324   */
1325  function views_ui_regenerate_tabs(&$view, $display_id = NULL, $object = NULL) {
1326    if (empty($display_id)) {
1327      $displays = array_keys($view->display);
1328    }
1329    elseif (!is_array($display_id)) {
1330      $displays = array($display_id);
1331      if ($display_id != 'default') {
1332        $displays[] = 'default';
1333      }
1334    }
1335    else {
1336      $displays = $display_id;
1337    }
1338  
1339    if (!$view->set_display('default')) {
1340      views_ajax_render(t('Invalid display id found while regenerating tabs'));
1341    }
1342  
1343    if (!is_object($object)) {
1344      $object = new stdClass();
1345    }
1346  
1347    $object->replace = array();
1348    foreach ($displays as $id) {
1349      list($title, $body) = views_ui_display_tab($view, $view->display[$id]);
1350      $object->replace['#views-tab-' . $id] = $body;
1351      $object->replace['#views-tab-title-' . $id] = check_plain($title);
1352    }
1353  
1354    if (!empty($view->changed)) {
1355      $object->changed = TRUE;
1356    }
1357  
1358    views_ajax_render($object);
1359  }
1360  
1361  /**
1362   * Provide standard buttons for the forms to make it easy. Also provide
1363   * a hidden op operator because the forms plugin doesn't seem to properly
1364   * provide which button was clicked.
1365   */
1366  function views_ui_standard_form_buttons(&$form, &$form_state, $form_id, $name = NULL, $third = NULL, $submit = NULL) {
1367    $form['buttons'] = array(
1368      '#prefix' => '<div class="clear-block"><div class="form-buttons">',
1369      '#suffix' => '</div></div>',
1370    );
1371  
1372    if (empty($name)) {
1373      $name = t('Update');
1374    }
1375  
1376    // Add the override and update button
1377    if ($name == t('Update default display')) {
1378      $form['buttons']['override_update'] = array(
1379        '#type' => 'submit',
1380        '#value' => t('Update and override'),
1381        '#submit' => array(
1382          'views_ui_edit_display_form_override_update_section',
1383          'views_ui_standard_submit',
1384          'views_ui_edit_display_form_override_update',
1385        ),
1386      );
1387    }
1388  
1389    if (empty($form_state['ok_button'])) {
1390      // but be sure submit button validates!
1391      $form['buttons']['submit'] = array(
1392        '#type' => 'submit',
1393        '#value' => $name,
1394        '#submit' => array('views_ui_standard_submit', $form_id . '_submit'),
1395      );
1396    }
1397  
1398    $cancel_submit = function_exists($form_id . '_cancel') ? $form_id . '_cancel' : 'views_ui_standard_cancel';
1399    $form['buttons']['cancel'] = array(
1400      '#type' => 'submit',
1401      '#value' => empty($form_state['ok_button']) ? t('Cancel') : t('Ok'),
1402      '#submit' => array($cancel_submit),
1403      '#validate' => array(),
1404    );
1405  
1406    if ($third) {
1407      if (empty($submit)) {
1408        $submit = 'third';
1409      }
1410      $third_submit = function_exists($form_id . '_' . $submit) ? $form_id . '_' . $submit : 'views_ui_standard_cancel';
1411  
1412      $form['buttons'][$submit] = array(
1413        '#type' => 'submit',
1414        '#value' => $third,
1415        '#validate' => array(),
1416        '#submit' => array($third_submit),
1417      );
1418    }
1419  
1420    // Compatibility, to be removed later:
1421    // We used to set these items on the form, but now we want them on the $form_state:
1422    if (isset($form['#title'])) {
1423      $form_state['title'] = $form['#title'];
1424    }
1425    if (isset($form['#help_topic'])) {
1426      $form_state['help_topic'] = $form['#help_topic'];
1427    }
1428    if (isset($form['#help_module'])) {
1429      $form_state['help_module'] = $form['#help_module'];
1430    }
1431    if (isset($form['#url'])) {
1432      $form_state['url'] = $form['#url'];
1433    }
1434    if (isset($form['#js'])) {
1435      if (!empty($form_state['js settings']) && is_array($form_state['js settings'])) {
1436        $form_state['js settings'] = array_merge($form_state['js settings'], $form['#js']);
1437      }
1438      else {
1439        $form_state['js settings'] = $form['#js'];
1440      }
1441    }
1442    if (isset($form['#section'])) {
1443      $form_state['#section'] = $form['#section'];
1444    }
1445    // Finally, we never want these cached -- our object cache does that for us.
1446    $form['#no_cache'] = TRUE;
1447  
1448    // If this isn't an ajaxy form, then we want to set the title.
1449    if (!empty($form['#title'])) {
1450      drupal_set_title($form['#title']);
1451    }
1452    views_add_css('views-admin');
1453  }
1454  
1455  /**
1456   * Basic submit handler applicable to all 'standard' forms
1457   */
1458  function views_ui_standard_submit($form, &$form_state) {
1459    if (!empty($form['#section'])) {
1460      $form_state['view']->changed_sections[$form['#section']] = TRUE;
1461    }
1462  }
1463  
1464  /**
1465   * Submit handler for cancel button
1466   */
1467  function views_ui_standard_cancel($form, &$form_state) {
1468    $form_state['redirect'] = 'admin/build/views/edit/' . $form_state['view']->name;
1469  }
1470  
1471  // --------------------------------------------------------------------------
1472  // Various subforms for editing the pieces of a view.
1473  
1474  function views_ui_ajax_forms($key = NULL) {
1475    $forms = array(
1476      'display' => array(
1477        'form_id' => 'views_ui_edit_display_form',
1478        'args' => array('section'),
1479      ),
1480      'remove-display' => array(
1481        'form_id' => 'views_ui_remove_display_form',
1482        'args' => array(),
1483      ),
1484      'config-type' => array(
1485        'form_id' => 'views_ui_config_type_form',
1486        'args' => array('type'),
1487      ),
1488      'rearrange' => array(
1489        'form_id' => 'views_ui_rearrange_form',
1490        'args' => array('type'),
1491      ),
1492      'add-item' => array(
1493        'form_id' => 'views_ui_add_item_form',
1494        'args' => array('type'),
1495      ),
1496      'config-item' => array(
1497        'form_id' => 'views_ui_config_item_form',
1498        'args' => array('type', 'id'),
1499      ),
1500      'config-item-extra' => array(
1501        'form_id' => 'views_ui_config_item_extra_form',
1502        'args' => array('type', 'id'),
1503      ),
1504      'change-style' => array(
1505        'form_id' => 'views_ui_change_style_form',
1506        'args' => array('type', 'id'),
1507      ),
1508      'config-style' => array(
1509        'form_id' => 'views_ui_config_style_form',
1510        'args' => array('type', 'id'),
1511      ),
1512    );
1513  
1514    if ($key) {
1515      return !empty($forms[$key]) ? $forms[$key] : NULL;
1516    }
1517  
1518    return $forms;
1519  }
1520  
1521  /**
1522   * Build a form identifier that we can use to see if one form
1523   * is the same as another. Since the arguments differ slightly
1524   * we do a lot of spiffy concenation here.
1525   */
1526  function views_ui_build_identifier($key, $view, $display_id, $args) {
1527    $form = views_ui_ajax_forms($key);
1528    $identifier = implode('-', array($key, $view->name, $display_id));
1529  
1530    foreach ($form['args'] as $id) {
1531      $arg = (!empty($args)) ? array_shift($args) : NULL;
1532      $identifier .= '-' . $arg;
1533    }
1534    return $identifier;
1535  }
1536  
1537  /**
1538   * Build up a $form_state object suitable for use with drupal_build_form
1539   * based on known information about a form.
1540   */
1541  function views_ui_build_form_state($js, $key, &$view, $display_id, $args) {
1542    $form = views_ui_ajax_forms($key);
1543    // Build up form state
1544    $form_state = array(
1545      'form_key' => $key,
1546      'form_id' => $form['form_id'],
1547      'view' => &$view,
1548      'ajax' => $js,
1549      'display_id' => $display_id,
1550      'no_redirect' => TRUE,
1551    );
1552  
1553    foreach ($form['args'] as $id) {
1554      $form_state[$id] = (!empty($args)) ? array_shift($args) : NULL;
1555    }
1556  
1557    return $form_state;
1558  }
1559  
1560  /**
1561   * Create the URL for one of our standard AJAX forms based upon known
1562   * information about the form.
1563   */
1564  function views_ui_build_form_url($form_state) {
1565    $form = views_ui_ajax_forms($form_state['form_key']);
1566    $ajax = empty($form_state['ajax']) ? 'nojs' : 'ajax';
1567    $name = $form_state['view']->name;
1568    $url = "admin/build/views/$ajax/$form_state[form_key]/$name/$form_state[display_id]";
1569    foreach ($form['args'] as $arg) {
1570      $url .= '/' . $form_state[$arg];
1571    }
1572    return $url;
1573  }
1574  
1575  /**
1576   * Add another form to the stack; clicking 'update' will go to this form
1577   * rather than closing the ajax pad.
1578   */
1579  function views_ui_add_form_to_stack($key, &$view, $display_id, $args, $top = FALSE) {
1580    if (empty($view->stack)) {
1581      $view->stack = array();
1582    }
1583  
1584    $stack = array(views_ui_build_identifier($key, $view, $display_id, $args), $key, &$view, $display_id, $args);
1585    if ($top) {
1586      array_unshift($view->stack, $stack);
1587    }
1588    else {
1589      $view->stack[] = $stack;
1590    }
1591  }
1592  
1593  /**
1594   * Generic entry point to handle forms.
1595   *
1596   * We do this for consistency and to make it easy to chain forms
1597   * together. This only works for forms that use both $view
1598   * and $display_id, so we have a couple of ajax forms that we don't
1599   * use with this system.
1600   */
1601  function views_ui_ajax_form($js, $key, $view, $display_id) {
1602    $form = views_ui_ajax_forms($key);
1603    if (empty($form)) {
1604      return drupal_not_found();
1605    }
1606  
1607    views_include('ajax');
1608    $args = func_get_args();
1609    // Remove the known args
1610    array_splice($args, 0, 4);
1611  
1612    $form_state = views_ui_build_form_state($js, $key, $view, $display_id, $args);
1613    // check to see if this is the top form of the stack. If it is, pop
1614    // it off; if it isn't, the user clicked somewhere else and the stack is
1615    // now irrelevant.
1616    if (!empty($view->stack)) {
1617      $identifier = views_ui_build_identifier($key, $view, $display_id, $args);
1618      $top = array_shift($view->stack);
1619      if (array_shift($top) != $identifier) {
1620        $view->stack = array();
1621      }
1622    }
1623  
1624    $output = views_ajax_form_wrapper($form_state['form_id'], $form_state);
1625  
1626    if (!$output) {
1627      // Sometimes we need to re-generate the form for multi-step type operations.
1628      $object = NULL;
1629      if (!empty($view->stack)) {
1630        $stack = $view->stack; // copy so the next shift doesn't break the array
1631        $top = array_shift($stack);
1632        $top[0] = $js; // change identifier into $js setting
1633        $stepview = $top[2]; // Change view into a reference [#452384]
1634        $top[2] = &$stepview;
1635        $form_state = call_user_func_array('views_ui_build_form_state', $top);
1636        $form_state['input'] = array(); // this is a new form, make sure it
1637        // doesn't try to inherit $_POST info.
1638        if (!$js) {
1639          return drupal_goto(views_ui_build_form_url($form_state));
1640        }
1641        $object = views_ajax_form_wrapper($form_state['form_id'], $form_state);
1642        $object->url = url(views_ui_build_form_url($form_state));
1643      }
1644      else if (!$js) {
1645        // if nothing on the stack, non-js forms just go back to the main view editor.
1646        return drupal_goto("admin/build/views/edit/$view->name");
1647      }
1648      // regenerate all tabs because changes to the default tab could ripple.
1649      return views_ui_regenerate_tabs($view, NULL, $object);
1650    }
1651  
1652    return ($js) ? views_ajax_render($output) : $output;
1653  }
1654  
1655  /**
1656   * AJAX callback to add a display.
1657   */
1658  function views_ui_add_display($js, $view) {
1659    views_include('ajax');
1660    $form_state = array(
1661      'view' => &$view,
1662      'ajax' => $js,
1663    );
1664  
1665    $output = views_ajax_form_wrapper('views_ui_add_display_form', $form_state);
1666  
1667    if ($js) {
1668      // If we don't have an output object, it was submitted. Set up the submission.
1669      if (empty($output)) {
1670        $id = $form_state['id'];
1671  
1672        // Make sure the new display is active
1673        if (!$view->set_display('default')) {
1674          views_ajax_render(t('Unable to initialize default display'));
1675        }
1676  
1677        // Render the new display
1678        list($title, $body) = views_ui_display_tab($view, $view->display[$id]);
1679  
1680        // Instruct the javascript on the browser to render the new tab.
1681        $output = new stdClass;
1682        $output->tab = array('#views-tab-' . $id => array('title' => $title, 'body' => $body));
1683      }
1684      // Render the command object. This automatically exits.
1685      views_ajax_render($output);
1686    }
1687  
1688    // But the non-js variant will return output if it didn't redirect us.
1689    return $output;
1690  }
1691  
1692  /**
1693   * Form to add a display to a view.
1694   */
1695  function views_ui_add_display_form(&$form_state) {
1696    $view = &$form_state['view'];
1697  
1698    $form['display']['display'] = array(
1699      '#type' => 'select',
1700      '#options' => views_fetch_plugin_names('display'),
1701      '#default_value' => 'page',
1702    );
1703  
1704    $form['display']['add_display'] = array(
1705      '#type' => 'submit',
1706      '#value' => t('Add display'),
1707      '#submit' => array('views_ui_add_display_form_submit'),
1708    );
1709  
1710    $form['#id'] = 'views-add-display-form';
1711    $form['#attributes'] = array('class' => 'views-ajax-form');
1712    $form['#action'] = url("admin/build/views/nojs/add-display/$view->name");
1713  
1714    return $form;
1715  }
1716  
1717  /**
1718   * Submit handler to add a display to a view.
1719   */
1720  function views_ui_add_display_form_submit($form, &$form_state) {
1721    // Create the new display
1722    $plugin = $form_state['values']['display'];
1723    $form_state['id'] = $form_state['view']->add_display($plugin);
1724  
1725    // Store in cache
1726    views_ui_cache_set($form_state['view']);
1727  
1728    // Send it back
1729    $form_state['redirect'] = array('admin/build/views/edit/' . $form_state['view']->name, NULL, 'views-tab-' . $form_state['id']);
1730  }
1731  
1732  /**
1733   * AJAX callback to add a display.
1734   */
1735  function views_ui_clone_display($js, $view, $id) {
1736    views_include('ajax');
1737    $form_state = array(
1738      'view' => &$view,
1739      'ajax' => $js,
1740      'display_id' => $id,
1741    );
1742  
1743    $output = views_ajax_form_wrapper('views_ui_clone_display_form', $form_state);
1744  
1745    if ($js) {
1746      // If we don't have an output object, it was submitted. Set up the submission.
1747      if (empty($output)) {
1748        $id = $form_state['id'];
1749  
1750        // Make sure the new display is active
1751        if (!$view->set_display('default')) {
1752          views_ajax_render(t('Unable to initialize default display'));
1753        }
1754  
1755        // Render the new display
1756        list($title, $body) = views_ui_display_tab($view, $view->display[$id]);
1757  
1758        // Instruct the javascript on the browser to render the new tab.
1759        $output = new stdClass;
1760        $output->tab = array('#views-tab-' . $id => array('title' => $title, 'body' => $body));
1761      }
1762      // Render the command object. This automatically exits.
1763      views_ajax_render($output);
1764    }
1765  
1766    // But the non-js variant will return output if it didn't redirect us.
1767    return $output;
1768  }
1769  
1770  /**
1771   * From to clone a display from a view.
1772   */
1773  function views_ui_clone_display_form(&$form_state) {
1774    $view = &$form_state['view'];
1775    $display_id = $form_state['display_id'];
1776  
1777    $form['clone_display'] = array(
1778      '#type' => 'submit',
1779      '#value' => t('Clone display'),
1780      '#submit' => array('views_ui_clone_display_form_submit'),
1781    );
1782  
1783    $form['#id'] = 'views-clone-display-form';
1784    $form['#action'] = url("admin/build/views/nojs/clone-display/$view->name/$display_id");
1785    $form['#attributes'] = array('class' => 'views-ajax-form');
1786  
1787    return $form;
1788  }
1789  
1790  /**
1791   * Submit handler to add a clone to a display from a view.
1792   */
1793  function views_ui_clone_display_form_submit($form, &$form_state) {
1794    // Create the new display
1795    $id = $form_state['display_id'];
1796    $display = $form_state['view']->display[$id];
1797  
1798    $new_id = $form_state['view']->add_display($display->display_plugin);
1799    $form_state['id'] = $new_id;
1800  
1801    // Replace the new display by a copy of the old
1802    $form_state['view']->display[$new_id] = drupal_clone($display);
1803    $form_state['view']->display[$new_id]->id = $new_id;
1804  
1805    // Store in cache
1806    views_ui_cache_set($form_state['view']);
1807  
1808    // Send it back
1809    $form_state['redirect'] = array('admin/build/views/edit/' . $form_state['view']->name, NULL, 'views-tab-' . $new_id);
1810  }
1811  
1812  /**
1813   * Form to remove a display from a view.
1814   */
1815  function views_ui_remove_display_form(&$form_state) {
1816    $view = &$form_state['view'];
1817    $display_id = $form_state['display_id'];
1818  
1819    if (empty($view->display[$display_id]->deleted)) {
1820      $form['display'] = array(
1821        '#prefix' => '<div class="display-button remove-display">',
1822        '#suffix' => '</div>',
1823      );
1824      $form['remove_display'] = array(
1825        '#type' => 'submit',
1826        '#value' => t('Remove display'),
1827        '#submit' => array('views_ui_remove_display_form_submit'),
1828      );
1829    }
1830    else {
1831      $form['display'] = array(
1832        '#prefix' => '<div class="display-button restore-display">',
1833        '#suffix' => '</div>',
1834      );
1835      $form['restore_display'] = array(
1836        '#type' => 'submit',
1837        '#value' => t('Restore display'),
1838        '#submit' => array('views_ui_remove_display_form_restore'),
1839      );
1840    }
1841    $form['#action'] = url("admin/build/views/nojs/remove-display/$view->name/$display_id");
1842    $form['#attributes'] = array('class' => 'views-ajax-form');
1843  
1844    return $form;
1845  }
1846  
1847  /**
1848   * Submit handler to add a remove to a display from a view.
1849   */
1850  function views_ui_remove_display_form_submit($form, &$form_state) {
1851    // Create the new display
1852    $plugin = views_fetch_plugin_data('display', $form_state['view']->display[$form_state['display_id']]->display_plugin);
1853    if (empty($plugin['no remove'])) {
1854      $id = $form_state['display_id'];
1855      $form_state['view']->display[$id]->deleted = TRUE;
1856  
1857      // Store in cache
1858      views_ui_cache_set($form_state['view']);
1859    }
1860  }
1861  
1862  /**
1863   * Submit handler to add a restore a removed display to a view.
1864   */
1865  function views_ui_remove_display_form_restore($form, &$form_state) {
1866    // Create the new display
1867    $id = $form_state['display_id'];
1868    $form_state['view']->display[$id]->deleted = FALSE;
1869  
1870    // Store in cache
1871    views_ui_cache_set($form_state['view']);
1872  }
1873  
1874  /**
1875   * Page callback to display analysis information on a view.
1876   */
1877  function views_ui_analyze_view($js, $view) {
1878    views_include('ajax');
1879    $form_state = array(
1880      'view' => &$view,
1881      'ajax' => $js,
1882    );
1883  
1884    $output = views_ajax_form_wrapper('views_ui_analyze_view_form', $form_state);
1885  
1886    if ($js) {
1887      // If we don't have an output object, it was submitted. Set up the submission.
1888      if (empty($output)) {
1889        return views_ui_regenerate_tabs($view);
1890      }
1891      return views_ajax_render($output);
1892  
1893    }
1894    return $output;
1895  }
1896  
1897  /**
1898   * This form doesn't particularly do much; it's really just providing a link
1899   * but a button seems like it would be nicer here.
1900   *
1901   * It has no submit or anything, as we will never actually submit this form
1902   * where the form is placed.
1903   */
1904  function views_ui_analyze_view_button(&$form_state, $view) {
1905    $form['#action'] = url("admin/build/views/nojs/analyze/$view->name");
1906    $form['#attributes'] = array('class' => 'views-ajax-form');
1907    $form['submit'] = array(
1908      '#type' => 'submit',
1909      '#value' => t('Analyze'),
1910    );
1911  
1912    return $form;
1913  }
1914  
1915  /**
1916   * Form constructor callback to display analysis information on a view
1917   */
1918  function views_ui_analyze_view_form(&$form_state) {
1919    $view = &$form_state['view'];
1920  
1921    $form['#title'] = t('View analysis');
1922    $form['#section'] = 'analyze';
1923  
1924    views_include('analyze');
1925    $messages = views_analyze_view($view);
1926  
1927    $form['analysis'] = array(
1928      '#prefix' => '<div class="form-item">',
1929      '#suffix' => '</div>',
1930      '#value' => views_analyze_format_result($view, $messages),
1931    );
1932  
1933    // Inform the standard button function that we want an OK button.
1934    $form_state['ok_button'] = TRUE;
1935    views_ui_standard_form_buttons($form, $form_state, 'views_ui_analyze_view_form');
1936    return $form;
1937  }
1938  
1939  /**
1940   * Submit handler for views_ui_analyze_view_form
1941   */
1942  function views_ui_analyze_view_form_submit($form, &$form_state) {
1943    $form_state['redirect'] = 'admin/build/views/edit/' . $form_state['view']->name;
1944  }
1945  
1946  /**
1947   * Page callback to edit details of a view.
1948   */
1949  function views_ui_edit_details($js, $view) {
1950    views_include('ajax');
1951    $form_state = array(
1952      'view' => &$view,
1953      'ajax' => $js,
1954    );
1955  
1956    $output = views_ajax_form_wrapper('views_ui_edit_details_form', $form_state);
1957  
1958    if ($js) {
1959      // If we don't have an output object, it was submitted. Set up the submission.
1960      if (empty($output)) {
1961        return views_ui_regenerate_tabs($view);
1962      }
1963      return views_ajax_render($output);
1964  
1965    }
1966    return $output;
1967  }
1968  
1969  /**
1970   * Form constructor callback to edit details of a view
1971   */
1972  function views_ui_edit_details_form(&$form_state) {
1973    $view = &$form_state['view'];
1974  
1975    $form['#title'] = t('View details');
1976    $form['#section'] = 'details';
1977  
1978    $form['description'] = array(
1979      '#type' => 'textfield',
1980      '#title' => t('View description'),
1981      '#description' => t('This description will appear on the Views administrative UI to tell you what the view is about.'),
1982      '#default_value' => $view->description,
1983    );
1984  
1985    $form['tag'] = array(
1986      '#type' => 'textfield',
1987      '#title' => t('View tag'),
1988      '#description' => t('Enter an optional tag for this view; it is used only to help sort views on the administrative page.'),
1989      '#default_value' => $view->tag,
1990      '#autocomplete_path' => 'admin/views/ajax/autocomplete/tag',
1991    );
1992  
1993    views_ui_standard_form_buttons($form, $form_state, 'views_ui_edit_details_form');
1994    return $form;
1995  }
1996  
1997  /**
1998   * Submit handler for views_ui_edit_details_form
1999   */
2000  function views_ui_edit_details_form_submit($form, &$form_state) {
2001    $form_state['view']->description = $form_state['values']['description'];
2002    $form_state['view']->tag = $form_state['values']['tag'];
2003    views_ui_cache_set($form_state['view']);
2004    $form_state['redirect'] = 'admin/build/views/edit/' . $form_state['view']->name;
2005  }
2006  
2007  /**
2008   * Form constructor callback to edit display of a view
2009   */
2010  function views_ui_edit_display_form(&$form_state) {
2011    $view = &$form_state['view'];
2012    $display_id = $form_state['display_id'];
2013    $section = $form_state['section'];
2014  
2015    if (!$view->set_display($display_id)) {
2016      views_ajax_render(t('Invalid display id @display', array('@display' => $display_id)));
2017    }
2018    $display = &$view->display[$display_id];
2019  
2020    // Get form from the handler.
2021    $display->handler->options_form($form, $form_state);
2022    $name = NULL;
2023    if (isset($form_state['update_name'])) {
2024      $name = $form_state['update_name'];
2025    }
2026  
2027    views_ui_standard_form_buttons($form, $form_state, 'views_ui_edit_display_form', $name);
2028    return $form;
2029  }
2030  
2031  /**
2032   * Validate handler for views_ui_edit_display_form
2033   */
2034  function views_ui_edit_display_form_validate($form, &$form_state) {
2035    $display = &$form_state['view']->display[$form_state['display_id']];
2036    $display->handler->options_validate($form, $form_state);
2037  }
2038  
2039  /**
2040   * Submit handler for views_ui_edit_display_form
2041   */
2042  function views_ui_edit_display_form_submit($form, &$form_state) {
2043    $display = &$form_state['view']->display[$form_state['display_id']];
2044    $display->handler->options_submit($form, $form_state);
2045  
2046    views_ui_cache_set($form_state['view']);
2047  }
2048  
2049  /**
2050   * Override handler for views_ui_edit_display_form
2051   */
2052  function views_ui_edit_display_form_override($form, &$form_state) {
2053    $display = &$form_state['view']->display[$form_state['display_id']];
2054    $display->handler->options_override($form, $form_state);
2055  
2056    views_ui_cache_set($form_state['view']);
2057    $form_state['rerender'] = TRUE;
2058    $form_state['rebuild'] = TRUE;
2059  }
2060  /**
2061   * Override handler and submit views_ui_edit_display_form
2062   */
2063  function views_ui_edit_display_form_override_update(&$form, &$form_state) {
2064    $display = &$form_state['view']->display[$form_state['display_id']];
2065    $display->handler->options_override($form, $form_state);
2066    $display->handler->options_submit($form, $form_state);
2067    views_ui_cache_set($form_state['view']);
2068  }
2069  
2070  /**
2071   * Override handler and submit views_ui_edit_display_form
2072   */
2073  function views_ui_edit_display_form_override_update_section(&$form, &$form_state) {
2074    // Update the #section so it knows what to mark changed.
2075    $form['#section'] = str_replace('default-', $form_state['display_id'] . '-', $form['#section']);
2076  }
2077  
2078  /**
2079   * Form to config items in the views UI.
2080   */
2081  function views_ui_config_type_form(&$form_state) {
2082    $view = &$form_state['view'];
2083    $display_id = $form_state['display_id'];
2084    $type = $form_state['type'];
2085  
2086    $types = views_object_types();
2087    if (!$view->set_display($display_id)) {
2088      views_ajax_render(t('Invalid display id @display', array('@display' => $display_id)));
2089    }
2090    $display = &$view->display[$display_id];
2091    $form['#title'] = check_plain($display->display_title) . ': ';
2092    $form['#title'] .= t('Configure @type', array('@type' => $types[$type]['ltitle']));
2093    $form['#section'] = $display_id . 'config-item';
2094  
2095    if ($display->handler->defaultable_sections($types[$type]['plural'])) {
2096      $form_state['section'] = $types[$type]['plural'];
2097      $display->handler->add_override_button($form, $form_state, $form_state['section']);
2098    }
2099  
2100    if (!empty($types[$type]['options']) && function_exists($types[$type]['options'])) {
2101      $options = $type . '_options';
2102      $form[$options] = array('#tree' => TRUE);
2103      $types[$type]['options']($form, $form_state);
2104    }
2105  
2106    $name = NULL;
2107    if (isset($form_state['update_name'])) {
2108      $name = $form_state['update_name'];
2109    }
2110  
2111    views_ui_standard_form_buttons($form, $form_state, 'views_ui_config_type_form', $name);
2112    return $form;
2113  }
2114  
2115  /**
2116   * Submit handler for type configuration form
2117   */
2118  function views_ui_config_type_form_submit($form, &$form_state) {
2119    $types = views_object_types();
2120    $display = &$form_state['view']->display[$form_state['display_id']];
2121  
2122    // Store in cache
2123    views_ui_cache_set($form_state['view']);
2124  }
2125  
2126  /**
2127   * Configure settings particular to filters.
2128   */
2129  function views_ui_config_filters_form(&$form, &$form_state) {
2130  
2131  }
2132  
2133  /**
2134   * Form to rearrange items in the views UI.
2135   */
2136  function views_ui_rearrange_form(&$form_state) {
2137    $view = &$form_state['view'];
2138    $display_id = $form_state['display_id'];
2139    $type = $form_state['type'];
2140  
2141    $types = views_object_types();
2142    if (!$view->set_display($display_id)) {
2143      views_ajax_render(t('Invalid display id @display', array('@display' => $display_id)));
2144    }
2145    $display = &$view->display[$display_id];
2146    $form['#title'] = check_plain($display->display_title) . ': ';
2147    $form['#title'] .= t('Rearrange @type', array('@type' => $types[$type]['ltitle']));
2148    $form['#section'] = $display_id . 'rearrange-item';
2149  
2150    if ($display->handler->defaultable_sections($types[$type]['plural'])) {
2151      $form_state['section'] = $types[$type]['plural'];
2152      $display->handler->add_override_button($form, $form_state, $form_state['section']);
2153    }
2154  
2155    $count = 0;
2156  
2157    // Get relationship labels
2158    $relationships = array();
2159    // @todo: get_handlers()
2160    foreach ($display->handler->get_option('relationships') as $id => $relationship) {
2161      $handler = views_get_handler($relationship['table'], $relationship['field'], 'relationship');
2162      if (empty($handler)) {
2163        continue;
2164      }
2165      $handler->init($view, $relationship);
2166      $relationships[$id] = $handler->label();
2167    }
2168  
2169    // @todo: get_handlers()
2170    $form['fields'] = array('#tree' => TRUE);
2171    foreach ($display->handler->get_option($types[$type]['plural']) as $id => $field) {
2172      $form['fields'][$id] = array('#tree' => TRUE);
2173      $form['fields'][$id]['weight'] = array(
2174        '#type' => 'weight',
2175        '#delta' => 200,
2176        '#default_value' => ++$count,
2177      );
2178      $handler = views_get_handler($field['table'], $field['field'], $type);
2179      if ($handler) {
2180        $handler->init($view, $field);
2181        $name = $handler->ui_name() . ' ' . $handler->admin_summary();
2182        if (!empty($field['relationship']) && !empty($relationships[$field['relationship']])) {
2183          $name = '(' . $relationships[$field['relationship']] . ') ' . $name;
2184        }
2185  
2186        $form['fields'][$id]['name'] = array(
2187          '#value' => $name,
2188        );
2189      }
2190      else {
2191        $form['fields'][$id]['name'] = array('#value' => t('Broken field @id', array('@id' => $id)));
2192      }
2193      $form['fields'][$id]['removed'] = array(
2194        '#type' => 'checkbox',
2195        '#id' => 'views-removed-' . $id,
2196        '#attributes' => array('class' => 'views-remove-checkbox'),
2197        '#default_value' => 0,
2198      );
2199    }
2200  
2201    // Add javascript settings that will be added via $.extend for tabledragging
2202    $form['#js']['tableDrag']['arrange']['weight'][0] = array(
2203      'target' => 'weight',
2204      'source' => NULL,
2205      'relationship' => 'sibling',
2206      'action' => 'order',
2207      'hidden' => TRUE,
2208      'limit' => 0,
2209    );
2210  
2211    $name = NULL;
2212    if (isset($form_state['update_name'])) {
2213      $name = $form_state['update_name'];
2214    }
2215  
2216    views_ui_standard_form_buttons($form, $form_state, 'views_ui_rearrange_form');
2217    return $form;
2218  }
2219  
2220  /**
2221   * Turn the rearrange form into a proper table
2222   */
2223  function theme_views_ui_rearrange_form($form) {
2224    $rows = array();
2225    foreach (element_children($form['fields']) as $id) {
2226      if (isset($form['fields'][$id]['name'])) {
2227        $row = array();
2228        $row[] = drupal_render($form['fields'][$id]['name']);
2229        $form['fields'][$id]['weight']['#attributes']['class'] = 'weight';
2230        $row[] = drupal_render($form['fields'][$id]['weight']);
2231        $row[] = drupal_render($form['fields'][$id]['removed']) . l('<span>' . t('Remove') . '</span>', 'javascript:void()', array('attributes' => array('id' => 'views-remove-link-' . $id, 'class' => 'views-button-remove views-remove-link', 'alt' => t('Remove this item'), 'title' => t('Remove this item')), 'html' => true));
2232  
2233        $rows[] = array('data' => $row, 'class' => 'draggable', 'id' => 'views-row-' . $id);
2234      }
2235    }
2236    if (empty($rows)) {
2237      $rows[] = array(array('data' => t('No fields available.'), 'colspan' => '2'));
2238    }
2239  
2240    $header = array('', t('Weight'), t('Remove'));
2241    drupal_add_tabledrag('arrange', 'order', 'sibling', 'weight');
2242    $output = drupal_render($form['override']);
2243    $output .= theme('table', $header, $rows, array('id' => 'arrange'));
2244    $output .= drupal_render($form);
2245    return $output;
2246  
2247  }
2248  
2249  /**
2250   * Submit handler for rearranging form
2251   */
2252  function views_ui_rearrange_form_submit($form, &$form_state) {
2253    $types = views_object_types();
2254    $display = &$form_state['view']->display[$form_state['display_id']];
2255  
2256    $old_fields = $display->handler->get_option($types[$form_state['type']]['plural']);
2257    $new_fields = $order = array();
2258  
2259    // Make an array with the weights
2260    foreach ($form_state['values']['fields'] as $field => $info) {
2261      // add each value that is a field with a weight to our list, but only if
2262      // it has had its 'removed' checkbox checked.
2263      if (is_array($info) && isset($info['weight']) && empty($info['removed'])) {
2264        $order[$field] = $info['weight'];
2265      }
2266    }
2267  
2268    // Sort the array
2269    asort($order);
2270  
2271    // Create a new list of fields in the new order.
2272    foreach (array_keys($order) as $field) {
2273      $new_fields[$field] = $old_fields[$field];
2274    }
2275    $display->handler->set_option($types[$form_state['type']]['plural'], $new_fields);
2276  
2277    // Store in cache
2278    views_ui_cache_set($form_state['view']);
2279  }
2280  
2281  /**
2282   * Form to add_item items in the views UI.
2283   */
2284  function views_ui_add_item_form(&$form_state) {
2285    $view = &$form_state['view'];
2286    $display_id = $form_state['display_id'];
2287    $type = $form_state['type'];
2288  
2289    if (!$view->set_display($display_id)) {
2290      views_ajax_render(t('Invalid display id @display', array('@display' => $display_id)));
2291    }
2292    $display = &$view->display[$display_id];
2293  
2294    $types = views_object_types();
2295    $form['#title'] = check_plain($display->display_title) . ': ';
2296    $form['#title'] .= t('Add @type', array('@type' => $types[$type]['ltitle']));
2297    $form['#section'] = $display_id . 'add-item';
2298  
2299    // Figure out all the base tables allowed based upon what the relationships provide.
2300    $base_tables = $view->get_base_tables();
2301    $options = views_fetch_fields(array_keys($base_tables), $type);
2302  
2303    if (!empty($options)) {
2304      $groups = array('all' => t('- All -'));
2305      $form['group'] = array(
2306        '#type' => 'select',
2307        '#title' => t('Groups'),
2308        '#options' => array(),
2309        '#attributes' => array('class' => 'views-master-dependent'),
2310      );
2311  
2312      $form['name'] = array(
2313        '#prefix' => '<div class="views-radio-box form-checkboxes">',
2314        '#suffix' => '</div>',
2315        '#tree' => TRUE,
2316        '#default_value' => 'all',
2317      );
2318  
2319      // Group options first to simplify the DOM objects that Views
2320      // dependent JS will act upon.
2321      $grouped_options = array();
2322      foreach ($options as $key => $option) {
2323        $group = preg_replace('/[^a-z0-9]/', '-', strtolower($option['group']));
2324        $groups[$group] = $option['group'];
2325        $grouped_options[$group][$key] = $option;
2326      }
2327  
2328      foreach ($grouped_options as $group => $group_options) {
2329        $form['name'][$group . '_start'] = array('#type' => 'markup', '#value' => '<div class="views-dependent-all views-dependent-' . $group . '">');
2330        foreach ($group_options as $key => $option) {
2331          $form['name'][$key] = array(
2332            '#type' => 'checkbox',
2333            '#title' => t('!group: !field', array('!group' => $option['group'], '!field' => $option['title'])),
2334            '#description' => $option['help'],
2335            '#return_value' => $key,
2336          );
2337        }
2338        $form['name'][$group . '_end'] = array('#type' => 'markup', '#value' => '</div>');
2339      }
2340  
2341      $form['group']['#options'] = $groups;
2342    }
2343    else {
2344      $form['markup'] = array(
2345        '#value' => '<div class="form-item">' . t('There are no @types available to add.', array('@types' =>  $types[$type]['ltitle'])) . '</div>',
2346      );
2347    }
2348    views_ui_standard_form_buttons($form, $form_state, 'views_ui_add_item_form', t('Add'));
2349  
2350    return $form;
2351  }
2352  
2353  /**
2354   * Submit handler for adding new item(s) to a view.
2355   */
2356  function views_ui_add_item_form_submit($form, &$form_state) {
2357    $type = $form_state['type'];
2358    $types = views_object_types();
2359  
2360    if (!empty($form_state['values']['name']) && is_array($form_state['values']['name'])) {
2361      // Loop through each of the items that were checked and add them to the view.
2362      foreach (array_keys(array_filter($form_state['values']['name'])) as $field) {
2363        list($table, $field) = explode('.', $field, 2);
2364        $id = $form_state['view']->add_item($form_state['display_id'], $type, $table, $field);
2365  
2366        // check to see if this type has settings, if so add the settings form first
2367        $handler = views_get_handler($table, $field, $type);
2368        if ($handler && $handler->has_extra_options()) {
2369          views_ui_add_form_to_stack('config-item-extra', $form_state['view'], $form_state['display_id'], array($type, $id));
2370        }
2371        // Then add the form to the stack
2372        views_ui_add_form_to_stack('config-item', $form_state['view'], $form_state['display_id'], array($type, $id));
2373      }
2374    }
2375  
2376    // Store in cache
2377    views_ui_cache_set($form_state['view']);
2378  }
2379  
2380  
2381  /**
2382   * Form to config_item items in the views UI.
2383   */
2384  function views_ui_config_item_form(&$form_state) {
2385    $view = &$form_state['view'];
2386    $display_id = $form_state['display_id'];
2387    $type = $form_state['type'];
2388    $id = $form_state['id'];
2389  
2390    $form = array('options' => array('#tree' => TRUE));
2391    if (!$view->set_display($display_id)) {
2392      views_ajax_render(t('Invalid display id @display', array('@display' => $display_id)));
2393    }
2394    $item = $view->get_item($display_id, $type, $id);
2395  
2396    if ($item) {
2397      $handler = views_get_handler($item['table'], $item['field'], $type);
2398      if (empty($handler)) {
2399        $form['markup'] = array('#value' => t("Error: handler for @table > @field doesn't exist!", array('@table' => $item['table'], '@field' => $item['field'])));
2400      }
2401      else {
2402        $handler->init($view, $item);
2403        $types = views_object_types();
2404  
2405        if ($view->display_handler->defaultable_sections($types[$type]['plural'])) {
2406          $form_state['section'] = $types[$type]['plural'];
2407          $view->display_handler->add_override_button($form['options'], $form_state, $form_state['section']);
2408        }
2409  
2410        // A whole bunch of code to figure out what relationships are valid for
2411        // this item.
2412        $relationships = $view->display_handler->get_option('relationships');
2413        $relationship_options = array();
2414  
2415        foreach ($relationships as $relationship) {
2416          // relationships can't link back to self. But also, due to ordering,
2417          // relationships can only link to prior relationships.
2418          if ($type == 'relationship' && $id == $relationship['id']) {
2419            break;
2420          }
2421          $relationship_handler = views_get_handler($relationship['table'], $relationship['field'], 'relationship');
2422          // ignore invalid/broken relationships.
2423          if (empty($relationship_handler)) {
2424            continue;
2425          }
2426  
2427          // If this relationship is valid for this type, add it to the list.
2428          $data = views_fetch_data($relationship['table']);
2429          $base = $data[$relationship['field']]['relationship']['base'];
2430          $base_fields = views_fetch_fields($base, $form_state['type']);
2431          if (isset($base_fields[$item['table'] . '.' . $item['field']])) {
2432            $relationship_handler->init($view, $relationship);
2433            $relationship_options[$relationship['id']] = $relationship_handler->label();
2434          }
2435        }
2436  
2437        if (!empty($relationship_options)) {
2438          // Make sure the existing relationship is even valid. If not, force
2439          // it to none.
2440          $base_fields = views_fetch_fields($view->base_table, $form_state['type']);
2441          if (isset($base_fields[$item['table'] . '.' . $item['field']])) {
2442            $relationship_options = array_merge(array('none' => t('Do not use a relationship')), $relationship_options);
2443          }
2444          $rel = empty($item['relationship']) ? 'none' : $item['relationship'];
2445          if (empty($relationship_options[$rel])) {
2446            // Pick the first relationship.
2447            $rel = key($relationship_options);
2448            // We want this relationship option to get saved even if the user
2449            // skips submitting the form.
2450            $view->set_item_option($display_id, $type, $id, 'relationship', $rel);
2451            $temp_view = $view->clone_view();
2452            views_ui_cache_set($temp_view);
2453          }
2454  
2455          $form['options']['relationship'] = array(
2456            '#type' => 'select',
2457            '#title' => t('Relationship'),
2458            '#options' => $relationship_options,
2459            '#default_value' => $rel,
2460          );
2461        }
2462        else {
2463          $form['options']['relationship'] = array(
2464            '#type' => 'value',
2465            '#value' => 'none',
2466          );
2467        }
2468  
2469        $form['#title'] = check_plain($view->display[$display_id]->display_title) . ': ';
2470        $form['#title'] .= t('Configure @type %item', array('@type' => $types[$type]['lstitle'], '%item' => $handler->ui_name()));
2471  
2472        $form['form_description'] = array(
2473          '#type' => 'markup',
2474          '#weight' => -1000,
2475          '#prefix' => '<div class="form-item description">',
2476          '#suffix' => '</div>',
2477          '#value' => $handler->definition['help'],
2478        );
2479  
2480        $form['#section'] = $display_id . '-' . $type . '-' . $id;
2481  
2482        // Get form from the handler.
2483        $handler->options_form($form['options'], $form_state);
2484        $form_state['handler'] = &$handler;
2485      }
2486  
2487      $name = NULL;
2488      if (isset($form_state['update_name'])) {
2489        $name = $form_state['update_name'];
2490      }
2491  
2492      views_ui_standard_form_buttons($form, $form_state, 'views_ui_config_item_form', $name, t('Remove'), 'remove');
2493    }
2494    return $form;
2495  }
2496  
2497  /**
2498   * Submit handler for configing new item(s) to a view.
2499   */
2500  function views_ui_config_item_form_validate($form, &$form_state) {
2501    $form_state['handler']->options_validate($form['options'], $form_state);
2502  }
2503  
2504  /**
2505   * Submit handler for configing new item(s) to a view.
2506   */
2507  function views_ui_config_item_form_submit($form, &$form_state) {
2508    // Run it through the handler's submit function.
2509    $form_state['handler']->options_submit($form['options'], $form_state);
2510    $item = $form_state['handler']->options;
2511  
2512    // Unset a button
2513    unset($form_state['values']['options']['expose_button']);
2514  
2515    // Store the data we're given.
2516    foreach ($form_state['values']['options'] as $key => $value) {
2517      $item[$key] = $value;
2518    }
2519  
2520    // Store the item back on the view
2521    $form_state['view']->set_item($form_state['display_id'], $form_state['type'], $form_state['id'], $item);
2522  
2523    $handler = views_get_handler($item['table'], $item['field'], $form_state['type']);
2524    $handler->init($form_state['view'], $item);
2525    if ($handler && $handler->needs_style_plugin()) {
2526      views_ui_add_form_to_stack('change-style', $form_state['view'], $form_state['display_id'], array($form_state['type'], $form_state['id']), TRUE);
2527    }
2528  
2529    // Write to cache
2530    views_ui_cache_set($form_state['view']);
2531  }
2532  
2533  /**
2534   * Submit handler for removing an item from a view
2535   */
2536  function views_ui_config_item_form_remove($form, &$form_state) {
2537    // Store the item back on the view
2538    $form_state['view']->set_item($form_state['display_id'], $form_state['type'], $form_state['id'], NULL);
2539  
2540    // Write to cache
2541    views_ui_cache_set($form_state['view']);
2542  }
2543  
2544  /**
2545   * Override handler for views_ui_edit_display_form
2546   */
2547  function views_ui_config_item_form_expose($form, &$form_state) {
2548    $item = &$form_state['handler']->options;
2549    // flip
2550    $item['exposed'] = empty($item['exposed']);
2551  
2552    // If necessary, set new defaults:
2553    if ($item['exposed']) {
2554      $form_state['handler']->expose_options();
2555    }
2556  
2557    $form_state['view']->set_item($form_state['display_id'], $form_state['type'], $form_state['id'], $item);
2558  
2559    views_ui_cache_set($form_state['view']);
2560    $form_state['rerender'] = TRUE;
2561    $form_state['rebuild'] = TRUE;
2562    $form_state['force_expose_options'] = TRUE;
2563  }
2564  
2565  /**
2566   * Form to config_item items in the views UI.
2567   */
2568  function views_ui_config_item_extra_form(&$form_state) {
2569    $view = &$form_state['view'];
2570    $display_id = $form_state['display_id'];
2571    $type = $form_state['type'];
2572    $id = $form_state['id'];
2573  
2574    $form = array('options' => array('#tree' => TRUE));
2575    if (!$view->set_display($display_id)) {
2576      views_ajax_render(t('Invalid display id @display', array('@display' => $display_id)));
2577    }
2578    $item = $view->get_item($display_id, $type, $id);
2579  
2580    if ($item) {
2581      $handler = views_get_handler($item['table'], $item['field'], $type);
2582      if (empty($handler)) {
2583        $form['markup'] = array('#value' => t("Error: handler for @table > @field doesn't exist!", array('@table' => $item['table'], '@field' => $item['field'])));
2584      }
2585      else {
2586        $handler->init($view, $item);
2587        $types = views_object_types();
2588  
2589        $form['#title'] = check_plain($view->display[$display_id]->display_title) . ': ';
2590        $form['#title'] .= t('Configure extra settings for @type %item', array('@type' => $types[$type]['lstitle'], '%item' => $handler->ui_name()));
2591  
2592        $form['#section'] = $display_id . '-' . $type . '-' . $id;
2593  
2594        // Get form from the handler.
2595        $handler->extra_options_form($form['options'], $form_state);
2596          $form_state['handler'] = &$handler;
2597  
2598      }
2599  
2600      views_ui_standard_form_buttons($form, $form_state, 'views_ui_config_item_extra_form');
2601    }
2602    return $form;
2603  }
2604  
2605  /**
2606   * Submit handler for configing new item(s) to a view.
2607   */
2608  function views_ui_config_item_extra_form_validate($form, &$form_state) {
2609    $form_state['handler']->extra_options_validate($form['options'], $form_state);
2610  }
2611  
2612  /**
2613   * Submit handler for configing new item(s) to a view.
2614   */
2615  function views_ui_config_item_extra_form_submit($form, &$form_state) {
2616    // Run it through the handler's submit function.
2617    $form_state['handler']->extra_options_submit($form['options'], $form_state);
2618    $item = $form_state['handler']->options;
2619  
2620    // Store the data we're given.
2621    foreach ($form_state['values']['options'] as $key => $value) {
2622      $item[$key] = $value;
2623    }
2624  
2625    // Store the item back on the view
2626    $form_state['view']->set_item($form_state['display_id'], $form_state['type'], $form_state['id'], $item);
2627  
2628    // Write to cache
2629    views_ui_cache_set($form_state['view']);
2630  }
2631  
2632  /**
2633   * Form to change_style items in the views UI.
2634   */
2635  function views_ui_change_style_form(&$form_state) {
2636    $view = &$form_state['view'];
2637    $display_id = $form_state['display_id'];
2638    $type = $form_state['type'];
2639    $id = $form_state['id'];
2640  
2641    $form = array('options' => array('#tree' => TRUE));
2642    if (!$view->set_display($display_id)) {
2643      views_ajax_render(t('Invalid display id @display', array('@display' => $display_id)));
2644    }
2645    $item = $view->get_item($display_id, $type, $id);
2646  
2647    if ($item) {
2648      $handler = views_get_handler($item['table'], $item['field'], $type);
2649      if (empty($handler)) {
2650        $form['markup'] = array('#value' => t("Error: handler for @table > @field doesn't exist!", array('@table' => $item['table'], '@field' => $item['field'])));
2651      }
2652      else {
2653        $handler->init($view, $item);
2654        $types = views_object_types();
2655        $form['#title'] = t('Change summary style for @type %item', array('@type' => $types[$type]['lstitle'], '%item' => $handler->ui_name()));
2656  
2657        $form['#section'] = $display_id . '-' . $type . '-' . $id . '-style-plugin';
2658  
2659        $form['style_plugin'] =  array(
2660          '#type' => 'radios',
2661          '#options' => views_fetch_plugin_names('style', 'summary'),
2662          '#default_value' => $item['style_plugin'],
2663        );
2664  
2665        $form_state['handler'] = &$handler;
2666      }
2667      views_ui_standard_form_buttons($form, $form_state, 'views_ui_change_style_form');
2668    }
2669    return $form;
2670  }
2671  
2672  function views_ui_change_style_form_validate($form, &$form_state) {
2673    // Run it through the handler's submit function.
2674    $form_state['handler']->options_validate($form['options'], $form_state);
2675  
2676    $plugin = views_get_plugin('style', $form_state['values']['style_plugin']);
2677    if (!$plugin) {
2678      form_error($form['style_plugin'], t('Internal error: broken plugin.'));
2679    }
2680  }
2681  
2682  /**
2683   * Submit handler for configing new item(s) to a view.
2684   */
2685  function views_ui_change_style_form_submit($form, &$form_state) {
2686    // Run it through the handler's submit function.
2687    $form_state['handler']->options_submit($form['options'], $form_state);
2688    $item = $form_state['handler']->options;
2689  
2690    $plugin = views_get_plugin('style', $form_state['values']['style_plugin']);
2691    if (!$plugin) {
2692      drupal_set_message(t('Internal error: broken plugin.'), 'error');
2693      return;
2694    }
2695  
2696    $plugin->init($form_state['view'], $form_state['view']->display[$form_state['display_id']]);
2697  
2698    // If changing style plugin, reset options to defaults.
2699    if (empty($item['style_plugin']) || $item['style_plugin'] != $form_state['values']['style_plugin']) {
2700      $item['style_options'] = $plugin->options;
2701    }
2702  
2703    // Store the data we're given.
2704    $item['style_plugin'] = $form_state['values']['style_plugin'];
2705  
2706    // Store the item back on the view
2707    $form_state['view']->set_item($form_state['display_id'], $form_state['type'], $form_state['id'], $item);
2708  
2709    if (!empty($plugin->definition['uses options'])) {
2710      views_ui_add_form_to_stack('config-style', $form_state['view'], $form_state['display_id'], array($form_state['type'], $form_state['id']), TRUE);
2711    }
2712  
2713    // Write to cache
2714    views_ui_cache_set($form_state['view']);
2715  }
2716  
2717  /**
2718   * Form to config_style items in the views UI.
2719   */
2720  function views_ui_config_style_form(&$form_state) {
2721    $view = &$form_state['view'];
2722    $display_id = $form_state['display_id'];
2723    $type = $form_state['type'];
2724    $id = $form_state['id'];
2725  
2726    $form = array('options' => array('#tree' => TRUE));
2727    if (!$view->set_display($display_id)) {
2728      views_ajax_render(t('Invalid display id @display', array('@display' => $display_id)));
2729    }
2730    $item = $view->get_item($display_id, $type, $id);
2731  
2732    if ($item) {
2733      $handler = views_get_handler($item['table'], $item['field'], $type);
2734      if (empty($handler)) {
2735        $form['markup'] = array('#value' => t("Error: handler for @table > @field doesn't exist!", array('@table' => $item['table'], '@field' => $item['field'])));
2736      }
2737      else {
2738        $handler->init($view, $item);
2739        $types = views_object_types();
2740  
2741        $form['#title'] = check_plain($view->display[$display_id]->display_title) . ': ';
2742        $form['#title'] .= t('Configure summary style for @type %item', array('@type' => $types[$type]['lstitle'], '%item' => $handler->ui_name()));
2743  
2744        $form['#section'] = $display_id . '-' . $type . '-style-options';
2745  
2746        $plugin = views_get_plugin('style', $item['style_plugin']);
2747        if ($plugin) {
2748          $form['style_options'] = array(
2749            '#tree' => TRUE,
2750          );
2751          $plugin->init($view, $view->display[$display_id], $item['style_options']);
2752  
2753          $plugin->options_form($form['style_options'], $form_state);
2754        }
2755  
2756        $form_state['handler'] = &$handler;
2757      }
2758      views_ui_standard_form_buttons($form, $form_state, 'views_ui_config_style_form');
2759    }
2760    return $form;
2761  }
2762  
2763  /**
2764   * Submit handler for configing new item(s) to a view.
2765   */
2766  function views_ui_config_style_form_submit($form, &$form_state) {
2767    // Run it through the handler's submit function.
2768    $form_state['handler']->options_submit($form['style_options'], $form_state);
2769    $item = $form_state['handler']->options;
2770  
2771    // Store the data we're given.
2772    $item['style_options'] = $form_state['values']['style_options'];
2773  
2774    // Store the item back on the view
2775    $form_state['view']->set_item($form_state['display_id'], $form_state['type'], $form_state['id'], $item);
2776  
2777    // Write to cache
2778    views_ui_cache_set($form_state['view']);
2779  }
2780  
2781  /**
2782   * Get a list of roles in the system.
2783   */
2784  function views_ui_get_roles() {
2785    static $roles = NULL;
2786    if (!isset($roles)) {
2787      $roles = array();
2788      $result = db_query("SELECT r.rid, r.name FROM {role} r ORDER BY r.name");
2789      while ($obj = db_fetch_object($result)) {
2790        $roles[$obj->rid] = $obj->name;
2791      }
2792    }
2793  
2794    return $roles;
2795  }
2796  
2797  /**
2798   * Get a css safe id for a particular section.
2799   */
2800  function views_ui_item_css($item) {
2801    return views_css_safe('views-item-' . $item);
2802  }
2803  
2804  /**
2805   * Page callback for the Views enable page.
2806   */
2807  function views_ui_enable_page($view) {
2808    if (isset($_GET['token']) && drupal_valid_token($_GET['token'], 'views-enable')) {
2809      $views_status = variable_get('views_defaults', array());
2810      $views_status[$view->name] = FALSE; // false is enabled
2811      variable_set('views_defaults', $views_status);
2812      views_invalidate_cache();
2813      menu_rebuild();
2814      drupal_goto('admin/build/views');
2815    }
2816    else {
2817      return drupal_access_denied();
2818    }
2819  }
2820  
2821  /**
2822   * Page callback for the Views enable page
2823   */
2824  function views_ui_disable_page($view) {
2825    if (isset($_GET['token']) && drupal_valid_token($_GET['token'], 'views-disable')) {
2826      $views_status = variable_get('views_defaults', array());
2827      $views_status[$view->name] = TRUE; // True is disabled
2828      variable_set('views_defaults', $views_status);
2829      views_invalidate_cache();
2830      menu_rebuild();
2831      drupal_goto('admin/build/views');
2832    }
2833    else {
2834      return drupal_access_denied();
2835    }
2836  }
2837  
2838  /**
2839   * Page callback for the tools - other page
2840   */
2841  function views_ui_admin_tools() {
2842    $form['clear_cache'] = array(
2843      '#type' => 'submit',
2844      '#value' => t("Clear Views' cache"),
2845      '#submit' => array('views_ui_tools_clear_cache'),
2846    );
2847  
2848    $form['views_sql_signature'] = array(
2849      '#type' => 'checkbox',
2850      '#title' => t('Add Views signature to all SQL queries'),
2851      '#description' => t("All Views-generated queries will include a special 'VIEWS' = 'VIEWS' string in the WHERE clause. This makes identifying Views queries in database server logs simpler, but should only be used when troubleshooting."),
2852      '#default_value' => variable_get('views_sql_signature', FALSE),
2853    );
2854  
2855    $form['views_skip_cache'] = array(
2856      '#type' => 'checkbox',
2857      '#title' => t('Disable views data caching'),
2858      '#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."),
2859      '#default_value' => variable_get('views_skip_cache', FALSE),
2860    );
2861  
2862    $form['views_hide_help_message'] = array(
2863      '#type' => 'checkbox',
2864      '#title' => t('Ignore missing advanced help module'),
2865      '#description' => t("Views uses the advanced help module to provide help text; if this module is not present Views will complain, unless this setting is checked."),
2866      '#default_value' => variable_get('views_hide_help_message', FALSE),
2867    );
2868  
2869    $form['views_ui_query_on_top'] = array(
2870      '#type' => 'checkbox',
2871      '#title' => t('Show query above live preview'),
2872      '#description' => t("The live preview feature will show you the output of the view you're creating, as well as the view. Check here to show the query and other information above the view; leave this unchecked to show that information below the view."),
2873      '#default_value' => variable_get('views_ui_query_on_top', FALSE),
2874    );
2875  
2876    $form['views_ui_disable_live_preview'] = array(
2877      '#type' => 'checkbox',
2878      '#title' => t('Disable automatic live preview'),
2879      '#description' => t("Don't automatically update the preview. This can speed up the editing of views a bit.'"),
2880      '#default_value' => variable_get('views_ui_disable_live_preview', 0),
2881    );
2882  
2883    $form['views_show_additional_queries'] = array(
2884      '#type' => 'checkbox',
2885      '#title' => t('Show other queries run during render during live preview'),
2886      '#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."),
2887      '#default_value' => variable_get('views_show_additional_queries', FALSE),
2888    );
2889  
2890    $form['views_no_hover_links'] = array(
2891      '#type' => 'checkbox',
2892      '#title' => t('Do not show hover links over views'),
2893      '#description' => t("To make it easier to administrate your views, Views provides 'hover' links to take you to the edit and export screen of a view whenever the view is used. This can be distracting on some themes, though; if it is problematic, you can turn it off here."),
2894      '#default_value' => variable_get('views_no_hover_links', FALSE),
2895    );
2896  
2897    $form['views_devel_output'] = array(
2898      '#type' => 'checkbox',
2899      '#title' => t('Enable views performance statistics via the Devel module'),
2900      '#description' => t("Check this to enable some Views query and performance statistics <em>if Devel is installed</em>."),
2901      '#default_value' => variable_get('views_devel_output', FALSE),
2902    );
2903  
2904    $form['views_no_javascript'] = array(
2905      '#type' => 'checkbox',
2906      '#title' => t('Disable javascript with Views'),
2907      '#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 just not as good."),
2908      '#default_value' => variable_get('views_no_javascript', FALSE),
2909    );
2910  
2911    $regions = system_region_list(variable_get('theme_default', 'garland'));
2912    $regions['watchdog'] = t('Watchdog');
2913  
2914    $form['views_devel_region'] = array(
2915      '#type' => 'select',
2916      '#title' => t('Page region to output performance statistics'),
2917      '#default_value' => variable_get('views_devel_region', 'footer'),
2918      '#options' => $regions,
2919    );
2920  
2921    $form['views_exposed_filter_any_label'] = array(
2922      '#type' => 'select',
2923      '#title' => t('Label for "Any" value on optional single-select exposed filters'),
2924      '#options' => array('old_any' => '<Any>', 'new_any' => t('- Any -')),
2925      '#default_value' => variable_get('views_exposed_filter_any_label', 'old_any'),
2926    );
2927  
2928    return system_settings_form($form);
2929  }
2930  
2931  /**
2932   * Submit hook to clear the views cache.
2933   */
2934  function views_ui_tools_clear_cache() {
2935    views_invalidate_cache();
2936    drupal_set_message(t('The cache has been cleared.'));
2937  }
2938  
2939  /**
2940   * Submit hook to clear Drupal's theme registry (thereby triggering
2941   * a templates rescan).
2942   */
2943  function views_ui_config_item_form_rescan($form, &$form_state) {
2944    drupal_rebuild_theme_registry();
2945  
2946    // The 'Theme: Information' page is about to be shown again. That page
2947    // analyzes the output of theme_get_registry(). However, this latter
2948    // function uses an internal cache (which was initialized before we
2949    // called drupal_rebuild_theme_registry()) so it won't reflect the
2950    // current state of our theme registry. The only way to clear that cache
2951    // is to re-initialize the theme system:
2952    unset($GLOBALS['theme']);
2953    init_theme();
2954  
2955    $form_state['rerender'] = TRUE;
2956    $form_state['rebuild'] = TRUE;
2957  }
2958  
2959  /**
2960   * Override handler for views_ui_edit_display_form
2961   */
2962  function views_ui_edit_display_form_change_theme($form, &$form_state) {
2963    // This is just a temporary variable.
2964    $form_state['view']->theme = $form_state['values']['theme'];
2965  
2966    views_ui_cache_set($form_state['view']);
2967    $form_state['rerender'] = TRUE;
2968    $form_state['rebuild'] = TRUE;
2969  }
2970  
2971  /**
2972   * Page callback for views tag autocomplete
2973   */
2974  function views_ui_autocomplete_tag($string = '') {
2975    $matches = array();
2976    // get matches from default views:
2977    views_include('view');
2978    $views = views_discover_default_views();
2979    foreach ($views as $view) {
2980      if (!empty($view->tag) && strpos($view->tag, $string) === 0) {
2981        $matches[$view->tag] = $view->tag;
2982      }
2983    }
2984  
2985    if ($string) {
2986      $result = db_query_range("SELECT DISTINCT tag FROM {views_view} WHERE LOWER(tag) LIKE LOWER('%s%%')", $string, 0, 10 - count($matches));
2987      while ($view = db_fetch_object($result)) {
2988        $matches[$view->tag] = check_plain($view->tag);
2989      }
2990    }
2991  
2992    drupal_json($matches);
2993  }
2994  
2995  // ------------------------------------------------------------------
2996  // Get information from the Views data
2997  
2998  function _views_weight_sort($a, $b) {
2999    if ($a['weight'] != $b['weight']) {
3000      return $a['weight'] < $b['weight'] ? -1 : 1;
3001    }
3002    if ($a['title'] != $b['title']) {
3003      return $a['title'] < $b['title'] ? -1 : 1;
3004    }
3005  
3006    return 0;
3007  }
3008  
3009  /**
3010   * Fetch a list of all base tables available
3011   *
3012   * @return
3013   *   A keyed array of in the form of 'base_table' => 'Description'.
3014   */
3015  function views_fetch_base_tables() {
3016    static $base_tables = array();
3017    if (empty($base_tables)) {
3018      $weights = array();
3019      $tables = array();
3020      $data = views_fetch_data();
3021      foreach ($data as $table => $info) {
3022        if (!empty($info['table']['base'])) {
3023          $tables[$table] = array(
3024            'title' => $info['table']['base']['title'],
3025            'description' => $info['table']['base']['help'],
3026            'weight' => !empty($info['table']['base']['weight']) ? $info['table']['base']['weight'] : 0,
3027          );
3028        }
3029      }
3030      uasort($tables, '_views_weight_sort');
3031      $base_tables = $tables;
3032    }
3033  
3034    return $base_tables;
3035  }
3036  
3037  function _views_sort_types($a, $b) {
3038    if ($a['group'] != $b['group']) {
3039      return $a['group'] < $b['group'] ? -1 : 1;
3040    }
3041  
3042    if ($a['title'] != $b['title']) {
3043      return $a['title'] < $b['title'] ? -1 : 1;
3044    }
3045  
3046    return 0;
3047  }
3048  
3049  /**
3050   * Fetch a list of all fields available for a given base type.
3051   *
3052   * @return
3053   *   A keyed array of in the form of 'base_table' => 'Description'.
3054   */
3055  function views_fetch_fields($base, $type) {
3056    static $fields = array();
3057    if (empty($fields)) {
3058      $data = views_fetch_data();
3059      $start = views_microtime();
3060      // This constructs this ginormous multi dimensional array to
3061      // collect the important data about fields. In the end,
3062      // the structure looks a bit like this (using nid as an example)
3063      // $strings['nid']['filter']['title'] = 'string'.
3064      //
3065      // This is constructed this way because the above referenced strings
3066      // can appear in different places in the actual data structure so that
3067      // the data doesn't have to be repeated a lot. This essentially lets
3068      // each field have a cheap kind of inheritance.
3069  
3070      foreach ($data as $table => $table_data) {
3071        $bases = array();
3072        $strings = array();
3073        $skip_bases = array();
3074        foreach ($table_data as $field => $info) {
3075          // Collect table data from this table
3076          if ($field == 'table') {
3077            // calculate what tables this table can join to.
3078            if (!empty($info['join'])) {
3079              $bases = array_keys($info['join']);
3080            }
3081            // And it obviously joins to itself.
3082            $bases[] = $table;
3083            continue;
3084          }
3085          foreach (array('field', 'sort', 'filter', 'argument', 'relationship') as $key) {
3086            if (!empty($info[$key])) {
3087              if (!empty($info[$key]['skip base'])) {
3088                foreach ((array) $info[$key]['skip base'] as $base_name) {
3089                  $skip_bases[$field][$key][$base_name] = TRUE;
3090                }
3091              }
3092              elseif (!empty($info['skip base'])) {
3093                foreach ((array) $info['skip base'] as $base_name) {
3094                  $skip_bases[$field][$key][$base_name] = TRUE;
3095                }
3096              }
3097              foreach (array('title', 'group', 'help', 'base') as $string) {
3098                // First, try the lowest possible level
3099                if (!empty($info[$key][$string])) {
3100                  $strings[$field][$key][$string] = $info[$key][$string];
3101                }
3102                // Then try the field level
3103                elseif (!empty($info[$string])) {
3104                  $strings[$field][$key][$string] = $info[$string];
3105                }
3106                // Finally, try the table level
3107                elseif (!empty($table_data['table'][$string])) {
3108                  $strings[$field][$key][$string] = $table_data['table'][$string];
3109                }
3110                else {
3111                  if ($string != 'base') {
3112                    $strings[$field][$key][$string] = t("Error: missing @component", array('@component' => $string));
3113                  }
3114                }
3115              }
3116            }
3117          }
3118        }
3119        foreach ($bases as $base_name) {
3120          foreach ($strings as $field => $field_strings) {
3121            foreach ($field_strings as $type_name => $type_strings) {
3122              if (empty($skip_bases[$field][$type_name][$base_name])) {
3123                $fields[$base_name][$type_name]["$table.$field"] = $type_strings;
3124              }
3125            }
3126          }
3127        }
3128      }
3129  //    vsm('Views UI data build time: ' . (views_microtime() - $start) * 1000 . ' ms');
3130    }
3131  
3132    // If we have an array of base tables available, go through them
3133    // all and add them together. Duplicate keys will be lost and that's
3134    // Just Fine.
3135    if (is_array($base)) {
3136      $strings = array();
3137      foreach ($base as $base_table) {
3138        if (isset($fields[$base_table][$type])) {
3139          $strings += $fields[$base_table][$type];
3140        }
3141      }
3142      uasort($strings, '_views_sort_types');
3143      return $strings;
3144    }
3145  
3146    if (isset($fields[$base][$type])) {
3147      uasort($fields[$base][$type], '_views_sort_types');
3148      return $fields[$base][$type];
3149    }
3150    return array();
3151  }
3152  
3153  /**
3154   * Fetch a list of all base tables available
3155   *
3156   * @param $type
3157   *   Either 'display', 'style' or 'row'
3158   * @param $key
3159   *   For style plugins, this is an optional type to restrict to. May be 'normal',
3160   *   'summary', 'feed' or others based on the neds of the display.
3161   * @param $base
3162   *   An array of possible base tables.
3163   *
3164   * @return
3165   *   A keyed array of in the form of 'base_table' => 'Description'.
3166   */
3167  function views_fetch_plugin_names($type, $key = NULL, $base = array()) {
3168    $data = views_fetch_plugin_data();
3169  
3170    $plugins[$type] = array();
3171  
3172    foreach ($data[$type] as $id => $plugin) {
3173      // Skip plugins that don't conform to our key.
3174      if ($key && (empty($plugin['type']) || $plugin['type'] != $key)) {
3175        continue;
3176      }
3177      if (empty($plugin['no ui']) && (empty($base) || empty($plugin['base']) || array_intersect($base, $plugin['base']))) {
3178        $plugins[$type][$id] = $plugin['title'];
3179      }
3180    }
3181  
3182    if (!empty($plugins[$type])) {
3183      asort($plugins[$type]);
3184      return $plugins[$type];
3185    }
3186    // fall-through
3187    return array();
3188  }
3189  
3190  
3191  /**
3192   * Theme the form for the table style plugin
3193   */
3194  function theme_views_ui_style_plugin_table($form) {
3195    $output = drupal_render($form['description_markup']);
3196  
3197    $header = array(
3198      t('Field'),
3199      t('Column'),
3200      t('Separator'),
3201      array(
3202        'data' => t('Sortable'),
3203        'align' => 'center',
3204      ),
3205      array(
3206        'data' => t('Default sort'),
3207        'align' => 'center',
3208      ),
3209    );
3210    $rows = array();
3211    foreach (element_children($form['columns']) as $id) {
3212      $row = array();
3213      $row[] = drupal_render($form['info'][$id]['name']);
3214      $row[] = drupal_render($form['columns'][$id]);
3215      $row[] = drupal_render($form['info'][$id]['separator']);
3216      if (!empty($form['info'][$id]['sortable'])) {
3217        $row[] = array(
3218          'data' => drupal_render($form['info'][$id]['sortable']),
3219          'align' => 'center',
3220        );
3221        $row[] = array(
3222          'data' => drupal_render($form['default'][$id]),
3223          'align' => 'center',
3224        );
3225      }
3226      else {
3227        $row[] = '';
3228        $row[] = '';
3229      }
3230      $rows[] = $row;
3231    }
3232  
3233    // Add the special 'None' row.
3234    $rows[] = array(t('None'), '', '', '', array('align' => 'center', 'data' => drupal_render($form['default'][-1])));
3235  
3236    $output .= theme('table', $header, $rows);
3237    $output .= drupal_render($form);
3238    return $output;
3239  }
3240  


Generated: Mon Jul 9 18:01:44 2012 Cross-referenced by PHPXref 0.7