4 * Provides the Views' administrative interface.
8 * Page callback to list views in the system.
10 function views_ui_list_views($arg = NULL
) {
12 return drupal_not_found();
15 $output = theme('views_ui_list_views');
16 views_ui_check_advanced_help();
21 * Check to see if the advanced help module is installed, and if not put up
24 * Only call this function if the user is already in a position for this to
27 function views_ui_check_advanced_help() {
28 if (variable_get('views_hide_help_message', FALSE
)) {
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'))));
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'))));
44 * Preprocess the list views theme
46 function template_preprocess_views_ui_list_views(&$vars) {
50 // Add some js for easier gui.
51 views_add_js('view-list');
53 $views = views_get_all_views();
55 $token_enable = drupal_get_token('views-enable');
56 $token_disable = drupal_get_token('views-disable');
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');
64 if (count($_GET) <= 1) {
65 if (isset($_SESSION['views']['#admin']) && is_array($_SESSION['views']['#admin'])) {
66 $_GET += $_SESSION['views']['#admin'];
70 $_SESSION['views']['#admin'] = $_GET;
71 unset($_SESSION['views']['#admin']['q']);
79 'no_redirect' => TRUE
,
82 $vars['widgets'] = drupal_build_form('views_ui_list_views_form', $form_state);
84 $vars['help_type_icon'] = theme('advanced_help_topic', 'views', 'view-type');
86 $base_tables = views_fetch_base_tables();
88 foreach ($views as
$view) {
89 if ($form_state['values']['tag'] != 'all') {
90 if ($form_state['values']['tag'] == 'none') {
91 if (!empty($view->tag
)) {
95 else if ($form_state['values']['tag'] != $view->tag
) {
99 if ($form_state['values']['type'] != 'all' && $form_state['values']['type'] != $view->type
) {
103 if ($form_state['values']['base'] != 'all' && $form_state['values']['base'] != $view->base_table
) {
107 if ($form_state['values']['display'] != 'all' && empty($view->display
[$form_state['values']['display']])) {
111 if ($form_state['values']['status'] != 'all' && (!empty($view->disabled
) == $form_state['values']['status'])) {
115 $item = new
stdClass();
116 $item->ops
= array();
118 if (empty($view->disabled
)) {
119 $item->ops
[] = l(t('Edit'), "admin/build/views/edit/$view->name");
120 $item->ops
[] = l(t('Export'), "admin/build/views/export/$view->name");
121 $item->ops
[] = l(t('Clone'), "admin/build/views/clone/$view->name");
123 if ($view->type
!= t('Default')) {
124 $text = $view->type
== t('Overridden') ?
t('Revert') : t('Delete');
125 $item->ops
[] = l($text, "admin/build/views/delete/$view->name");
128 if (empty($view->disabled
)) {
129 $item->ops
[] = l(t('Disable'), "admin/build/views/disable/$view->name", array('query' => drupal_get_destination() .
'&token=' .
$token_disable));
132 $item->ops
[] = l(t('Enable'), "admin/build/views/enable/$view->name", array('query' => drupal_get_destination() .
'&token=' .
$token_enable));
136 $item->ops
= implode(' | ', $item->ops
);
138 if (empty($view->display
)) {
139 $item->path
= t('Warning! Broken view!');
142 $view->init_display(); // Make sure all the handlers are set up
143 $all_paths = array();
144 foreach ($view->display as
$display) {
145 if (!empty($display->handler
) && $display->handler
->has_path()) {
146 $one_path = $display->handler
->get_option('path');
147 if (empty($path_sort)) {
148 $path_sort = strtolower($one_path);
150 if (empty($view->disabled
) && strpos($one_path, '%') === FALSE
) {
151 $all_paths[] = l($one_path, $one_path);
154 $all_paths[] = check_plain($one_path);
158 if (!empty($all_paths)) {
159 $item->path
= implode(', ', array_unique($all_paths));
163 $item->type
= $view->type
;
164 $item->name
= $view->name
;
166 if (!empty($view->tag
)) {
167 $item->tag
= check_plain($view->tag
);
170 $item->title
= $view->get_title();
171 $item->base
= !empty($base_tables[$view->base_table
]['title']) ?
$base_tables[$view->base_table
]['title'] : t('Broken');
173 $item->displays
= array();
174 foreach ($view->display as
$display) {
175 if (!empty($display->handler
->definition
['admin'])) {
176 $item->displays
[$display->handler
->definition
['admin']] = TRUE
;
180 if ($item->displays
) {
181 ksort($item->displays
);
182 $item->displays
= implode(', ', array_keys($item->displays
));
185 $item->description
= check_plain($view->description
);
186 $item->classes
= empty($view->disabled
) ?
'view-enabled' : 'view-disabled';
189 $sort = intval(empty($view->disabled
) xor
$form_state['values']['sort'] == 'asc');
191 switch ($form_state['values']['order']) {
194 $sort .
= strtolower($view->name
);
197 $sort .
= strtolower($item->title
);
200 $sort .
= strtolower($raw_path); // $path;
203 $sort .
= $view->type .
$view->name
;
206 $sort .
= strtolower($view->tag
);
209 $sort .
= strtolower($view->description
);
216 if ($form_state['values']['sort'] == 'desc') {
224 foreach ($sorts as
$id => $title) {
228 views_add_css('views-list');
231 $getting_started = theme('advanced_help_topic', 'views', 'getting-started', 'title');
232 if (!$getting_started) {
233 $getting_started = t('Install the advanced help module for the getting started');
236 $vars['help'] = t('Not sure what to do? Try the "!getting-started" page.', array('!getting-started' => $getting_started));
240 * Provide a form for sorting and filtering the list of views.
242 function views_ui_list_views_form(&$form_state) {
243 if (!variable_get('clean_url', FALSE
)) {
246 '#value' => $_GET['q'],
250 $all = array('all' => t('- All -'));
251 $none = array('none' => t('- None -'));
253 $form['type'] = array(
255 '#title' => t('Storage'),
257 'all' => t('- All -'),
258 t('Normal') => t('Normal'),
259 t('Default') => t('Default'),
260 t('Overridden') => t('Overridden'),
262 '#default_value' => 'all',
266 '0' => t('Disabled'),
269 $form['status'] = array(
271 '#title' => t('Status'),
272 '#options' => array_merge($all, $status),
273 '#default_value' => 'all',
277 foreach (views_fetch_base_tables() as
$table => $info) {
278 $bases[$table] = $info['title'];
281 $form['base'] = array(
283 '#title' => t('Type'),
284 '#options' => array_merge($all, $bases),
285 '#default_value' => 'all',
291 foreach ($form_state['views'] as
$name => $view) {
292 if (!empty($view->tag
)) {
293 $tags[$view->tag
] = $view->tag
;
299 $form['tag'] = array(
301 '#title' => t('Tag'),
302 '#options' => array_merge($all, $none, $tags),
303 '#default_value' => 'all',
307 foreach (views_fetch_plugin_data('display') as
$id => $info) {
308 if (!empty($info['admin'])) {
309 $displays[$id] = $info['admin'];
315 $form['display'] = array(
317 '#title' => t('Displays'),
318 '#options' => array_merge($all, $displays),
319 '#default_value' => 'all',
322 $form['order'] = array(
324 '#title' => t('Sort by'),
327 'title' => t('Title'),
331 'desc' => t('Description'),
333 '#default_value' => 'name',
336 $form['sort'] = array(
338 '#title' => t('Order'),
343 '#default_value' => 'asc',
346 $form['submit'] = array(
347 '#name' => '', // so it won't in the $_GET args
349 '#id' => 'edit-views-apply',
350 '#value' => t('Apply'),
353 if (!empty($_SESSION['views']['#admin'])) {
354 $form['reset'] = array(
356 '#id' => 'edit-views-reset',
357 '#value' => t('Reset'),
361 $form['#theme'] = array('views_ui_list_views_form');
365 function theme_views_ui_list_views_form($form) {
366 // Don't render these:
367 unset($form['form_id']);
368 unset($form['form_build_id']);
369 unset($form['form_token']);
370 return drupal_render($form);
374 * Page callback for the live preview.
376 * @todo make this use a template
378 function views_ui_preview($js, $view) {
379 // Take off the items we know so that we can have just the args passed
381 $func_args = func_get_args();
382 array_shift($func_args); // $js
383 array_shift($func_args); // $view
384 $display_id = (count($func_args)) ?
array_shift($func_args) : 'default';
387 'display_id' => $display_id,
388 'view_args' => $func_args ?
implode('/', $func_args) : '',
390 'no_redirect' => TRUE
,
395 $output = drupal_build_form('views_ui_preview_form', $form_state);
397 if (isset($form_state['view_args']) && $form_state['view_args'] !== '') {
398 $args = explode('/', $form_state['view_args']);
401 $errors = $view->validate();
402 if ($errors === TRUE
) {
404 $view->live_preview
= TRUE
;
406 // Store the current view URL for later use:
407 $view->set_display($form_state['display_id']);
408 $view->set_arguments($args);
410 if ($view->display_handler
->get_option('path')) {
411 $path = $view->get_url();
414 // Make view links come back to preview.
415 $ajax = $js ?
'ajax' : 'nojs';
416 $view->override_path
= "admin/build/views/$ajax/preview/" .
$view->name .
'/' .
$form_state['display_id'];
418 // also override $_GET['q'] so we get the pager
419 $_GET['q'] = $view->override_path
;
420 if ($form_state['view_args']) {
421 $_GET['q'] .
= '/' .
$form_state['view_args'];
424 $preview = $view->preview($form_state['display_id'], $args);
426 // Get information from the preview for display.
427 if (empty($view->build_info
['fail'])) {
428 $rows = $view->query
->get_preview_info();
430 if (!empty($view->additional_queries
)) {
431 $queries = '<strong>' .
t('These queries were run during view rendering:') .
'</strong>';
432 foreach ($view->additional_queries as
$query) {
436 $queries .
= t('[@time ms]', array('@time' => intval($query[1] * 100000) / 100)) .
' ' .
check_plain($query[0]);
439 $rows[] = array('<strong>' .
t('Other queries') .
'</strong>', '<pre>' .
$queries .
'</pre>');
442 $rows[] = array('<strong>' .
t('Title') .
'</strong>', filter_xss_admin($view->get_title()));
444 $path = l($path, $path);
447 $path = t('This display has no path.');
450 $rows[] = array('<strong>' .
t('Path') .
'</strong>', $path);
452 $rows[] = array('<strong>' .
t('Query build time') .
'</strong>', t('@time ms', array('@time' => intval($view->build_time
* 100000) / 100)));
453 $rows[] = array('<strong>' .
t('Query execute time') .
'</strong>', t('@time ms', array('@time' => intval($view->execute_time
* 100000) / 100)));
454 $rows[] = array('<strong>' .
t('View render time') .
'</strong>', t('@time ms', array('@time' => intval($view->render_time
* 100000) / 100)));
455 drupal_alter('views_preview_info', $rows, $view);
457 $info = theme('table', array(), $rows);
460 $info = theme('table', array(), array(array('<strong>' .
t('Query') .
'</strong>', t('No query was run'))));
464 foreach ($errors as
$error) {
465 drupal_set_message($error, 'error');
467 $preview = t('Unable to preview due to validation errors.');
471 $info = '<div class="views-query-info">' .
$info .
'</div>';
473 if (variable_get('views_ui_query_on_top', FALSE
)) {
474 $output .
= $info .
$preview;
477 $output .
= $preview .
$info;
481 views_add_css('views-admin');
482 drupal_set_title($view->get_title());
486 views_include('ajax');
487 $object = new
stdClass();
488 if (!empty($view->js_settings
)) {
489 $object->js
= $view->js_settings
;
491 $object->display
= '';
492 if ($messages = theme('status_messages')) {
493 $object->display
= '<div class="views-messages">' .
$messages .
'</div>';
495 $object->display .
= $output;
496 $object->title
= $view->get_title();
497 views_ajax_render($object);
502 * Form for generating argument information for the live preview.
504 function views_ui_preview_form(&$form_state) {
505 $view = &$form_state['view'];
506 $view->init_display();
508 foreach ($view->display as
$id => $display) {
509 $options[$id] = $display->display_title
;
512 $form['#attributes'] = array(
513 'class' => 'clear-block',
516 $form['display_id'] = array(
518 '#title' => t('Display'),
519 '#options' => $options,
520 '#default_value' => $form_state['display_id'],
521 '#id' => 'preview-display-id',
524 $form['args'] = array(
525 '#type' => 'textfield',
526 '#title' => t('Arguments'),
527 '#default_value' => $form_state['view_args'],
528 '#description' => t('Separate arguments with a / as though they were a URL path.'),
529 '#id' => 'preview-args',
532 $form['preview'] = array(
534 '#value' => t('Preview'),
535 '#id' => 'preview-submit',
539 $form['live_preview'] = array(
540 '#type' => 'checkbox',
541 '#title' => t('Automatic live preview'),
542 '#default_value' => !variable_get('views_ui_disable_live_preview', 0),
545 $form['#action'] = url("admin/build/views/nojs/preview/$view->name");
550 * Submit the preview form.
552 * This just takes the data and stores it on the form state in a
553 * known location. The caller will be responsible for using it.
555 function views_ui_preview_form_submit(&$form, &$form_state) {
556 $form_state['display_id'] = $form_state['values']['display_id'];
557 $form_state['view_args'] = $form_state['values']['args'];
561 * Page callback to add a new view.
563 function views_ui_add_page() {
568 return drupal_build_form('views_ui_add_form', $form_state);
572 * Page callback to add a new view.
574 function views_ui_clone_page($view) {
576 'view' => $view->copy(),
579 drupal_set_title(t('Clone view %view', array('%view' => $view->name
)));
580 return drupal_build_form('views_ui_add_form', $form_state);
584 * Form constructor callback to create the views Add Form, phase 1.
586 function views_ui_add_form(&$form_state) {
587 $view = $form_state['view'];
590 $form['name'] = array(
591 '#type' => 'textfield',
592 '#title' => t('View name'),
593 '#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.'),
596 '#default_value' => $view ?
$view->name
: '',
597 '#attributes' => array('dir'=>'ltr'),
600 $form['human_name'] = array(
601 '#type' => 'textfield',
602 '#title' => t('Human readable name'),
603 '#description' => t('You can use a more descriptive name for this view here. Spaces are allowed'),
604 '#default_value' => $view ?
$view->human_name
: '',
607 $form['description'] = array(
608 '#type' => 'textfield',
609 '#title' => t('View description'),
610 '#description' => t('This description will appear on the Views administrative UI to tell you what the view is about.'),
611 '#default_value' => $view ?
$view->description
: '',
615 $form['tag'] = array(
616 '#type' => 'textfield',
617 '#title' => t('View tag'),
618 '#description' => t('Enter an optional tag for this view; it is used only to help sort views on the administrative page.'),
619 '#default_value' => $view ?
$view->tag
: '',
620 '#autocomplete_path' => 'admin/views/ajax/autocomplete/tag',
623 $base_tables = array();
624 foreach (views_fetch_base_tables() as
$table => $info) {
625 $base_tables[$table] = $info['title'] .
'<div class="description">' .
$info['description'] .
'</div>';
628 $form['base_table'] = array(
630 '#title' => t('View type'),
631 '#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>.'),
632 '#default_value' => $view ?
$view->base_table
: 'node',
633 '#options' => $base_tables,
637 $form['base_table']['#disabled'] = TRUE
;
640 $form['submit'] = array(
642 '#value' => t('Next'),
643 '#validate' => array('views_ui_add_form_validate'),
644 '#submit' => array('views_ui_add_form_submit'),
651 * Validate the add view form.
653 function views_ui_add_form_validate($form, &$form_state) {
654 $name = $form_state['values']['name'];
656 // View name must be alphanumeric or underscores, no other punctuation.
657 if (preg_match('/[^a-zA-Z0-9_]/', $name) || is_numeric($name)) {
658 form_error($form['name'], t('View name must be alphanumeric or underscores only, but cannot be numeric.'));
661 // View name must already exist.
662 $view = views_get_view($form_state['values']['name']);
663 if ($view && $view->type
!= t('Default')) {
664 form_error($form['name'], t('You must use a unique name for this view.'));
669 * Process the add view form
671 function views_ui_add_form_submit($form, &$form_state) {
672 $view = $form_state['view'] ?
$form_state['view'] : views_new_view();
673 $view->name
= $form_state['values']['name'];
674 $view->human_name
= $form_state['values']['human_name'];
675 $view->description
= $form_state['values']['description'];
676 $view->tag
= $form_state['values']['tag'];
677 $view->core
= VERSION
;
678 if (empty($form['base_table']['#disabled'])) {
679 $view->base_table
= $form_state['values']['base_table'];
682 views_ui_cache_set($view);
683 $form_state['redirect'] ='admin/build/views/edit/' .
$view->name
;
687 * Page to delete a view.
689 function views_ui_delete_confirm(&$form_state, $view) {
690 $form_state['view'] = &$view;
693 $cancel = 'admin/build/views';
694 if (!empty($_REQUEST['cancel'])) {
695 $cancel = $_REQUEST['cancel'];
698 if ($view->type
== t('Overridden')) {
699 $title = t('Are you sure you want to revert the view %name?', array('%name' => $view->name
));
700 $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.');
701 $button = t('Revert');
704 $title = t('Are you sure you want to delete the view %name?', array('%name' => $view->name
));
705 $desc = t('Deleting a view cannot be undone.');
706 $button = t('Delete');
709 return confirm_form($form,
718 * Submit handler to delete a view.
720 function views_ui_delete_confirm_submit(&$form, &$form_state) {
721 $form_state['view']->delete();
722 views_object_cache_clear('view', $form_state['view']->name
);
723 drupal_set_message(t('The view has been deleted.'));
724 $form_state['redirect'] = 'admin/build/views';
728 * Page to delete a view.
730 function views_ui_break_lock_confirm(&$form_state, $view) {
731 $form_state['view'] = &$view;
734 if (empty($view->locked
)) {
735 return t('There is no lock on view %view to break.', array('%name' => $view->name
));
738 $cancel = 'admin/build/views/edit/' .
$view->name
;
739 if (!empty($_REQUEST['cancel'])) {
740 $cancel = $_REQUEST['cancel'];
743 $account = user_load($view->locked
->uid
);
744 return confirm_form($form,
745 t('Are you sure you want to break the lock on view %name?',
746 array('%name' => $view->name
)),
748 t('By breaking this lock, any unsaved changes made by !user will be lost!', array('!user' => theme('username', $account))),
754 * Submit handler to break_lock a view.
756 function views_ui_break_lock_confirm_submit(&$form, &$form_state) {
757 db_query("DELETE FROM {views_object_cache} WHERE obj = 'view' AND name = '%s'", $form_state['view']->name
);
758 $form_state['redirect'] = 'admin/build/views/edit/' .
$form_state['view']->name
;
759 drupal_set_message(t('The lock has been broken and you may now edit this view.'));
763 * The main view edit page
765 function views_ui_edit_page($view) {
766 drupal_set_title(t('Edit view %view', array('%view' => $view->name
)));
767 $output = theme('views_ui_edit_view', $view);
768 views_ui_check_advanced_help();
773 * Export a view for cut & paste.
775 function views_ui_export_page(&$form_state, $view) {
776 $code = $view->export();
777 $lines = substr_count($code, "\n");
778 $form['code'] = array(
779 '#type' => 'textarea',
780 '#title' => $view->name
,
781 '#default_value' => $code,
788 * Import a view from cut & paste
790 function views_ui_import_page(&$form_state) {
791 $form['name'] = array(
792 '#type' => 'textfield',
793 '#title' => t('View name'),
794 '#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.'),
797 $form['view'] = array(
798 '#type' => 'textarea',
799 '#title' => t('Paste view code here'),
803 $form['submit'] = array(
805 '#value' => t('Import'),
806 '#submit' => array('views_ui_import_submit'),
807 '#validate' => array('views_ui_import_validate'),
813 * Validate handler to import a view
815 function views_ui_import_validate($form, &$form_state) {
817 views_include('view');
818 // Be forgiving if someone pastes views code that starts with '<?php'.
819 if (substr($form_state['values']['view'], 0, 5) == '<?php') {
820 $form_state['values']['view'] = substr($form_state['values']['view'], 5);
823 eval($form_state['values']['view']);
826 if (!is_object($view)) {
827 return form_error($form['view'], t('Unable to interpret view code.'));
830 if (empty($view->api_version
) || $view->api_version
< 2) {
831 // Check for some value that would only exist on a Views 1 view.
832 if (isset($view->url
) || isset($view->page
) || isset($view->block
)) {
833 views_include('convert');
834 $view = views1_import($view);
835 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');
838 form_error($form['view'], t('That view is not compatible with this version of Views.'));
841 elseif ($view->api_version
> views_api_version()) {
842 form_error($form['view'], t('That view is created for the version @import_version of views, but you only have @api_version', array(
843 '@import_version' => $view->api_version
,
844 '@api_version' => views_api_version())));
847 // View name must be alphanumeric or underscores, no other punctuation.
848 if (!empty($form_state['values']['name']) && preg_match('/[^a-zA-Z0-9_]/', $form_state['values']['name'])) {
849 form_error($form['name'], t('View name must be alphanumeric or underscores only.'));
852 if ($form_state['values']['name']) {
853 $view->name
= $form_state['values']['name'];
856 $test = views_get_view($view->name
);
857 if ($test && $test->type
!= t('Default')) {
858 form_set_error('', t('A view by that name already exists; please choose a different name'));
861 views_include('analyze');
862 if ($messages = views_analyze_view($view)) {
863 foreach ($messages as
$message) {
864 if ($message['type'] == 'error') {
865 drupal_set_message($message['message'], 'error');
870 $form_state['view'] = &$view;
874 * Submit handler for view import
876 function views_ui_import_submit($form, &$form_state) {
877 // Store in cache and then go to edit.
878 views_ui_cache_set($form_state['view']);
879 $form_state['redirect'] = 'admin/build/views/edit/' .
$form_state['view']->name
;
883 * The main edit view form, which is really just a save/cancel/delete button.
885 function views_ui_edit_view_form(&$form_state, $view) {
886 $form['buttons']['save'] = array(
888 '#value' => t('Save'),
889 '#validate' => array('views_ui_edit_view_form_validate'),
890 '#submit' => array('views_ui_edit_view_form_submit'),
893 $form['buttons']['cancel'] = array(
895 '#value' => t('Cancel'),
896 '#submit' => array('views_ui_edit_view_form_cancel'),
899 if (is_numeric($view->vid
)) {
900 $form['buttons']['delete'] = array(
902 '#value' => $view->type
== t('Overridden') ?
t('Revert') : t('Delete'),
903 '#submit' => array('views_ui_edit_view_form_delete'),
907 $form_state['view'] = &$view;
912 * Validate that a view is complete and whole.
914 function views_ui_edit_view_form_validate($form, &$form_state) {
915 // Do not validate cancel or delete or revert.
916 if (empty($form_state['clicked_button']['#value']) || $form_state['clicked_button']['#value'] != t('Save')) {
920 $errors = $form_state['view']->validate();
921 if ($errors !== TRUE
) {
922 foreach ($errors as
$error) {
923 form_set_error('', $error);
929 * Submit handler for the edit view form.
931 function views_ui_edit_view_form_submit($form, &$form_state) {
932 // Go through and remove displayed scheduled for removal.
933 foreach ($form_state['view']->display as
$id => $display) {
934 if (!empty($display->deleted
)) {
935 unset($form_state['view']->display
[$id]);
938 // Rename display ids if needed.
939 foreach ($form_state['view']->display as
$id => $display) {
940 if (!empty($display->new_id
)) {
941 $form_state['view']->display
[$id]->id
= $display->new_id
;
945 $form_state['view']->save();
946 drupal_set_message(t('The view %name has been saved.', array('%name' => $form_state['view']->get_human_name())));
948 // Make sure menu items get rebuilt as neces
951 // Clear the views cache.
952 cache_clear_all('*', 'cache_views');
954 // Clear the page cache.
957 // Remove this view from cache so we can edit it properly.
958 views_object_cache_clear('view', $form_state['view']->name
);
962 * Submit handler for the edit view form.
964 function views_ui_edit_view_form_cancel($form, &$form_state) {
965 // Remove this view from cache so edits will be lost.
966 views_object_cache_clear('view', $form_state['view']->name
);
967 if (empty($form['view']->vid
)) {
968 // I seem to have to drupal_goto here because I can't get fapi to
969 // honor the redirect target. Not sure what I screwed up here.
970 drupal_goto('admin/build/views');
974 function views_ui_edit_view_form_delete($form, &$form_state) {
975 unset($_REQUEST['destination']);
976 // Redirect to the delete confirm page
977 $form_state['redirect'] = array('admin/build/views/delete/' .
$form_state['view']->name
, 'cancel=admin/build/views/edit/' .
$form_state['view']->name .
'&' .
drupal_get_destination());
981 * Preprocess the view edit page.
983 function template_preprocess_views_ui_edit_view(&$vars) {
984 $view = &$vars['view'];
986 $vars['save_button'] = drupal_get_form('views_ui_edit_view_form', $view);
988 $table = views_fetch_data($view->base_table
);
989 $vars['base_table'] = !empty($table['table']['base']['title']) ?
990 $table['table']['base']['title'] : t('Unknown or missing table name');
992 views_include('tabs');
993 $tabs = new views_tabset
;
995 $vars['message'] = '<div class="message">' .
t("Click on an item to edit that item's details.") .
'</div>';
997 if (!$view->set_display('default')) {
998 drupal_set_message(t('This view has a broken default display and cannot be used.'), 'error');
1001 foreach ($view->display as
$display) {
1002 list($title, $body) = views_ui_display_tab($view, $display);
1003 // The first display is the default.
1004 $tabs->set($display->id
, $title, $body);
1007 // This is the area that will render beneath the links
1008 $form_state = array(
1013 $display_button = drupal_build_form('views_ui_add_display_form', $form_state);
1014 $analyze_button = drupal_get_form('views_ui_analyze_view_button', $view);
1015 $reorder_button = drupal_get_form('views_ui_reorder_displays_button', $view);
1016 $tabs->add_extra($display_button .
$reorder_button .
$analyze_button);
1018 $vars['tabs'] = $tabs->render();
1020 $form_state = array(
1021 'display_id' => 'default',
1023 'rerender' => FALSE
,
1024 'no_redirect' => TRUE
,
1028 $vars['preview'] = drupal_build_form('views_ui_preview_form', $form_state);
1030 $vars['locked'] = NULL
;
1031 if (isset($view->locked
) && is_object($view->locked
)) {
1032 $account = user_load($view->locked
->uid
);
1033 $vars['locked'] = theme('username', $account);
1034 $vars['lock_age'] = format_interval(time() - $view->locked
->updated
);
1035 $vars['break'] = url('admin/build/views/break-lock/' .
$view->name
);
1038 $vars['quick_links_raw'] = array(
1040 'title' => t('Export'),
1041 'alt' => t("Export this view"),
1042 'href' => "admin/build/views/export/$view->name",
1045 'title' => t('Clone'),
1046 'alt' => t("Create a copy of this view"),
1047 'href' => "admin/build/views/clone/$view->name",
1052 foreach ($view->display as
$id => $display) {
1053 if (!empty($display->handler
) && $display->handler
->has_path()) {
1054 $path = $display->handler
->get_path();
1055 if (strpos($path, '%') === FALSE
&& !isset($paths[$path])) {
1056 $vars['quick_links_raw'][] = array(
1057 'title' => t('View "@display"', array('@display' => $display->display_title
)),
1058 'alt' => t("Go to the real page for this display"),
1061 // Displays can have the same path; no point in showing more than one link.
1062 $paths[$path] = TRUE
;
1067 $vars['quick_links'] = theme('links', $vars['quick_links_raw']);
1068 views_add_css('views-admin');
1069 drupal_add_js('misc/tabledrag.js', 'core');
1070 views_add_js('ajax');
1071 drupal_add_js('misc/jquery.form.js', 'core');
1073 // Also add any js files required by plugins:
1074 $plugins = views_fetch_plugin_data();
1075 foreach ($plugins as
$type => $type_plugins) {
1076 foreach ($type_plugins as
$name => $plugin) {
1077 if (!empty($plugin['js'])) {
1078 foreach ($plugin['js'] as
$file) {
1079 drupal_add_js($file);
1085 $settings = array('views' => array('ajax' => array(
1086 'id' => '#views-ajax-pad',
1087 'title' => '#views-ajax-title',
1088 'defaultForm' => $vars['message'],
1091 drupal_add_js($settings, 'setting');
1094 function template_preprocess_views_ui_edit_tab(&$vars) {
1095 $view = $vars['view'];
1096 $display = $vars['display'];
1097 $plugin = $display->handler
->definition
;
1099 $top = $left = $middle = $right = '';
1101 // If this form was submitted it was already handled, so force it not to
1104 $vars['remove'] = '';
1105 $vars['clone'] = '';
1106 if (empty($plugin['no remove'])) {
1107 if (!empty($_POST['form_id']) && in_array($_POST['form_id'], array('views_ui_remove_display_form', 'views_ui_clone_display_form'))) {
1108 unset($_POST['form_id']);
1110 $form_state = array('view' => &$view, 'display_id' => $display->id
, 'ajax' => FALSE
);
1111 $vars['remove'] = drupal_build_form('views_ui_remove_display_form', $form_state);
1112 $vars['clone'] = drupal_build_form('views_ui_clone_display_form', $form_state);
1116 $vars['title'] = check_plain($display->display_title
);
1117 $vars['description'] = check_plain($plugin['help']);
1119 // Special fields if this is the default display.
1120 $vars['default'] = ($display->id
== 'default');
1121 $vars['details_class'] = views_ui_item_css('details');
1123 foreach (array('human_name', 'tag', 'description') as
$property) {
1124 if (!empty($view->changed_sections
[$property])) {
1125 $vars['details_changed'][$property] = TRUE
;
1127 switch ($property) {
1129 $title = t('Human name');
1135 $title = t('Description');
1138 $value = empty($view->{$property}) ?
t('None') : check_plain($view->{$property});
1139 $vars['details'][$property] = $title .
': ' .
l($value, "admin/build/views/nojs/$property/$view->name", array('attributes' => array('class' => 'views-ajax-link')));
1142 // Calculate options from display plugin.
1143 $options = $categories = array();
1144 $display->handler
->options_summary($categories, $options);
1146 // Build all of the options we were returned and put them into the
1147 // category data fields.
1148 foreach ($options as
$id => $option) {
1149 if (empty($categories[$option['category']]['data'])) {
1150 $categories[$option['category']]['data'] = array();
1152 $categories[$option['category']]['data'][$id] = array();
1153 $data = &$categories[$option['category']]['data'][$id];
1154 $data['content'] = '';
1155 $data['links'] = '';
1156 $data['overridden'] = FALSE
;
1157 $data['defaulted'] = FALSE
;
1159 // If there are optional links, build them first so they float properly.
1160 if (!empty($option['links'])) {
1161 foreach ($option['links'] as
$link_id => $link_value) {
1162 $data['links'] .
= $display->handler
->option_link($link_value, $link_id, 'views-button-configure');
1165 if (!empty($option['title'])) {
1166 $data['content'] .
= $option['title'] .
': ';
1169 $data['content'] .
= $display->handler
->option_link($option['value'], $id, '', empty($option['desc']) ?
'' : $option['desc']);
1170 if (!empty($display->handler
->options
['defaults'][$id])) {
1171 $display_id = 'default';
1172 $data['defaulted'] = TRUE
;
1175 $display_id = $display->id
;
1176 if (!$display->handler
->is_default_display()) {
1177 if ($display->handler
->defaultable_sections($id)) {
1178 $data['overridden'] = TRUE
;
1182 $data['class'] = views_ui_item_css($display_id .
'-' .
$id);
1183 if (!empty($view->changed_sections
[$display_id .
'-' .
$id])) {
1184 $data['changed'] = TRUE
;
1188 $vars['categories'] = $categories;
1191 if (isset($plugin['help topic'])) {
1192 $vars['display_help_icon'] = theme('advanced_help_topic', $plugin['module'], $plugin['help topic']);
1195 $vars['display_help_icon'] = '';
1198 // Fetch style plugin info because it has some effect on how/what we render.
1199 $style_plugin = $display->handler
->get_plugin();
1201 $vars['fields'] = '';
1202 $vars['areas'] = array();
1203 foreach (array('header', 'footer', 'empty') as
$area) {
1204 $vars['areas'][$area] = theme('views_ui_edit_item', $area, $view, $display);
1206 $vars['fields'] = theme('views_ui_edit_item', 'field', $view, $display, !($style_plugin && $style_plugin->uses_fields()));
1207 $vars['relationships'] = theme('views_ui_edit_item', 'relationship', $view, $display);
1208 $vars['arguments'] = theme('views_ui_edit_item', 'argument', $view, $display);
1209 $vars['filters'] = theme('views_ui_edit_item', 'filter', $view, $display);
1210 $vars['sorts'] = theme('views_ui_edit_item', 'sort', $view, $display);
1214 * Generate the summary output for a single display to render in a tab.
1216 function views_ui_display_tab($view, $display) {
1217 if (isset($display->handler
)) {
1218 $plugin = $display->handler
->definition
;
1220 if (empty($plugin)) {
1221 $title = isset($display->display_title
) ?
$display->display_title
: t('Invalid');
1222 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
)));
1224 // @todo We can do a better 'plugin does not exist' tab.
1227 // The display should always be initialized prior to this call.
1228 if (empty($display->handler
)) {
1232 $body = theme('views_ui_edit_tab', $view, $display);
1233 return array($display->display_title
, $body);
1237 * Add information about a section to a display.
1239 function template_preprocess_views_ui_edit_item(&$vars) {
1240 $type = $vars['type'];
1241 $view = $vars['view'];
1242 $display = $vars['display'];
1244 $types = views_object_types();
1246 $vars['overridden'] = FALSE
;
1247 $vars['defaulted'] = FALSE
;
1249 if ($vars['no_fields']) {
1250 $vars['title'] = $types[$type]['title'];
1251 $vars['item_help_icon'] = theme('advanced_help_topic', 'views', $type);
1252 $vars['rearrange'] = NULL
;
1253 $vars['add'] = NULL
;
1257 // Different types now have different rearrange forms, so we use this switch
1258 // to get the right one.
1261 $rearrange_url = "admin/build/views/nojs/rearrange-$type/$view->name/$display->id/$type";
1264 $rearrange_url = "admin/build/views/nojs/rearrange/$view->name/$display->id/$type";
1267 $vars['rearrange'] = l('<span>' .
t('Rearrange') .
'</span>', $rearrange_url, array('attributes' => array('class' => 'views-button-rearrange views-ajax-link', 'title' => t('Rearrange')), 'html' => true
));
1269 $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
));
1271 if (!$display->handler
->is_default_display()) {
1272 if (!$display->handler
->is_defaulted($types[$type]['plural'])) {
1273 $vars['overridden'] = TRUE
;
1276 $vars['defaulted'] = TRUE
;
1280 if ($display->display_plugin
!= 'default') {
1281 $vars['title'] = l($types[$type]['title'], "admin/build/views/nojs/config-type/$view->name/$display->id/$type", array('attributes' => array('class' => 'views-ajax-link')));
1284 $vars['title'] = $types[$type]['title'];
1289 static
$relationships = NULL
;
1290 if (!isset($relationships)) {
1291 // Get relationship labels
1292 $relationships = array();
1293 // @todo: get_handlers()
1294 $handlers = $display->handler
->get_option('relationships');
1296 foreach ($handlers as
$id => $info) {
1297 $handler = $display->handler
->get_handler('relationship', $id);
1298 $relationships[$id] = $handler->label();
1303 // Filters can now be grouped so we do a little bit extra:
1306 if ($type == 'filter') {
1307 $group_info = $view->display_handler
->get_option('filter_groups');
1308 if (!empty($group_info['groups']) && count($group_info['groups']) > 1) {
1310 $groups = array(0 => array());
1314 foreach ($display->handler
->get_option($types[$type]['plural']) as
$id => $field) {
1315 $fields[$id] = array();
1317 $handler = $display->handler
->get_handler($type, $id);
1318 if (empty($handler)) {
1319 $fields[$id]['class'] = 'broken';
1320 $field_name = t('Broken/missing handler: @table > @field', array('@table' => $field['table'], '@field' => $field['field']));
1321 $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
));
1322 $fields[$id]['info'] = '';
1326 $field_name = $handler->ui_name(TRUE
);
1327 if (!empty($field['relationship']) && !empty($relationships[$field['relationship']])) {
1328 $field_name = '(' .
$relationships[$field['relationship']] .
') ' .
$field_name;
1331 $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
));
1332 $fields[$id]['class'] = views_ui_item_css($display->id .
'-' .
$type .
'-' .
$id);
1333 if (!empty($view->changed_sections
[$display->id .
'-' .
$type .
'-' .
$id])) {
1334 $fields[$id]['changed'] = TRUE
;
1336 $fields[$id]['info'] = $handler->admin_summary();
1338 if ($display->handler
->use_group_by()) {
1339 $fields[$id]['links'] = l('<span>' .
t('Group settings') .
'</span>', "admin/build/views/nojs/config-item-group/$view->name/$display->id/$type/$id", array('attributes' => array('class' => 'views-button-configure views-ajax-link', 'title' => t('Group settings')), 'html' => true
));
1341 if ($handler->has_extra_options()) {
1342 $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
));
1345 if ($handler->needs_style_plugin()) {
1346 $style_plugin = views_fetch_plugin_data('style', $handler->options
['style_plugin']);
1347 $style_title = empty($style_plugin['title']) ?
t('Missing style plugin') : $style_plugin['title'];
1348 $pid = $id .
'-style-plugin';
1350 if (!empty($style_plugin['uses options'])) {
1351 $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
));
1354 $fields[$pid]['title'] = ' ' .
t(' 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')))));
1355 $fields[$pid]['class'] = views_ui_item_css($display->id .
'-' .
$type .
'-' .
$pid);
1356 if (!empty($view->changed_sections
[$display->id .
'-' .
$type .
'-' .
$pid])) {
1357 $fields[$pid]['changed'] = TRUE
;
1359 $fields[$pid]['info'] = '';
1363 $gid = $handler->options
['group'];
1365 // Show in default group if the group does not exist.
1366 if (empty($group_info['groups'][$gid])) {
1369 $groups[$gid][] = $id;
1373 // If using grouping, re-order fields so that they show up properly in the list.
1374 if ($type == 'filter' && $grouping) {
1377 foreach ($groups as
$gid => $contents) {
1378 if (!empty($fields)) {
1379 $operator = ') ' .
($group_info['operator'] == 'OR' ?
t('OR') : t('AND')) .
' (';
1385 'class' => 'views-group-text',
1386 'title' => $operator,
1390 foreach ($contents as
$pid) {
1391 $operator = ' ';
1393 $operator .
= ($group_info['groups'][$gid] == 'OR' ?
t('OR') : t('AND')) .
' ';
1395 $store[$pid]['title'] = $operator .
$store[$pid]['title'];
1397 $fields[$pid] = $store[$pid];
1401 'class' => 'views-group-text',
1407 $vars['fields'] = $fields;
1408 $vars['item_help_icon'] = theme('advanced_help_topic', 'views', $type);
1412 * Regenerate the tabs for AJAX updates.
1414 function views_ui_regenerate_tabs(&$view, $display_id = NULL
, $object = NULL
) {
1415 if (empty($display_id)) {
1416 $displays = array_keys($view->display
);
1418 elseif (!is_array($display_id)) {
1419 $displays = array($display_id);
1420 if ($display_id != 'default') {
1421 $displays[] = 'default';
1425 $displays = $display_id;
1428 if (!$view->set_display('default')) {
1429 views_ajax_render(t('Invalid display id found while regenerating tabs'));
1432 if (!is_object($object)) {
1433 $object = new
stdClass();
1436 $object->replace
= array();
1437 foreach ($displays as
$id) {
1438 list($title, $body) = views_ui_display_tab($view, $view->display
[$id]);
1439 $object->replace
['#views-tab-' .
$id] = $body;
1440 $object->replace
['#views-tab-title-' .
$id] = check_plain($title);
1443 if (!empty($view->changed
)) {
1444 $object->changed
= TRUE
;
1447 views_ajax_render($object);
1451 * Provide standard buttons for the forms to make it easy. Also provide
1452 * a hidden op operator because the forms plugin doesn't seem to properly
1453 * provide which button was clicked.
1455 function views_ui_standard_form_buttons(&$form, &$form_state, $form_id, $name = NULL
, $third = NULL
, $submit = NULL
) {
1456 $form['buttons'] = array(
1457 '#prefix' => '<div class="clear-block"><div class="form-buttons">',
1458 '#suffix' => '</div></div>',
1462 $name = t('Update');
1465 // Add the override and update button
1466 if ($name == t('Update default display')) {
1467 $form['buttons']['override_update'] = array(
1468 '#type' => 'submit',
1469 '#value' => t('Update and override'),
1471 'views_ui_edit_display_form_override_update_section',
1472 'views_ui_standard_submit',
1473 'views_ui_edit_display_form_override_update',
1478 if (empty($form_state['ok_button'])) {
1479 // but be sure submit button validates!
1480 $form['buttons']['submit'] = array(
1481 '#type' => 'submit',
1483 '#submit' => array('views_ui_standard_submit', $form_id .
'_submit'),
1487 $cancel_submit = function_exists($form_id .
'_cancel') ?
$form_id .
'_cancel' : 'views_ui_standard_cancel';
1488 $form['buttons']['cancel'] = array(
1489 '#type' => 'submit',
1490 '#value' => empty($form_state['ok_button']) ?
t('Cancel') : t('Ok'),
1491 '#submit' => array($cancel_submit),
1492 '#validate' => array(),
1496 if (empty($submit)) {
1499 $third_submit = function_exists($form_id .
'_' .
$submit) ?
$form_id .
'_' .
$submit : 'views_ui_standard_cancel';
1501 $form['buttons'][$submit] = array(
1502 '#type' => 'submit',
1504 '#validate' => array(),
1505 '#submit' => array($third_submit),
1509 // Compatibility, to be removed later:
1510 // We used to set these items on the form, but now we want them on the $form_state:
1511 if (isset($form['#title'])) {
1512 $form_state['title'] = $form['#title'];
1514 if (isset($form['#help_topic'])) {
1515 $form_state['help_topic'] = $form['#help_topic'];
1517 if (isset($form['#help_module'])) {
1518 $form_state['help_module'] = $form['#help_module'];
1520 if (isset($form['#url'])) {
1521 $form_state['url'] = $form['#url'];
1523 if (isset($form['#js'])) {
1524 if (!empty($form_state['js settings']) && is_array($form_state['js settings'])) {
1525 $form_state['js settings'] = array_merge($form_state['js settings'], $form['#js']);
1528 $form_state['js settings'] = $form['#js'];
1531 if (isset($form['#section'])) {
1532 $form_state['#section'] = $form['#section'];
1534 // Finally, we never want these cached -- our object cache does that for us.
1535 $form['#no_cache'] = TRUE
;
1537 // If this isn't an ajaxy form, then we want to set the title.
1538 if (!empty($form['#title'])) {
1539 drupal_set_title($form['#title']);
1541 views_add_css('views-admin');
1545 * Basic submit handler applicable to all 'standard' forms
1547 function views_ui_standard_submit($form, &$form_state) {
1548 if (!empty($form['#section'])) {
1549 $form_state['view']->changed_sections
[$form['#section']] = TRUE
;
1554 * Submit handler for cancel button
1556 function views_ui_standard_cancel($form, &$form_state) {
1557 if (!empty($form_state['view']->changed
) && isset($form_state['view']->form_cache
)) {
1558 unset($form_state['view']->form_cache
);
1559 views_ui_cache_set($form_state['view']);
1562 $form_state['redirect'] = 'admin/build/views/edit/' .
$form_state['view']->name
;
1565 // --------------------------------------------------------------------------
1566 // Various subforms for editing the pieces of a view.
1568 function views_ui_ajax_forms($key = NULL
) {
1571 'form_id' => 'views_ui_edit_display_form',
1572 'args' => array('section'),
1574 'remove-display' => array(
1575 'form_id' => 'views_ui_remove_display_form',
1578 'config-type' => array(
1579 'form_id' => 'views_ui_config_type_form',
1580 'args' => array('type'),
1582 'rearrange' => array(
1583 'form_id' => 'views_ui_rearrange_form',
1584 'args' => array('type'),
1586 'rearrange-filter' => array(
1587 'form_id' => 'views_ui_rearrange_filter_form',
1588 'args' => array('type'),
1590 'add-item' => array(
1591 'form_id' => 'views_ui_add_item_form',
1592 'args' => array('type'),
1594 'config-item' => array(
1595 'form_id' => 'views_ui_config_item_form',
1596 'args' => array('type', 'id'),
1598 'config-item-group' => array(
1599 'form_id' => 'views_ui_config_item_group_form',
1600 'args' => array('type', 'id'),
1602 'config-item-extra' => array(
1603 'form_id' => 'views_ui_config_item_extra_form',
1604 'args' => array('type', 'id'),
1606 'change-style' => array(
1607 'form_id' => 'views_ui_change_style_form',
1608 'args' => array('type', 'id'),
1610 'config-style' => array(
1611 'form_id' => 'views_ui_config_style_form',
1612 'args' => array('type', 'id'),
1617 return !empty($forms[$key]) ?
$forms[$key] : NULL
;
1624 * Build a form identifier that we can use to see if one form
1625 * is the same as another. Since the arguments differ slightly
1626 * we do a lot of spiffy concenation here.
1628 function views_ui_build_identifier($key, $view, $display_id, $args) {
1629 $form = views_ui_ajax_forms($key);
1630 // Automatically remove the single-form cache if it exists and
1631 // does not match the key.
1632 $identifier = implode('-', array($key, $view->name
, $display_id));
1634 foreach ($form['args'] as
$id) {
1635 $arg = (!empty($args)) ?
array_shift($args) : NULL
;
1636 $identifier .
= '-' .
$arg;
1642 * Build up a $form_state object suitable for use with drupal_build_form
1643 * based on known information about a form.
1645 function views_ui_build_form_state($js, $key, &$view, $display_id, $args) {
1646 $form = views_ui_ajax_forms($key);
1647 // Build up form state
1648 $form_state = array(
1650 'form_id' => $form['form_id'],
1653 'display_id' => $display_id,
1654 'no_redirect' => TRUE
,
1657 foreach ($form['args'] as
$id) {
1658 $form_state[$id] = (!empty($args)) ?
array_shift($args) : NULL
;
1665 * Create the URL for one of our standard AJAX forms based upon known
1666 * information about the form.
1668 function views_ui_build_form_url($form_state) {
1669 $form = views_ui_ajax_forms($form_state['form_key']);
1670 $ajax = empty($form_state['ajax']) ?
'nojs' : 'ajax';
1671 $name = $form_state['view']->name
;
1672 $url = "admin/build/views/$ajax/$form_state[form_key]/$name/$form_state[display_id]";
1673 foreach ($form['args'] as
$arg) {
1674 $url .
= '/' .
$form_state[$arg];
1680 * Add another form to the stack; clicking 'update' will go to this form
1681 * rather than closing the ajax pad.
1683 function views_ui_add_form_to_stack($key, &$view, $display_id, $args, $top = FALSE
) {
1684 if (empty($view->stack
)) {
1685 $view->stack
= array();
1688 $stack = array(views_ui_build_identifier($key, $view, $display_id, $args), $key, &$view, $display_id, $args);
1690 array_unshift($view->stack
, $stack);
1693 $view->stack
[] = $stack;
1698 * Generic entry point to handle forms.
1700 * We do this for consistency and to make it easy to chain forms
1701 * together. This only works for forms that use both $view
1702 * and $display_id, so we have a couple of ajax forms that we don't
1703 * use with this system.
1705 function views_ui_ajax_form($js, $key, $view, $display_id) {
1706 $form = views_ui_ajax_forms($key);
1708 return drupal_not_found();
1711 views_include('ajax');
1712 $args = func_get_args();
1713 // Remove the known args
1714 array_splice($args, 0, 4);
1716 $form_state = views_ui_build_form_state($js, $key, $view, $display_id, $args);
1717 // check to see if this is the top form of the stack. If it is, pop
1718 // it off; if it isn't, the user clicked somewhere else and the stack is
1720 if (!empty($view->stack
)) {
1721 $identifier = views_ui_build_identifier($key, $view, $display_id, $args);
1722 $top = array_shift($view->stack
);
1723 if (array_shift($top) != $identifier) {
1724 $view->stack
= array();
1728 // Automatically remove the form cache if it is set and the key does
1729 // not match. This way navigating away from the form without hitting
1730 // update will work.
1731 if (isset($view->form_cache
) && $view->form_cache
['key'] != $key) {
1732 unset($view->form_cache
);
1735 $output = views_ajax_form_wrapper($form_state['form_id'], $form_state);
1738 // Sometimes we need to re-generate the form for multi-step type operations.
1740 if (!empty($view->stack
)) {
1741 $stack = $view->stack
; // copy so the next shift doesn't break the array
1742 $top = array_shift($stack);
1743 $top[0] = $js; // change identifier into $js setting
1744 $stepview = $top[2]; // Change view into a reference [#452384]
1745 $top[2] = &$stepview;
1746 $form_state = call_user_func_array('views_ui_build_form_state', $top);
1747 $form_state['input'] = array(); // this is a new form, make sure it
1748 // doesn't try to inherit $_POST info.
1750 return drupal_goto(views_ui_build_form_url($form_state));
1752 $object = views_ajax_form_wrapper($form_state['form_id'], $form_state);
1753 $object->url
= url(views_ui_build_form_url($form_state));
1756 // if nothing on the stack, non-js forms just go back to the main view editor.
1757 return drupal_goto("admin/build/views/edit/$view->name");
1759 // regenerate all tabs because changes to the default tab could ripple.
1760 return views_ui_regenerate_tabs($view, NULL
, $object);
1763 return ($js) ?
views_ajax_render($output) : $output;
1767 * AJAX callback to add a display.
1769 function views_ui_add_display($js, $view) {
1770 views_include('ajax');
1771 $form_state = array(
1776 $output = views_ajax_form_wrapper('views_ui_add_display_form', $form_state);
1779 // If we don't have an output object, it was submitted. Set up the submission.
1780 if (empty($output)) {
1781 $id = $form_state['id'];
1783 // Make sure the new display is active
1784 if (!$view->set_display('default')) {
1785 views_ajax_render(t('Unable to initialize default display'));
1788 // Render the new display
1789 list($title, $body) = views_ui_display_tab($view, $view->display
[$id]);
1791 // Instruct the javascript on the browser to render the new tab.
1792 $output = new stdClass
;
1793 $output->tab
= array('#views-tab-' .
$id => array('title' => $title, 'body' => $body));
1795 // Render the command object. This automatically exits.
1796 views_ajax_render($output);
1799 // But the non-js variant will return output if it didn't redirect us.
1804 * Form to add a display to a view.
1806 function views_ui_add_display_form(&$form_state) {
1807 $view = &$form_state['view'];
1809 $form['display']['display'] = array(
1810 '#type' => 'select',
1811 '#options' => views_fetch_plugin_names('display'),
1812 '#default_value' => 'page',
1815 $form['display']['add_display'] = array(
1816 '#type' => 'submit',
1817 '#value' => t('Add display'),
1818 '#submit' => array('views_ui_add_display_form_submit'),
1821 $form['#id'] = 'views-add-display-form';
1822 $form['#attributes'] = array('class' => 'views-ajax-form');
1823 $form['#action'] = url("admin/build/views/nojs/add-display/$view->name");
1829 * Submit handler to add a display to a view.
1831 function views_ui_add_display_form_submit($form, &$form_state) {
1832 // Create the new display
1833 $plugin = $form_state['values']['display'];
1834 $form_state['id'] = $form_state['view']->add_display($plugin);
1837 views_ui_cache_set($form_state['view']);
1840 $form_state['redirect'] = array('admin/build/views/edit/' .
$form_state['view']->name
, NULL
, 'views-tab-' .
$form_state['id']);
1844 * AJAX callback to add a display.
1846 function views_ui_clone_display($js, $view, $id) {
1847 views_include('ajax');
1848 $form_state = array(
1851 'display_id' => $id,
1854 $output = views_ajax_form_wrapper('views_ui_clone_display_form', $form_state);
1857 // If we don't have an output object, it was submitted. Set up the submission.
1858 if (empty($output)) {
1859 $id = $form_state['id'];
1861 // Make sure the new display is active
1862 if (!$view->set_display('default')) {
1863 views_ajax_render(t('Unable to initialize default display'));
1866 // Render the new display
1867 list($title, $body) = views_ui_display_tab($view, $view->display
[$id]);
1869 // Instruct the javascript on the browser to render the new tab.
1870 $output = new stdClass
;
1871 $output->tab
= array('#views-tab-' .
$id => array('title' => $title, 'body' => $body));
1873 // Render the command object. This automatically exits.
1874 views_ajax_render($output);
1877 // But the non-js variant will return output if it didn't redirect us.
1882 * From to clone a display from a view.
1884 function views_ui_clone_display_form(&$form_state) {
1885 $view = &$form_state['view'];
1886 $display_id = $form_state['display_id'];
1888 $form['clone_display'] = array(
1889 '#type' => 'submit',
1890 '#value' => t('Clone display'),
1891 '#submit' => array('views_ui_clone_display_form_submit'),
1894 $form['#id'] = 'views-clone-display-form';
1895 $form['#action'] = url("admin/build/views/nojs/clone-display/$view->name/$display_id");
1896 $form['#attributes'] = array('class' => 'views-ajax-form');
1902 * Submit handler to add a clone to a display from a view.
1904 function views_ui_clone_display_form_submit($form, &$form_state) {
1905 // Create the new display
1906 $id = $form_state['display_id'];
1907 $display = $form_state['view']->display
[$id];
1909 $new_id = $form_state['view']->add_display($display->display_plugin
);
1910 $form_state['id'] = $new_id;
1912 // Replace the new display by a copy of the old
1913 $form_state['view']->display
[$new_id] = drupal_clone($display);
1914 $form_state['view']->display
[$new_id]->id
= $new_id;
1917 views_ui_cache_set($form_state['view']);
1920 $form_state['redirect'] = array('admin/build/views/edit/' .
$form_state['view']->name
, NULL
, 'views-tab-' .
$new_id);
1924 * Form to remove a display from a view.
1926 function views_ui_remove_display_form(&$form_state) {
1927 $view = &$form_state['view'];
1928 $display_id = $form_state['display_id'];
1930 if (empty($view->display
[$display_id]->deleted
)) {
1931 $form['display'] = array(
1932 '#prefix' => '<div class="display-button remove-display">',
1933 '#suffix' => '</div>',
1935 $form['remove_display'] = array(
1936 '#type' => 'submit',
1937 '#value' => t('Remove display'),
1938 '#submit' => array('views_ui_remove_display_form_submit'),
1942 $form['display'] = array(
1943 '#prefix' => '<div class="display-button restore-display">',
1944 '#suffix' => '</div>',
1946 $form['restore_display'] = array(
1947 '#type' => 'submit',
1948 '#value' => t('Restore display'),
1949 '#submit' => array('views_ui_remove_display_form_restore'),
1952 $form['#action'] = url("admin/build/views/nojs/remove-display/$view->name/$display_id");
1953 $form['#attributes'] = array('class' => 'views-ajax-form');
1959 * Submit handler to add a remove to a display from a view.
1961 function views_ui_remove_display_form_submit($form, &$form_state) {
1962 // Create the new display
1963 $plugin = views_fetch_plugin_data('display', $form_state['view']->display
[$form_state['display_id']]->display_plugin
);
1964 if (empty($plugin['no remove'])) {
1965 $id = $form_state['display_id'];
1966 $form_state['view']->display
[$id]->deleted
= TRUE
;
1969 views_ui_cache_set($form_state['view']);
1974 * Submit handler to add a restore a removed display to a view.
1976 function views_ui_remove_display_form_restore($form, &$form_state) {
1977 // Create the new display
1978 $id = $form_state['display_id'];
1979 $form_state['view']->display
[$id]->deleted
= FALSE
;
1982 views_ui_cache_set($form_state['view']);
1986 * Page callback to display analysis information on a view.
1988 function views_ui_analyze_view($js, $view) {
1989 views_include('ajax');
1990 $form_state = array(
1995 $output = views_ajax_form_wrapper('views_ui_analyze_view_form', $form_state);
1998 // If we don't have an output object, it was submitted. Set up the submission.
1999 if (empty($output)) {
2000 return views_ui_regenerate_tabs($view);
2002 return views_ajax_render($output);
2009 * This form doesn't particularly do much; it's really just providing a link
2010 * but a button seems like it would be nicer here.
2012 * It has no submit or anything, as we will never actually submit this form
2013 * where the form is placed.
2015 function views_ui_analyze_view_button(&$form_state, $view) {
2016 $form['#action'] = url("admin/build/views/nojs/analyze/$view->name");
2017 $form['#attributes'] = array('class' => 'views-ajax-form');
2018 $form['submit'] = array(
2019 '#type' => 'submit',
2020 '#value' => t('Analyze'),
2027 * Form constructor callback to display analysis information on a view
2029 function views_ui_analyze_view_form(&$form_state) {
2030 $view = &$form_state['view'];
2032 $form['#title'] = t('View analysis');
2033 $form['#section'] = 'analyze';
2035 views_include('analyze');
2036 $messages = views_analyze_view($view);
2038 $form['analysis'] = array(
2039 '#prefix' => '<div class="form-item">',
2040 '#suffix' => '</div>',
2041 '#value' => views_analyze_format_result($view, $messages),
2044 // Inform the standard button function that we want an OK button.
2045 $form_state['ok_button'] = TRUE
;
2046 views_ui_standard_form_buttons($form, $form_state, 'views_ui_analyze_view_form');
2051 * Submit handler for views_ui_analyze_view_form
2053 function views_ui_analyze_view_form_submit($form, &$form_state) {
2054 $form_state['redirect'] = 'admin/build/views/edit/' .
$form_state['view']->name
;
2058 * Page callback to display analysis information on a view.
2060 function views_ui_reorder_view($js, $view) {
2061 views_include('ajax');
2062 $form_state = array(
2067 $output = views_ajax_form_wrapper('views_ui_reorder_displays_form', $form_state);
2070 if(empty($output)) {
2071 // I don't want preprocess to modify the views -> no references
2072 $vars = array('view' => $view);
2073 template_preprocess_views_ui_edit_view($vars);
2074 $output = new
stdClass();
2075 $output->replace
['.views-tabset'] = $vars['tabs'];
2076 // Not the right place to have html i know !
2077 $output->replace
['.views-quick-links'] = '<div class="views-quick-links">'.
$vars['quick_links'] .
'</div>';
2078 // Doesn't work yet, maybe we should reload the page dunno
2079 //return views_ui_regenerate_tabs($view);
2081 return views_ajax_render($output);
2088 * Form constructor callback to reorder displays on a view
2090 function views_ui_reorder_displays_form(&$form_state) {
2091 $view = &$form_state['view'];
2093 $form['view'] = array('#type' => 'value', '#value' => $view);
2095 $form['#tree'] = TRUE
;
2097 $last_display = end($view->display
);
2099 foreach ($view->display as
$display) {
2100 $form[$display->id
] = array(
2101 'title' => array('#value' => $display->display_title
),
2103 '#type' => 'weight',
2104 '#value' => $display->position
,
2105 '#delta' => $last_display->position
,
2108 '#display' => $display,
2110 '#type' => 'checkbox',
2111 '#id' => 'display-removed-' .
$display->id
,
2112 '#attributes' => array('class' => 'views-remove-checkbox'),
2113 '#default_value' => isset($display->deleted
),
2117 if (isset($display->deleted
) && $display->deleted
) {
2118 $form[$display->id
]['deleted'] = array('#type' => 'value', '#value' => TRUE
);
2120 if ($display->id
=== 'default') {
2121 unset($form[$display->id
]['weight']);
2122 unset($form[$display->id
]['removed']);
2127 $form['#title'] = t('Displays Reorder');
2128 $form['#section'] = 'reorder';
2130 // Add javascript settings that will be added via $.extend for tabledragging
2131 $form['#js']['tableDrag']['reorder-displays']['weight'][0] = array(
2132 'target' => 'weight',
2134 'relationship' => 'sibling',
2135 'action' => 'order',
2141 $form['#action'] = url('admin/build/views/nojs/reorder-display/'.
$view->name
);
2143 views_ui_standard_form_buttons($form, $form_state, 'views_ui_reorder_displays_form');
2150 * Display position sorting function
2152 function _views_position_sort($display1, $display2) {
2153 if ($display1->position
!= $display2->position
) {
2154 return $display1->position
< $display2->position ?
-1 : 1;
2161 * Submit handler for rearranging display form
2163 function views_ui_reorder_displays_form_submit($form, &$form_state) {
2164 foreach($form_state['input'] as
$display => $info) {
2165 // add each value that is a field with a weight to our list, but only if
2166 // it has had its 'removed' checkbox checked.
2167 if (is_array($info) && isset($info['weight']) && empty($info['removed'])) {
2168 $order[$display] = $info['weight'];
2172 // Sort the order array
2175 // Fixing up positions
2178 foreach(array_keys($order) as
$display) {
2179 $order[$display] = $position++;
2182 // Setting up position and removing deleted displays
2183 $displays = $form_state['view']->display
;
2184 foreach($displays as
$display_id => $display) {
2185 // Don't touch the default !!!
2186 if ($display_id === 'default') {
2189 if (isset($order[$display_id])) {
2190 $form_state['view']->display
[$display_id]->position
= $order[$display_id];
2193 $form_state['view']->display
[$display_id]->deleted
= TRUE
;
2197 // Sorting back the display array as the position is not enough
2198 uasort($form_state['view']->display
, '_views_position_sort');
2201 views_ui_cache_set($form_state['view']);
2202 $form_state['redirect'] = array('admin/build/views/edit/' .
$form_state['view']->name
, NULL
, 'views-tab-default');
2206 * Turn the reorder form into a proper table
2208 function theme_views_ui_reorder_displays_form($form) {
2210 foreach (element_children($form) as
$key) {
2211 if (isset($form[$key]['#display'])) {
2212 $display = &$form[$key];
2215 $row[] = drupal_render($display['title']);
2216 $form[$key]['weight']['#attributes']['class'] = 'weight';
2217 $row[] = drupal_render($form[$key]['weight']);
2218 if (isset($display['removed'])) {
2219 $row[] = drupal_render($form[$key]['removed']) .
2220 l('<span>' .
t('Remove') .
'</span>',
2221 'javascript:void()',
2223 'attributes' => array(
2224 'id' => 'display-remove-link-' .
$key,
2225 'class' => 'views-button-remove display-remove-link',
2226 'alt' => t('Remove this display'),
2227 'title' => t('Remove this display')),
2235 if (isset($form[$key]['weight']['#type'])) {
2236 $class[] = 'draggable';
2238 if (isset($form[$key]['deleted']['#value']) && $form[$key]['deleted']['#value']) {
2239 $styles[] = 'display: none;';
2241 $rows[] = array('data' => $row, 'class' => implode(' ', $class), 'id' => 'display-row-' .
$key, 'style' => implode(' ', $styles));
2245 $header = array(t('Display'), t('Weight'), t('Remove'));
2247 drupal_add_tabledrag('reorder-displays', 'order', 'sibling', 'weight');
2249 $output = drupal_render($form['override']);
2250 $output .
= theme('table', $header, $rows, array('id' => 'reorder-displays'));
2251 $output .
= drupal_render($form);
2257 * This form doesn't particularly do much; it's really just providing a link
2258 * but a button seems like it would be nicer here.
2260 * It has no submit or anything, as we will never actually submit this form
2261 * where the form is placed.
2263 function views_ui_reorder_displays_button(&$form_state, $view) {
2264 $form['#action'] = url("admin/build/views/nojs/reorder-displays/$view->name");
2265 $form['#attributes'] = array('class' => 'views-ajax-form');
2266 $form['submit'] = array(
2267 '#type' => 'submit',
2268 '#value' => t('Reorder'),
2275 * Page callback to edit details of a view.
2277 function views_ui_edit_details($property, $js, $view) {
2278 views_include('ajax');
2279 $form_state = array(
2282 'property' => $property,
2285 $output = views_ajax_form_wrapper('views_ui_edit_details_form', $form_state);
2288 // If we don't have an output object, it was submitted. Set up the submission.
2289 if (empty($output)) {
2290 return views_ui_regenerate_tabs($view);
2292 return views_ajax_render($output);
2299 * Form constructor callback to edit details of a view
2301 function views_ui_edit_details_form(&$form_state) {
2302 $view = &$form_state['view'];
2304 $form['#title'] = t('View details');
2305 $form['#section'] = 'details';
2307 switch ($form_state['property']) {
2309 $form['human_name'] = array(
2310 '#type' => 'textfield',
2311 '#title' => t('Human readable name'),
2312 '#description' => t('You can use a more descriptive name for this view here. Spaces are allowed'),
2313 '#default_value' => $view->human_name
,
2318 $form['tag'] = array(
2319 '#type' => 'textfield',
2320 '#title' => t('View tag'),
2321 '#description' => t('Enter an optional tag for this view; it is used only to help sort views on the administrative page.'),
2322 '#default_value' => $view->tag
,
2323 '#autocomplete_path' => 'admin/views/ajax/autocomplete/tag',
2328 $form['description'] = array(
2329 '#type' => 'textfield',
2330 '#title' => t('View description'),
2331 '#description' => t('This description will appear on the Views administrative UI to tell you what the view is about.'),
2332 '#default_value' => $view->description
,
2337 views_ui_standard_form_buttons($form, $form_state, 'views_ui_edit_details_form');
2342 * Submit handler for views_ui_edit_details_form
2344 function views_ui_edit_details_form_submit($form, &$form_state) {
2345 $form_state['view']->{$form_state['property']} = $form_state['values'][$form_state['property']];
2346 views_ui_cache_set($form_state['view']);
2347 $form_state['redirect'] = 'admin/build/views/edit/' .
$form_state['view']->name
;
2351 * Form constructor callback to edit display of a view
2353 function views_ui_edit_display_form(&$form_state) {
2354 $view = &$form_state['view'];
2355 $display_id = $form_state['display_id'];
2356 $section = $form_state['section'];
2358 if (!$view->set_display($display_id)) {
2359 views_ajax_render(t('Invalid display id @display', array('@display' => $display_id)));
2361 $display = &$view->display
[$display_id];
2363 // Get form from the handler.
2364 $display->handler
->options_form($form, $form_state);
2366 if (isset($form_state['update_name'])) {
2367 $name = $form_state['update_name'];
2370 views_ui_standard_form_buttons($form, $form_state, 'views_ui_edit_display_form', $name);
2375 * Validate handler for views_ui_edit_display_form
2377 function views_ui_edit_display_form_validate($form, &$form_state) {
2378 $display = &$form_state['view']->display
[$form_state['display_id']];
2379 $display->handler
->options_validate($form, $form_state);
2383 * Submit handler for views_ui_edit_display_form
2385 function views_ui_edit_display_form_submit($form, &$form_state) {
2386 $display = &$form_state['view']->display
[$form_state['display_id']];
2387 $display->handler
->options_submit($form, $form_state);
2389 views_ui_cache_set($form_state['view']);
2393 * Override handler for views_ui_edit_display_form
2395 function views_ui_edit_display_form_override($form, &$form_state) {
2396 $display = &$form_state['view']->display
[$form_state['display_id']];
2397 $display->handler
->options_override($form, $form_state);
2399 views_ui_cache_set($form_state['view']);
2400 $form_state['rerender'] = TRUE
;
2401 $form_state['rebuild'] = TRUE
;
2404 * Override handler and submit views_ui_edit_display_form
2406 function views_ui_edit_display_form_override_update(&$form, &$form_state) {
2407 $display = &$form_state['view']->display
[$form_state['display_id']];
2408 $display->handler
->options_override($form, $form_state);
2409 $display->handler
->options_submit($form, $form_state);
2410 views_ui_cache_set($form_state['view']);
2414 * Override handler and submit views_ui_edit_display_form
2416 function views_ui_edit_display_form_override_update_section(&$form, &$form_state) {
2417 // Update the #section so it knows what to mark changed.
2418 $form['#section'] = str_replace('default-', $form_state['display_id'] .
'-', $form['#section']);
2422 * Form to config items in the views UI.
2424 function views_ui_config_type_form(&$form_state) {
2425 $view = &$form_state['view'];
2426 $display_id = $form_state['display_id'];
2427 $type = $form_state['type'];
2429 $types = views_object_types();
2430 if (!$view->set_display($display_id)) {
2431 views_ajax_render(t('Invalid display id @display', array('@display' => $display_id)));
2433 $display = &$view->display
[$display_id];
2434 $form['#title'] = check_plain($display->display_title
) .
': ';
2435 $form['#title'] .
= t('Configure @type', array('@type' => $types[$type]['ltitle']));
2436 $form['#section'] = $display_id .
'config-item';
2438 if ($display->handler
->defaultable_sections($types[$type]['plural'])) {
2439 $form_state['section'] = $types[$type]['plural'];
2440 $display->handler
->add_override_button($form, $form_state, $form_state['section']);
2443 if (!empty($types[$type]['options']) && function_exists($types[$type]['options'])) {
2444 $options = $type .
'_options';
2445 $form[$options] = array('#tree' => TRUE
);
2446 $types[$type]['options']($form, $form_state);
2450 if (isset($form_state['update_name'])) {
2451 $name = $form_state['update_name'];
2454 views_ui_standard_form_buttons($form, $form_state, 'views_ui_config_type_form', $name);
2459 * Submit handler for type configuration form
2461 function views_ui_config_type_form_submit($form, &$form_state) {
2462 $types = views_object_types();
2463 $display = &$form_state['view']->display
[$form_state['display_id']];
2466 views_ui_cache_set($form_state['view']);
2470 * Configure settings particular to filters.
2472 function views_ui_config_filters_form(&$form, &$form_state) {
2477 * Form to rearrange items in the views UI.
2479 function views_ui_rearrange_form(&$form_state) {
2480 $view = &$form_state['view'];
2481 $display_id = $form_state['display_id'];
2482 $type = $form_state['type'];
2484 $types = views_object_types();
2485 if (!$view->set_display($display_id)) {
2486 views_ajax_render(t('Invalid display id @display', array('@display' => $display_id)));
2488 $display = &$view->display
[$display_id];
2489 $form['#title'] = check_plain($display->display_title
) .
': ';
2490 $form['#title'] .
= t('Rearrange @type', array('@type' => $types[$type]['ltitle']));
2491 $form['#section'] = $display_id .
'rearrange-item';
2493 if ($display->handler
->defaultable_sections($types[$type]['plural'])) {
2494 $form_state['section'] = $types[$type]['plural'];
2495 $display->handler
->add_override_button($form, $form_state, $form_state['section']);
2500 // Get relationship labels
2501 $relationships = array();
2502 foreach ($display->handler
->get_handlers('relationship') as
$id => $handler) {
2503 $relationships[$id] = $handler->label();
2506 foreach ($display->handler
->get_option($types[$type]['plural']) as
$id => $field) {
2507 $form['fields'][$id] = array('#tree' => TRUE
);
2508 $form['fields'][$id]['weight'] = array(
2509 '#type' => 'textfield',
2510 '#default_value' => ++$count,
2512 $handler = $display->handler
->get_handler($type, $id);
2514 $name = $handler->ui_name() .
' ' .
$handler->admin_summary();
2515 if (!empty($field['relationship']) && !empty($relationships[$field['relationship']])) {
2516 $name = '(' .
$relationships[$field['relationship']] .
') ' .
$name;
2519 $form['fields'][$id]['name'] = array(
2524 $form['fields'][$id]['name'] = array('#value' => t('Broken field @id', array('@id' => $id)));
2526 $form['fields'][$id]['removed'] = array(
2527 '#type' => 'checkbox',
2528 '#id' => 'views-removed-' .
$id,
2529 '#attributes' => array('class' => 'views-remove-checkbox'),
2530 '#default_value' => 0,
2534 // Add javascript settings that will be added via $.extend for tabledragging
2535 $form['#js']['tableDrag']['arrange']['weight'][0] = array(
2536 'target' => 'weight',
2538 'relationship' => 'sibling',
2539 'action' => 'order',
2545 if (isset($form_state['update_name'])) {
2546 $name = $form_state['update_name'];
2549 views_ui_standard_form_buttons($form, $form_state, 'views_ui_rearrange_form');
2554 * Turn the rearrange form into a proper table
2556 function theme_views_ui_rearrange_form($form) {
2558 foreach (element_children($form['fields']) as
$id) {
2559 if (isset($form['fields'][$id]['name'])) {
2561 $row[] = drupal_render($form['fields'][$id]['name']);
2562 $form['fields'][$id]['weight']['#attributes']['class'] = 'weight';
2563 $row[] = drupal_render($form['fields'][$id]['weight']);
2564 $row[] = drupal_render($form['fields'][$id]['removed']) .
l('<span>' .
t('Remove') .
'</span>', 'javascript:void()', array('attributes' => array('id' => 'views-remove-link-' .
$id, 'class' => 'views-hidden views-button-remove views-remove-link', 'alt' => t('Remove this item'), 'title' => t('Remove this item')), 'html' => true
));
2566 $rows[] = array('data' => $row, 'class' => 'draggable', 'id' => 'views-row-' .
$id);
2570 $rows[] = array(array('data' => t('No fields available.'), 'colspan' => '2'));
2573 $header = array('', t('Weight'), t('Remove'));
2574 drupal_add_tabledrag('arrange', 'order', 'sibling', 'weight');
2575 $output = drupal_render($form['override']);
2576 $output .
= theme('table', $header, $rows, array('id' => 'arrange'));
2577 $output .
= drupal_render($form);
2583 * Submit handler for rearranging form
2585 function views_ui_rearrange_form_submit($form, &$form_state) {
2586 $types = views_object_types();
2587 $display = &$form_state['view']->display
[$form_state['display_id']];
2589 $old_fields = $display->handler
->get_option($types[$form_state['type']]['plural']);
2590 $new_fields = $order = array();
2592 // Make an array with the weights
2593 foreach ($form_state['values'] as
$field => $info) {
2594 // add each value that is a field with a weight to our list, but only if
2595 // it has had its 'removed' checkbox checked.
2596 if (is_array($info) && isset($info['weight']) && empty($info['removed'])) {
2597 $order[$field] = $info['weight'];
2604 // Create a new list of fields in the new order.
2605 foreach (array_keys($order) as
$field) {
2606 $new_fields[$field] = $old_fields[$field];
2608 $display->handler
->set_option($types[$form_state['type']]['plural'], $new_fields);
2611 views_ui_cache_set($form_state['view']);
2615 * Form to rearrange items in the views UI.
2617 function views_ui_rearrange_filter_form(&$form_state) {
2618 $view = &$form_state['view'];
2619 $display_id = $form_state['display_id'];
2620 $type = $form_state['type'];
2622 $types = views_object_types();
2623 if (!$view->set_display($display_id)) {
2624 views_ajax_render(t('Invalid display id @display', array('@display' => $display_id)));
2626 $display = &$view->display
[$display_id];
2627 $form['#title'] = check_plain($display->display_title
) .
': ';
2628 $form['#title'] .
= t('Rearrange @type', array('@type' => $types[$type]['ltitle']));
2629 $form['#section'] = $display_id .
'rearrange-item';
2631 if ($display->handler
->defaultable_sections($types[$type]['plural'])) {
2632 $form_state['section'] = $types[$type]['plural'];
2633 $display->handler
->add_override_button($form, $form_state, $form_state['section']);
2636 if (!empty($view->form_cache
)) {
2637 $groups = $view->form_cache
['groups'];
2638 $handlers = $view->form_cache
['handlers'];
2641 $groups = $display->handler
->get_option('filter_groups');
2642 $handlers = $display->handler
->get_option($types[$type]['plural']);
2646 // Get relationship labels
2647 $relationships = array();
2648 foreach ($display->handler
->get_handlers('relationship') as
$id => $handler) {
2649 $relationships[$id] = $handler->label();
2652 $group_options = array();
2655 * Filter groups is an array that contains:
2657 * 'operator' => 'and' || 'or',
2658 * 'groups' => array(
2659 * $group_id => 'and' || 'or',
2664 $grouping = count(array_keys($groups['groups'])) > 1;
2666 $form['filter_groups']['#tree'] = TRUE
;
2667 $form['filter_groups']['operator'] = array(
2668 '#type' => 'select',
2669 '#options' => array (
2673 '#default_value' => $groups['operator'],
2674 '#attributes' => array(
2675 'class' => 'warning-on-change',
2677 '#title' => t('Operator to use on all groups'),
2678 '#description' => t('Either "group 0 AND group 1 AND group 2" or "group 0 OR group 1 OR group 2", etc'),
2679 '#access' => $grouping,
2682 $form['remove_groups']['#tree'] = TRUE
;
2684 foreach ($groups['groups'] as
$id => $group) {
2685 $form['filter_groups']['groups'][$id] = array(
2686 '#title' => t('Group operator'),
2687 '#type' => 'select',
2688 '#options' => array(
2692 '#default_value' => $group,
2693 '#attributes' => array(
2694 'class' => 'warning-on-change',
2698 $form['remove_groups'][$id] = array(); // to prevent a notice
2700 $form['remove_groups'][$id] = array(
2701 '#type' => 'submit',
2702 '#value' => t('Remove group @group', array('@group' => $id)),
2706 $group_options[$id] = $id == 0 ?
t('Default group') : t('Group @group', array('@group' => $id));
2707 $form['#group_renders'][$id] = array();
2710 $form['#group_options'] = $group_options;
2711 $form['#groups'] = $groups;
2712 // We don't use get_handlers() because we want items without handlers to
2713 // appear and show up as 'broken' so that the user can see them.
2714 $form['filters'] = array('#tree' => TRUE
);
2715 foreach ($handlers as
$id => $field) {
2716 // If the group does not exist, move the filters to the default group.
2717 if (empty($field['group']) || empty($groups['groups'][$field['group']])) {
2718 $field['group'] = 0;
2721 $handler = $display->handler
->get_handler($type, $id);
2722 if ($grouping && $handler && !$handler->can_group()) {
2723 $field['group'] = 'ungroupable';
2726 // If not grouping and the handler is set ungroupable, move it back to
2727 // the default group to prevent weird errors from having it be in its
2729 if (!$grouping && $field['group'] == 'ungroupable') {
2730 $field['group'] = 0;
2733 // Place this item into the proper group for rendering.
2734 $form['#group_renders'][$field['group']][] = $id;
2736 $form['filters'][$id]['weight'] = array(
2737 '#type' => 'textfield',
2738 '#default_value' => ++$count,
2740 $form['filters'][$id]['group'] = array(
2741 '#type' => 'select',
2742 '#options' => $group_options,
2743 '#default_value' => $field['group'],
2744 '#attributes' => array(
2745 'class' => 'views-region-select views-region-' .
$id,
2747 '#access' => $field['group'] !== 'ungroupable',
2751 $name = $handler->ui_name() .
' ' .
$handler->admin_summary();
2752 if (!empty($field['relationship']) && !empty($relationships[$field['relationship']])) {
2753 $name = '(' .
$relationships[$field['relationship']] .
') ' .
$name;
2756 $form['filters'][$id]['name'] = array(
2761 $form['filters'][$id]['name'] = array('#value' => t('Broken field @id', array('@id' => $id)));
2763 $form['filters'][$id]['removed'] = array(
2764 '#type' => 'checkbox',
2765 '#id' => 'views-removed-' .
$id,
2766 '#attributes' => array('class' => 'views-remove-checkbox'),
2767 '#default_value' => 0,
2771 // Add javascript settings that will be added via $.extend for tabledragging
2772 // Equivalent: drupal_add_tabledrag('arrange', 'order', 'sibling', 'weight');
2773 $form['#js']['tableDrag']['arrange']['weight'][0] = array(
2774 'target' => 'weight',
2776 'relationship' => 'sibling',
2777 'action' => 'order',
2782 $form['#js']['tableDrag']['ungroupable_arrange']['weight'][0] = array(
2783 'target' => 'weight',
2785 'relationship' => 'sibling',
2786 'action' => 'order',
2791 foreach ($form['#group_renders'] as
$group_id => $title) {
2792 // Add javascript settings that will be added via $.extend for tabledragging
2793 // Equivalent: drupal_add_tabledrag('arrange', 'match', 'sibling', 'views-group-select', 'views-group-' . $group_id);
2794 $form['#js']['tableDrag']['arrange']['views-group-select'][] = array(
2795 'target' => 'views-group-' .
$group_id,
2796 'source' => 'views-group-' .
$group_id,
2797 'relationship' => 'sibling',
2798 'action' => 'match',
2804 if (isset($form_state['update_name'])) {
2805 $name = $form_state['update_name'];
2808 views_ui_standard_form_buttons($form, $form_state, 'views_ui_rearrange_filter_form');
2809 $form['buttons']['add_group'] = array(
2810 '#type' => 'submit',
2811 '#value' => t('Add new group'),
2812 '#id' => 'views-add-group',
2820 * Turn the rearrange form into a proper table
2822 function theme_views_ui_rearrange_filter_form($form) {
2823 $rows = $ungroupable_rows = array();
2824 // Enable grouping only if > 1 group.
2825 $grouping = count(array_keys($form['#group_options'])) > 1;
2827 foreach ($form['#group_renders'] as
$group_id => $contents) {
2828 // Header row for the group.
2829 if ($grouping && $group_id !== 'ungroupable') {
2831 $row[] = array('class' => 'group group-title', 'data' => $form['#group_options'][$group_id]);
2832 $row[] = array('class' => 'group container-inline', 'data' => drupal_render($form['filter_groups']['groups'][$group_id]), 'colspan' => 3);
2833 $rows[] = array('class' => 'views-group', 'data' => $row, 'id' => 'views-group-' .
$group_id);
2834 // Row which will only appear if the group has nothing in it:
2836 $class = 'group-' .
(count($contents) ?
'populated' : 'empty');
2837 $row[] = array('colspan' => 4, 'data' => '<span>' .
t('This group is empty') .
'</span> ' .
2838 drupal_render($form['remove_groups'][$group_id]));
2839 $rows[] = array('class' => "group-message group-$group_id-message " .
$class, 'data' => $row, 'id' => 'views-group-' .
$group_id);
2842 foreach ($contents as
$id) {
2843 if (isset($form['filters'][$id]['name'])) {
2845 $row[] = drupal_render($form['filters'][$id]['name']);
2846 $form['filters'][$id]['weight']['#attributes']['class'] = 'weight';
2847 $row[] = drupal_render($form['filters'][$id]['weight']);
2848 $form['filters'][$id]['group']['#attributes']['class'] = 'views-group-select views-group-' .
$group_id;
2849 $row[] = drupal_render($form['filters'][$id]['group']);
2850 $row[] = drupal_render($form['filters'][$id]['removed']) .
l('<span>' .
t('Remove') .
'</span>', 'javascript:void()', array('attributes' => array('id' => 'views-remove-link-' .
$id, 'class' => 'views-hidden views-button-remove views-groups-remove-link', 'alt' => t('Remove this item'), 'title' => t('Remove this item')), 'html' => true
));
2852 $row = array('data' => $row, 'class' => 'draggable', 'id' => 'views-row-' .
$id);
2853 if ($group_id !== 'ungroupable') {
2857 $ungroupable_rows[] = $row;
2863 $rows[] = array(array('data' => t('No fields available.'), 'colspan' => '2'));
2866 $output = drupal_render($form['override']);
2868 $output .
= drupal_render($form['filter_groups']['operator']);
2871 $form['filter_groups']['groups'][0]['#title'] = t('Operator');
2872 $output .
= drupal_render($form['filter_groups']['groups'][0]);
2875 if (!empty($ungroupable_rows)) {
2876 drupal_add_tabledrag('ungroupable_arrange', 'order', 'sibling', 'weight');
2877 $header = array(t('Ungroupable filters'), t('Weight'), array('class' => 'views-hide-label', 'data' => t('Group')), array('class' => 'views-hide-label', 'data' => t('Remove')));
2878 $output .
= theme('table', $header, $ungroupable_rows, array('id' => 'ungroupable_arrange', 'class' => 'arrange'));
2881 drupal_add_tabledrag('arrange', 'order', 'sibling', 'weight');
2882 $header = array('', t('Weight'), t('Group'), t('Remove'));
2883 $output .
= theme('table', $header, $rows, array('id' => 'arrange', 'class' => 'arrange'));
2884 $output .
= drupal_render($form);
2890 * Submit handler for rearranging form
2892 function views_ui_rearrange_filter_form_submit($form, &$form_state) {
2893 $types = views_object_types();
2894 $display = &$form_state['view']->display
[$form_state['display_id']];
2895 $remember_groups = array();
2897 if (!empty($form_state['view']->form_cache
)) {
2898 $old_fields = $form_state['view']->form_cache
['handlers'];
2901 $old_fields = $display->handler
->get_option($types[$form_state['type']]['plural']);
2905 $groups = $form_state['values']['filter_groups'];
2906 // Whatever button was clicked, re-calculate field information.
2907 $new_fields = $order = array();
2909 // Make an array with the weights
2910 foreach ($form_state['values']['filters'] as
$field => $info) {
2911 // add each value that is a field with a weight to our list, but only if
2912 // it has had its 'removed' checkbox checked.
2913 if (is_array($info) && empty($info['removed'])) {
2914 if (isset($info['weight'])) {
2915 $order[$field] = $info['weight'];
2918 if (isset($info['group'])) {
2919 $old_fields[$field]['group'] = $info['group'];
2920 $remember_groups[$info['group']][] = $field;
2928 // Create a new list of fields in the new order.
2929 foreach (array_keys($order) as
$field) {
2930 $new_fields[$field] = $old_fields[$field];
2933 // Save if the actual update button was clicked.
2934 if (!empty($form_state['clicked_button']['#group'])) {
2935 if ($form_state['clicked_button']['#group'] == 'add') {
2937 $groups['groups'][] = 'AND';
2940 // Renumber groups above the removed one down.
2941 foreach (array_keys($groups['groups']) as
$group_id) {
2942 if ($group_id >= $form_state['clicked_button']['#group']) {
2943 $old_group = $group_id + 1;
2944 if (isset($groups['groups'][$old_group])) {
2945 $groups['groups'][$group_id] = $groups['groups'][$old_group];
2946 if (isset($remember_groups[$old_group])) {
2947 foreach ($remember_groups[$old_group] as
$id) {
2948 $new_fields[$id]['group'] = $group_id;
2953 // If this is the last one, just unset it.
2954 unset($groups['groups'][$group_id]);
2959 // Update our cache with values so that cancel still works the way
2961 $form_state['view']->form_cache
= array(
2962 'key' => 'rearrange-filter',
2963 'groups' => $groups,
2964 'handlers' => $new_fields,
2967 // Return to this form except on actual Update.
2968 views_ui_add_form_to_stack('rearrange-filter', $form_state['view'], $form_state['display_id'], array($form_state['type']));
2971 // Actually write changed handler values.
2972 $display->handler
->set_option($types[$form_state['type']]['plural'], $new_fields);
2973 $display->handler
->set_option('filter_groups', $groups);
2974 if (isset($form_state['view']->form_cache
)) {
2975 unset($form_state['view']->form_cache
);
2980 views_ui_cache_set($form_state['view']);
2984 * Form to add_item items in the views UI.
2986 function views_ui_add_item_form(&$form_state) {
2987 $view = &$form_state['view'];
2988 $display_id = $form_state['display_id'];
2989 $type = $form_state['type'];
2991 if (!$view->set_display($display_id)) {
2992 views_ajax_render(t('Invalid display id @display', array('@display' => $display_id)));
2994 $display = &$view->display
[$display_id];
2996 $types = views_object_types();
2997 $ltitle = $types[$type]['ltitle'];
2999 if (!empty($types[$type]['type'])) {
3000 $type = $types[$type]['type'];
3003 $form['#title'] = check_plain($display->display_title
) .
': ';
3004 $form['#title'] .
= t('Add @type', array('@type' => $ltitle));
3005 $form['#section'] = $display_id .
'add-item';
3007 // Figure out all the base tables allowed based upon what the relationships provide.
3008 $base_tables = $view->get_base_tables();
3009 $options = views_fetch_fields(array_keys($base_tables), $type, $display->handler
->use_group_by());
3011 if (!empty($options)) {
3012 $groups = array('all' => t('- All -'));
3013 $form['group'] = array(
3014 '#type' => 'select',
3015 '#title' => t('Groups'),
3016 '#options' => array(),
3017 '#attributes' => array('class' => 'views-master-dependent'),
3020 $form['name'] = array(
3021 '#prefix' => '<div class="views-radio-box form-checkboxes">',
3022 '#suffix' => '</div>',
3024 '#default_value' => 'all',
3027 // Group options first to simplify the DOM objects that Views
3028 // dependent JS will act upon.
3029 $grouped_options = array();
3030 foreach ($options as
$key => $option) {
3031 $group = preg_replace('/[^a-z0-9]/', '-', strtolower($option['group']));
3032 $groups[$group] = $option['group'];
3033 $grouped_options[$group][$key] = $option;
3036 foreach ($grouped_options as
$group => $group_options) {
3037 $form['name'][$group .
'_start'] = array('#type' => 'markup', '#value' => '<div class="views-dependent-all views-dependent-' .
$group .
'">');
3038 foreach ($group_options as
$key => $option) {
3039 $form['name'][$key] = array(
3040 '#type' => 'checkbox',
3041 '#title' => t('!group: !field', array('!group' => $option['group'], '!field' => $option['title'])),
3042 '#description' => $option['help'],
3043 '#return_value' => $key,
3046 $form['name'][$group .
'_end'] = array('#type' => 'markup', '#value' => '</div>');
3049 $form['group']['#options'] = $groups;
3052 $form['markup'] = array(
3053 '#value' => '<div class="form-item">' .
t('There are no @types available to add.', array('@types' => $ltitle)) .
'</div>',
3056 views_ui_standard_form_buttons($form, $form_state, 'views_ui_add_item_form', t('Add'));
3062 * Submit handler for adding new item(s) to a view.
3064 function views_ui_add_item_form_submit($form, &$form_state) {
3065 $type = $form_state['type'];
3066 $types = views_object_types();
3068 if (!empty($form_state['values']['name']) && is_array($form_state['values']['name'])) {
3069 // Loop through each of the items that were checked and add them to the view.
3070 foreach (array_keys(array_filter($form_state['values']['name'])) as
$field) {
3071 list($table, $field) = explode('.', $field, 2);
3072 $id = $form_state['view']->add_item($form_state['display_id'], $type, $table, $field);
3074 // check to see if we have group by settings
3075 if ($form_state['view']->display_handler
->use_group_by()) {
3076 views_ui_add_form_to_stack('config-item-group', $form_state['view'], $form_state['display_id'], array($type, $id));
3079 // check to see if this type has settings, if so add the settings form first
3080 $handler = views_get_handler($table, $field, $type);
3081 if ($handler && $handler->has_extra_options()) {
3082 views_ui_add_form_to_stack('config-item-extra', $form_state['view'], $form_state['display_id'], array($type, $id));
3084 // Then add the form to the stack
3085 views_ui_add_form_to_stack('config-item', $form_state['view'], $form_state['display_id'], array($type, $id));
3089 if (isset($form_state['view']->form_cache
)) {
3090 unset($form_state['view']->form_cache
);
3094 views_ui_cache_set($form_state['view']);