[#1205626] by dww: Follow-up fix to allow editing a broken converted view.
[project/views.git] / includes / admin.inc
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 if ($form_state['values']['status'] != 'all' && (!empty($view->disabled) == $form_state['values']['status'])) {
112 continue;
113 }
114
115 $item = new stdClass();
116 $item->ops = array();
117
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");
122 }
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");
126 }
127 else {
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));
130 }
131 else {
132 $item->ops[] = l(t('Enable'), "admin/build/views/enable/$view->name", array('query' => drupal_get_destination() . '&token=' . $token_enable));
133 }
134 }
135
136 $item->ops = implode(' | ', $item->ops);
137 $item->path = '';
138 if (empty($view->display)) {
139 $item->path = t('Warning! Broken view!');
140 }
141 else {
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);
149 }
150 if (empty($view->disabled) && strpos($one_path, '%') === FALSE) {
151 $all_paths[] = l($one_path, $one_path);
152 }
153 else {
154 $all_paths[] = check_plain($one_path);
155 }
156 }
157 }
158 if (!empty($all_paths)) {
159 $item->path = implode(', ', array_unique($all_paths));
160 }
161 }
162
163 $item->type = $view->type;
164 $item->name = $view->name;
165
166 if (!empty($view->tag)) {
167 $item->tag = check_plain($view->tag);
168 }
169
170 $item->title = $view->get_title();
171 $item->base = !empty($base_tables[$view->base_table]['title']) ? $base_tables[$view->base_table]['title'] : t('Broken');
172
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;
177 }
178 }
179
180 if ($item->displays) {
181 ksort($item->displays);
182 $item->displays = implode(', ', array_keys($item->displays));
183 }
184
185 $item->description = check_plain($view->description);
186 $item->classes = empty($view->disabled) ? 'view-enabled' : 'view-disabled';
187 $items[] = $item;
188
189 $sort = intval(empty($view->disabled) xor $form_state['values']['sort'] == 'asc');
190
191 switch ($form_state['values']['order']) {
192 case 'name':
193 default:
194 $sort .= strtolower($view->name);
195 break;
196 case 'title':
197 $sort .= strtolower($item->title);
198 break;
199 case 'path':
200 $sort .= strtolower($raw_path); // $path;
201 break;
202 case 'type':
203 $sort .= $view->type . $view->name;
204 break;
205 case 'tag':
206 $sort .= strtolower($view->tag);
207 break;
208 case 'desc':
209 $sort .= strtolower($view->description);
210 break;
211 }
212
213 $sorts[] = $sort;
214 }
215
216 if ($form_state['values']['sort'] == 'desc') {
217 arsort($sorts);
218 }
219 else {
220 asort($sorts);
221 }
222
223 $i = array();
224 foreach ($sorts as $id => $title) {
225 $i[] = $items[$id];
226 }
227
228 views_add_css('views-list');
229 $vars['views'] = $i;
230
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');
234 }
235
236 $vars['help'] = t('Not sure what to do? Try the "!getting-started" page.', array('!getting-started' => $getting_started));
237 }
238
239 /**
240 * Provide a form for sorting and filtering the list of views.
241 */
242 function views_ui_list_views_form(&$form_state) {
243 if (!variable_get('clean_url', FALSE)) {
244 $form['q'] = array(
245 '#type' => 'hidden',
246 '#value' => $_GET['q'],
247 );
248 }
249
250 $all = array('all' => t('- All -'));
251 $none = array('none' => t('- None -'));
252
253 $form['type'] = array(
254 '#type' => 'select',
255 '#title' => t('Storage'),
256 '#options' => array(
257 'all' => t('- All -'),
258 t('Normal') => t('Normal'),
259 t('Default') => t('Default'),
260 t('Overridden') => t('Overridden'),
261 ),
262 '#default_value' => 'all',
263 );
264
265 $status = array(
266 '0' => t('Disabled'),
267 '1' => t('Enabled'),
268 );
269 $form['status'] = array(
270 '#type' => 'select',
271 '#title' => t('Status'),
272 '#options' => array_merge($all, $status),
273 '#default_value' => 'all',
274 );
275
276 $bases = array();
277 foreach (views_fetch_base_tables() as $table => $info) {
278 $bases[$table] = $info['title'];
279 }
280
281 $form['base'] = array(
282 '#type' => 'select',
283 '#title' => t('Type'),
284 '#options' => array_merge($all, $bases),
285 '#default_value' => 'all',
286 );
287
288 $tags = array();
289
290 $extras = array();
291 foreach ($form_state['views'] as $name => $view) {
292 if (!empty($view->tag)) {
293 $tags[$view->tag] = $view->tag;
294 }
295 }
296
297 asort($tags);
298
299 $form['tag'] = array(
300 '#type' => 'select',
301 '#title' => t('Tag'),
302 '#options' => array_merge($all, $none, $tags),
303 '#default_value' => 'all',
304 );
305
306 $displays = array();
307 foreach (views_fetch_plugin_data('display') as $id => $info) {
308 if (!empty($info['admin'])) {
309 $displays[$id] = $info['admin'];
310 }
311 }
312
313 asort($displays);
314
315 $form['display'] = array(
316 '#type' => 'select',
317 '#title' => t('Displays'),
318 '#options' => array_merge($all, $displays),
319 '#default_value' => 'all',
320 );
321
322 $form['order'] = array(
323 '#type' => 'select',
324 '#title' => t('Sort by'),
325 '#options' => array(
326 'name' => t('Name'),
327 'title' => t('Title'),
328 'tag' => t('Tag'),
329 'path' => t('Path'),
330 'type' => t('Type'),
331 'desc' => t('Description'),
332 ),
333 '#default_value' => 'name',
334 );
335
336 $form['sort'] = array(
337 '#type' => 'select',
338 '#title' => t('Order'),
339 '#options' => array(
340 'asc' => t('Up'),
341 'desc' => t('Down'),
342 ),
343 '#default_value' => 'asc',
344 );
345
346 $form['submit'] = array(
347 '#name' => '', // so it won't in the $_GET args
348 '#type' => 'submit',
349 '#id' => 'edit-views-apply',
350 '#value' => t('Apply'),
351 );
352
353 if (!empty($_SESSION['views']['#admin'])) {
354 $form['reset'] = array(
355 '#type' => 'submit',
356 '#id' => 'edit-views-reset',
357 '#value' => t('Reset'),
358 );
359 }
360
361 $form['#theme'] = array('views_ui_list_views_form');
362 return $form;
363 }
364
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);
371 }
372
373 /**
374 * Page callback for the live preview.
375 *
376 * @todo make this use a template
377 */
378 function views_ui_preview($js, $view) {
379 // Take off the items we know so that we can have just the args passed
380 // in for later use.
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';
385
386 $form_state = array(
387 'display_id' => $display_id,
388 'view_args' => $func_args ? implode('/', $func_args) : '',
389 'rerender' => TRUE,
390 'no_redirect' => TRUE,
391 'view' => &$view,
392 'ajax' => $js
393 );
394
395 $output = drupal_build_form('views_ui_preview_form', $form_state);
396 $args = array();
397 if (isset($form_state['view_args']) && $form_state['view_args'] !== '') {
398 $args = explode('/', $form_state['view_args']);
399 }
400
401 $errors = $view->validate();
402 if ($errors === TRUE) {
403 $view->ajax = $js;
404 $view->live_preview = TRUE;
405
406 // Store the current view URL for later use:
407 $view->set_display($form_state['display_id']);
408 $view->set_arguments($args);
409
410 if ($view->display_handler->get_option('path')) {
411 $path = $view->get_url();
412 }
413
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'];
417
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'];
422 }
423
424 $preview = $view->preview($form_state['display_id'], $args);
425
426 // Get information from the preview for display.
427 if (empty($view->build_info['fail'])) {
428 $rows = $view->query->get_preview_info();
429
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) {
433 if ($queries) {
434 $queries .= "\n";
435 }
436 $queries .= t('[@time ms]', array('@time' => intval($query[1] * 100000) / 100)) . ' ' . check_plain($query[0]);
437 }
438
439 $rows[] = array('<strong>' . t('Other queries') . '</strong>', '<pre>' . $queries . '</pre>');
440 }
441
442 $rows[] = array('<strong>' . t('Title') . '</strong>', filter_xss_admin($view->get_title()));
443 if (isset($path)) {
444 $path = l($path, $path);
445 }
446 else {
447 $path = t('This display has no path.');
448 }
449
450 $rows[] = array('<strong>' . t('Path') . '</strong>', $path);
451
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);
456
457 $info = theme('table', array(), $rows);
458 }
459 else {
460 $info = theme('table', array(), array(array('<strong>' . t('Query') . '</strong>', t('No query was run'))));
461 }
462 }
463 else {
464 foreach ($errors as $error) {
465 drupal_set_message($error, 'error');
466 }
467 $preview = t('Unable to preview due to validation errors.');
468 $info = '';
469 }
470
471 $info = '<div class="views-query-info">' . $info . '</div>';
472
473 if (variable_get('views_ui_query_on_top', FALSE)) {
474 $output .= $info . $preview;
475 }
476 else {
477 $output .= $preview . $info;
478 }
479
480 if (!$js) {
481 views_add_css('views-admin');
482 drupal_set_title($view->get_title());
483 return $output;
484 }
485 else {
486 views_include('ajax');
487 $object = new stdClass();
488 if (!empty($view->js_settings)) {
489 $object->js = $view->js_settings;
490 }
491 $object->display = '';
492 if ($messages = theme('status_messages')) {
493 $object->display = '<div class="views-messages">' . $messages . '</div>';
494 }
495 $object->display .= $output;
496 $object->title = $view->get_title();
497 views_ajax_render($object);
498 }
499 }
500
501 /**
502 * Form for generating argument information for the live preview.
503 */
504 function views_ui_preview_form(&$form_state) {
505 $view = &$form_state['view'];
506 $view->init_display();
507 $options = array();
508 foreach ($view->display as $id => $display) {
509 $options[$id] = $display->display_title;
510 }
511
512 $form['#attributes'] = array(
513 'class' => 'clear-block',
514 );
515
516 $form['display_id'] = array(
517 '#type' => 'select',
518 '#title' => t('Display'),
519 '#options' => $options,
520 '#default_value' => $form_state['display_id'],
521 '#id' => 'preview-display-id',
522 );
523
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',
530 );
531
532 $form['preview'] = array(
533 '#type' => 'submit',
534 '#value' => t('Preview'),
535 '#id' => 'preview-submit',
536 );
537
538
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),
543 );
544
545 $form['#action'] = url("admin/build/views/nojs/preview/$view->name");
546 return $form;
547 }
548
549 /**
550 * Submit the preview form.
551 *
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.
554 */
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'];
558 }
559
560 /**
561 * Page callback to add a new view.
562 */
563 function views_ui_add_page() {
564 $form_state = array(
565 'view' => NULL
566 );
567
568 return drupal_build_form('views_ui_add_form', $form_state);
569 }
570
571 /**
572 * Page callback to add a new view.
573 */
574 function views_ui_clone_page($view) {
575 $form_state = array(
576 'view' => $view->copy(),
577 );
578
579 drupal_set_title(t('Clone view %view', array('%view' => $view->name)));
580 return drupal_build_form('views_ui_add_form', $form_state);
581 }
582
583 /**
584 * Form constructor callback to create the views Add Form, phase 1.
585 */
586 function views_ui_add_form(&$form_state) {
587 $view = $form_state['view'];
588 $form = array();
589
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.'),
594 '#required' => TRUE,
595 '#maxlength' => 32,
596 '#default_value' => $view ? $view->name : '',
597 '#attributes' => array('dir'=>'ltr'),
598 );
599
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 : '',
605 );
606
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 : '',
612 '#maxlength' => 255,
613 );
614
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',
621 );
622
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>';
626 }
627
628 $form['base_table'] = array(
629 '#type' => 'radios',
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,
634 );
635
636 if ($view) {
637 $form['base_table']['#disabled'] = TRUE;
638 }
639
640 $form['submit'] = array(
641 '#type' => 'submit',
642 '#value' => t('Next'),
643 '#validate' => array('views_ui_add_form_validate'),
644 '#submit' => array('views_ui_add_form_submit'),
645 );
646
647 return $form;
648 }
649
650 /**
651 * Validate the add view form.
652 */
653 function views_ui_add_form_validate($form, &$form_state) {
654 $name = $form_state['values']['name'];
655
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.'));
659 }
660
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.'));
665 }
666 }
667
668 /**
669 * Process the add view form
670 */
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'];
680 }
681
682 views_ui_cache_set($view);
683 $form_state['redirect'] ='admin/build/views/edit/' . $view->name;
684 }
685
686 /**
687 * Page to delete a view.
688 */
689 function views_ui_delete_confirm(&$form_state, $view) {
690 $form_state['view'] = &$view;
691 $form = array();
692
693 $cancel = 'admin/build/views';
694 if (!empty($_REQUEST['cancel'])) {
695 $cancel = $_REQUEST['cancel'];
696 }
697
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');
702 }
703 else {
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');
707 }
708
709 return confirm_form($form,
710 $title,
711 $cancel,
712 $desc,
713 $button,
714 t('Cancel'));
715 }
716
717 /**
718 * Submit handler to delete a view.
719 */
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';
725 }
726
727 /**
728 * Page to delete a view.
729 */
730 function views_ui_break_lock_confirm(&$form_state, $view) {
731 $form_state['view'] = &$view;
732 $form = array();
733
734 if (empty($view->locked)) {
735 return t('There is no lock on view %view to break.', array('%name' => $view->name));
736 }
737
738 $cancel = 'admin/build/views/edit/' . $view->name;
739 if (!empty($_REQUEST['cancel'])) {
740 $cancel = $_REQUEST['cancel'];
741 }
742
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)),
747 $cancel,
748 t('By breaking this lock, any unsaved changes made by !user will be lost!', array('!user' => theme('username', $account))),
749 t('Break lock'),
750 t('Cancel'));
751 }
752
753 /**
754 * Submit handler to break_lock a view.
755 */
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.'));
760 }
761
762 /**
763 * The main view edit page
764 */
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();
769 return $output;
770 }
771
772 /**
773 * Export a view for cut & paste.
774 */
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,
782 '#rows' => $lines,
783 );
784 return $form;
785 }
786
787 /**
788 * Import a view from cut & paste
789 */
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.'),
795 );
796
797 $form['view'] = array(
798 '#type' => 'textarea',
799 '#title' => t('Paste view code here'),
800 '#required' => TRUE,
801 );
802
803 $form['submit'] = array(
804 '#type' => 'submit',
805 '#value' => t('Import'),
806 '#submit' => array('views_ui_import_submit'),
807 '#validate' => array('views_ui_import_validate'),
808 );
809 return $form;
810 }
811
812 /**
813 * Validate handler to import a view
814 */
815 function views_ui_import_validate($form, &$form_state) {
816 $view = '';
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);
821 }
822 ob_start();
823 eval($form_state['values']['view']);
824 ob_end_clean();
825
826 if (!is_object($view)) {
827 return form_error($form['view'], t('Unable to interpret view code.'));
828 }
829
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');
836 }
837 else {
838 form_error($form['view'], t('That view is not compatible with this version of Views.'));
839 }
840 }
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())));
845 }
846
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.'));
850 }
851
852 if ($form_state['values']['name']) {
853 $view->name = $form_state['values']['name'];
854 }
855
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'));
859 }
860
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');
866 }
867 }
868 }
869
870 $form_state['view'] = &$view;
871 }
872
873 /**
874 * Submit handler for view import
875 */
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;
880 }
881
882 /**
883 * The main edit view form, which is really just a save/cancel/delete button.
884 */
885 function views_ui_edit_view_form(&$form_state, $view) {
886 $form['buttons']['save'] = array(
887 '#type' => 'submit',
888 '#value' => t('Save'),
889 '#validate' => array('views_ui_edit_view_form_validate'),
890 '#submit' => array('views_ui_edit_view_form_submit'),
891 );
892
893 $form['buttons']['cancel'] = array(
894 '#type' => 'submit',
895 '#value' => t('Cancel'),
896 '#submit' => array('views_ui_edit_view_form_cancel'),
897 );
898
899 if (is_numeric($view->vid)) {
900 $form['buttons']['delete'] = array(
901 '#type' => 'submit',
902 '#value' => $view->type == t('Overridden') ? t('Revert') : t('Delete'),
903 '#submit' => array('views_ui_edit_view_form_delete'),
904 );
905 }
906
907 $form_state['view'] = &$view;
908 return $form;
909 }
910
911 /**
912 * Validate that a view is complete and whole.
913 */
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')) {
917 return;
918 }
919
920 $errors = $form_state['view']->validate();
921 if ($errors !== TRUE) {
922 foreach ($errors as $error) {
923 form_set_error('', $error);
924 }
925 }
926 }
927
928 /**
929 * Submit handler for the edit view form.
930 */
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]);
936 }
937 }
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;
942 }
943 }
944
945 $form_state['view']->save();
946 drupal_set_message(t('The view %name has been saved.', array('%name' => $form_state['view']->get_human_name())));
947
948 // Make sure menu items get rebuilt as neces
949 menu_rebuild();
950
951 // Clear the views cache.
952 cache_clear_all('*', 'cache_views');
953
954 // Clear the page cache.
955 cache_clear_all();
956
957 // Remove this view from cache so we can edit it properly.
958 views_object_cache_clear('view', $form_state['view']->name);
959 }
960
961 /**
962 * Submit handler for the edit view form.
963 */
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');
971 }
972 }
973
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());
978 }
979
980 /**
981 * Preprocess the view edit page.
982 */
983 function template_preprocess_views_ui_edit_view(&$vars) {
984 $view = &$vars['view'];
985
986 $vars['save_button'] = drupal_get_form('views_ui_edit_view_form', $view);
987
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');
991
992 views_include('tabs');
993 $tabs = new views_tabset;
994
995 $vars['message'] = '<div class="message">' . t("Click on an item to edit that item's details.") . '</div>';
996
997 if (!$view->set_display('default')) {
998 drupal_set_message(t('This view has a broken default display and cannot be used.'), 'error');
999 }
1000
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);
1005 }
1006
1007 // This is the area that will render beneath the links
1008 $form_state = array(
1009 'view' => &$view,
1010 'ajax' => FALSE,
1011 );
1012
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);
1017
1018 $vars['tabs'] = $tabs->render();
1019
1020 $form_state = array(
1021 'display_id' => 'default',
1022 'view_args' => '',
1023 'rerender' => FALSE,
1024 'no_redirect' => TRUE,
1025 'view' => &$view,
1026 'input' => array(),
1027 );
1028 $vars['preview'] = drupal_build_form('views_ui_preview_form', $form_state);
1029
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);
1036 }
1037
1038 $vars['quick_links_raw'] = array(
1039 array(
1040 'title' => t('Export'),
1041 'alt' => t("Export this view"),
1042 'href' => "admin/build/views/export/$view->name",
1043 ),
1044 array(
1045 'title' => t('Clone'),
1046 'alt' => t("Create a copy of this view"),
1047 'href' => "admin/build/views/clone/$view->name",
1048 ),
1049 );
1050
1051 $paths = array();
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"),
1059 'href' => $path,
1060 );
1061 // Displays can have the same path; no point in showing more than one link.
1062 $paths[$path] = TRUE;
1063 }
1064 }
1065 }
1066
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');
1072
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);
1080 }
1081 }
1082 }
1083 }
1084
1085 $settings = array('views' => array('ajax' => array(
1086 'id' => '#views-ajax-pad',
1087 'title' => '#views-ajax-title',
1088 'defaultForm' => $vars['message'],
1089 )));
1090
1091 drupal_add_js($settings, 'setting');
1092 }
1093
1094 function template_preprocess_views_ui_edit_tab(&$vars) {
1095 $view = $vars['view'];
1096 $display = $vars['display'];
1097 $plugin = $display->handler->definition;
1098
1099 $top = $left = $middle = $right = '';
1100
1101 // If this form was submitted it was already handled, so force it not to
1102 // submit again.
1103
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']);
1109 }
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);
1113 }
1114
1115 // basic fields
1116 $vars['title'] = check_plain($display->display_title);
1117 $vars['description'] = check_plain($plugin['help']);
1118
1119 // Special fields if this is the default display.
1120 $vars['default'] = ($display->id == 'default');
1121 $vars['details_class'] = views_ui_item_css('details');
1122
1123 foreach (array('human_name', 'tag', 'description') as $property) {
1124 if (!empty($view->changed_sections[$property])) {
1125 $vars['details_changed'][$property] = TRUE;
1126 }
1127 switch ($property) {
1128 case 'human_name':
1129 $title = t('Human name');
1130 break;
1131 case 'tag':
1132 $title = t('Tag');
1133 break;
1134 case 'description':
1135 $title = t('Description');
1136 break;
1137 }
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')));
1140 }
1141
1142 // Calculate options from display plugin.
1143 $options = $categories = array();
1144 $display->handler->options_summary($categories, $options);
1145
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();
1151 }
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;
1158
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');
1163 }
1164 }
1165 if (!empty($option['title'])) {
1166 $data['content'] .= $option['title'] . ': ';
1167 }
1168
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;
1173 }
1174 else {
1175 $display_id = $display->id;
1176 if (!$display->handler->is_default_display()) {
1177 if ($display->handler->defaultable_sections($id)) {
1178 $data['overridden'] = TRUE;
1179 }
1180 }
1181 }
1182 $data['class'] = views_ui_item_css($display_id . '-' . $id);
1183 if (!empty($view->changed_sections[$display_id . '-' . $id])) {
1184 $data['changed'] = TRUE;
1185 }
1186 }
1187
1188 $vars['categories'] = $categories;
1189
1190 // Add a help icon
1191 if (isset($plugin['help topic'])) {
1192 $vars['display_help_icon'] = theme('advanced_help_topic', $plugin['module'], $plugin['help topic']);
1193 }
1194 else {
1195 $vars['display_help_icon'] = '';
1196 }
1197
1198 // Fetch style plugin info because it has some effect on how/what we render.
1199 $style_plugin = $display->handler->get_plugin();
1200
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);
1205 }
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);
1211 }
1212
1213 /**
1214 * Generate the summary output for a single display to render in a tab.
1215 */
1216 function views_ui_display_tab($view, $display) {
1217 if (isset($display->handler)) {
1218 $plugin = $display->handler->definition;
1219 }
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)));
1223
1224 // @todo We can do a better 'plugin does not exist' tab.
1225 }
1226
1227 // The display should always be initialized prior to this call.
1228 if (empty($display->handler)) {
1229 return FALSE;
1230 }
1231
1232 $body = theme('views_ui_edit_tab', $view, $display);
1233 return array($display->display_title, $body);
1234 }
1235
1236 /**
1237 * Add information about a section to a display.
1238 */
1239 function template_preprocess_views_ui_edit_item(&$vars) {
1240 $type = $vars['type'];
1241 $view = $vars['view'];
1242 $display = $vars['display'];
1243
1244 $types = views_object_types();
1245
1246 $vars['overridden'] = FALSE;
1247 $vars['defaulted'] = FALSE;
1248
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;
1254 return;
1255 }
1256
1257 // Different types now have different rearrange forms, so we use this switch
1258 // to get the right one.
1259 switch ($type) {
1260 case 'filter':
1261 $rearrange_url = "admin/build/views/nojs/rearrange-$type/$view->name/$display->id/$type";
1262 break;
1263 default:
1264 $rearrange_url = "admin/build/views/nojs/rearrange/$view->name/$display->id/$type";
1265 }
1266
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));
1268
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));
1270
1271 if (!$display->handler->is_default_display()) {
1272 if (!$display->handler->is_defaulted($types[$type]['plural'])) {
1273 $vars['overridden'] = TRUE;
1274 }
1275 else {
1276 $vars['defaulted'] = TRUE;
1277 }
1278 }
1279
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')));
1282 }
1283 else {
1284 $vars['title'] = $types[$type]['title'];
1285 }
1286
1287 $fields = array();
1288
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');
1295 if ($handlers) {
1296 foreach ($handlers as $id => $info) {
1297 $handler = $display->handler->get_handler('relationship', $id);
1298 $relationships[$id] = $handler->label();
1299 }
1300 }
1301 }
1302
1303 // Filters can now be grouped so we do a little bit extra:
1304 $groups = array();
1305 $grouping = FALSE;
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) {
1309 $grouping = TRUE;
1310 $groups = array(0 => array());
1311 }
1312 }
1313
1314 foreach ($display->handler->get_option($types[$type]['plural']) as $id => $field) {
1315 $fields[$id] = array();
1316
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'] = '';
1323 continue;
1324 }
1325
1326 $field_name = $handler->ui_name(TRUE);
1327 if (!empty($field['relationship']) && !empty($relationships[$field['relationship']])) {
1328 $field_name = '(' . $relationships[$field['relationship']] . ') ' . $field_name;
1329 }
1330
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;
1335 }
1336 $fields[$id]['info'] = $handler->admin_summary();
1337
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));
1340 }
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));
1343 }
1344
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';
1349
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));
1352 }
1353
1354 $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')))));
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;
1358 }
1359 $fields[$pid]['info'] = '';
1360 }
1361
1362 if ($grouping) {
1363 $gid = $handler->options['group'];
1364
1365 // Show in default group if the group does not exist.
1366 if (empty($group_info['groups'][$gid])) {
1367 $gid = 0;
1368 }
1369 $groups[$gid][] = $id;
1370 }
1371 }
1372
1373 // If using grouping, re-order fields so that they show up properly in the list.
1374 if ($type == 'filter' && $grouping) {
1375 $store = $fields;
1376 $fields = array();
1377 foreach ($groups as $gid => $contents) {
1378 if (!empty($fields)) {
1379 $operator = ') ' . ($group_info['operator'] == 'OR' ? t('OR') : t('AND')) . ' (';
1380 }
1381 else {
1382 $operator = '(';
1383 }
1384 $fields[] = array(
1385 'class' => 'views-group-text',
1386 'title' => $operator,
1387 'info' => '',
1388 );
1389 $started = FALSE;
1390 foreach ($contents as $pid) {
1391 $operator = '&nbsp;&nbsp;';
1392 if ($started) {
1393 $operator .= ($group_info['groups'][$gid] == 'OR' ? t('OR') : t('AND')) . ' ';
1394 }
1395 $store[$pid]['title'] = $operator . $store[$pid]['title'];
1396 $started = TRUE;
1397 $fields[$pid] = $store[$pid];
1398 }
1399 }
1400 $fields[] = array(
1401 'class' => 'views-group-text',
1402 'title' => ')',
1403 'info' => '',
1404 );
1405 }
1406
1407 $vars['fields'] = $fields;
1408 $vars['item_help_icon'] = theme('advanced_help_topic', 'views', $type);
1409 }
1410
1411 /**
1412 * Regenerate the tabs for AJAX updates.
1413 */
1414 function views_ui_regenerate_tabs(&$view, $display_id = NULL, $object = NULL) {
1415 if (empty($display_id)) {
1416 $displays = array_keys($view->display);
1417 }
1418 elseif (!is_array($display_id)) {
1419 $displays = array($display_id);
1420 if ($display_id != 'default') {
1421 $displays[] = 'default';
1422 }
1423 }
1424 else {
1425 $displays = $display_id;
1426 }
1427
1428 if (!$view->set_display('default')) {
1429 views_ajax_render(t('Invalid display id found while regenerating tabs'));
1430 }
1431
1432 if (!is_object($object)) {
1433 $object = new stdClass();
1434 }
1435
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);
1441 }
1442
1443 if (!empty($view->changed)) {
1444 $object->changed = TRUE;
1445 }
1446
1447 views_ajax_render($object);
1448 }
1449
1450 /**
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.
1454 */
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>',
1459 );
1460
1461 if (empty($name)) {
1462 $name = t('Update');
1463 }
1464
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'),
1470 '#submit' => array(
1471 'views_ui_edit_display_form_override_update_section',
1472 'views_ui_standard_submit',
1473 'views_ui_edit_display_form_override_update',
1474 ),
1475 );
1476 }
1477
1478 if (empty($form_state['ok_button'])) {
1479 // but be sure submit button validates!
1480 $form['buttons']['submit'] = array(
1481 '#type' => 'submit',
1482 '#value' => $name,
1483 '#submit' => array('views_ui_standard_submit', $form_id . '_submit'),
1484 );
1485 }
1486
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(),
1493 );
1494
1495 if ($third) {
1496 if (empty($submit)) {
1497 $submit = 'third';
1498 }
1499 $third_submit = function_exists($form_id . '_' . $submit) ? $form_id . '_' . $submit : 'views_ui_standard_cancel';
1500
1501 $form['buttons'][$submit] = array(
1502 '#type' => 'submit',
1503 '#value' => $third,
1504 '#validate' => array(),
1505 '#submit' => array($third_submit),
1506 );
1507 }
1508
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'];
1513 }
1514 if (isset($form['#help_topic'])) {
1515 $form_state['help_topic'] = $form['#help_topic'];
1516 }
1517 if (isset($form['#help_module'])) {
1518 $form_state['help_module'] = $form['#help_module'];
1519 }
1520 if (isset($form['#url'])) {
1521 $form_state['url'] = $form['#url'];
1522 }
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']);
1526 }
1527 else {
1528 $form_state['js settings'] = $form['#js'];
1529 }
1530 }
1531 if (isset($form['#section'])) {
1532 $form_state['#section'] = $form['#section'];
1533 }
1534 // Finally, we never want these cached -- our object cache does that for us.
1535 $form['#no_cache'] = TRUE;
1536
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']);
1540 }
1541 views_add_css('views-admin');
1542 }
1543
1544 /**
1545 * Basic submit handler applicable to all 'standard' forms
1546 */
1547 function views_ui_standard_submit($form, &$form_state) {
1548 if (!empty($form['#section'])) {
1549 $form_state['view']->changed_sections[$form['#section']] = TRUE;
1550 }
1551 }
1552
1553 /**
1554 * Submit handler for cancel button
1555 */
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']);
1560 }
1561
1562 $form_state['redirect'] = 'admin/build/views/edit/' . $form_state['view']->name;
1563 }
1564
1565 // --------------------------------------------------------------------------
1566 // Various subforms for editing the pieces of a view.
1567
1568 function views_ui_ajax_forms($key = NULL) {
1569 $forms = array(
1570 'display' => array(
1571 'form_id' => 'views_ui_edit_display_form',
1572 'args' => array('section'),
1573 ),
1574 'remove-display' => array(
1575 'form_id' => 'views_ui_remove_display_form',
1576 'args' => array(),
1577 ),
1578 'config-type' => array(
1579 'form_id' => 'views_ui_config_type_form',
1580 'args' => array('type'),
1581 ),
1582 'rearrange' => array(
1583 'form_id' => 'views_ui_rearrange_form',
1584 'args' => array('type'),
1585 ),
1586 'rearrange-filter' => array(
1587 'form_id' => 'views_ui_rearrange_filter_form',
1588 'args' => array('type'),
1589 ),
1590 'add-item' => array(
1591 'form_id' => 'views_ui_add_item_form',
1592 'args' => array('type'),
1593 ),
1594 'config-item' => array(
1595 'form_id' => 'views_ui_config_item_form',
1596 'args' => array('type', 'id'),
1597 ),
1598 'config-item-group' => array(
1599 'form_id' => 'views_ui_config_item_group_form',
1600 'args' => array('type', 'id'),
1601 ),
1602 'config-item-extra' => array(
1603 'form_id' => 'views_ui_config_item_extra_form',
1604 'args' => array('type', 'id'),
1605 ),
1606 'change-style' => array(
1607 'form_id' => 'views_ui_change_style_form',
1608 'args' => array('type', 'id'),
1609 ),
1610 'config-style' => array(
1611 'form_id' => 'views_ui_config_style_form',
1612 'args' => array('type', 'id'),
1613 ),
1614 );
1615
1616 if ($key) {
1617 return !empty($forms[$key]) ? $forms[$key] : NULL;
1618 }
1619
1620 return $forms;
1621 }
1622
1623 /**
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.
1627 */
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));
1633
1634 foreach ($form['args'] as $id) {
1635 $arg = (!empty($args)) ? array_shift($args) : NULL;
1636 $identifier .= '-' . $arg;
1637 }
1638 return $identifier;
1639 }
1640
1641 /**
1642 * Build up a $form_state object suitable for use with drupal_build_form
1643 * based on known information about a form.
1644 */
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(
1649 'form_key' => $key,
1650 'form_id' => $form['form_id'],
1651 'view' => &$view,
1652 'ajax' => $js,
1653 'display_id' => $display_id,
1654 'no_redirect' => TRUE,
1655 );
1656
1657 foreach ($form['args'] as $id) {
1658 $form_state[$id] = (!empty($args)) ? array_shift($args) : NULL;
1659 }
1660
1661 return $form_state;
1662 }
1663
1664 /**
1665 * Create the URL for one of our standard AJAX forms based upon known
1666 * information about the form.
1667 */
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];
1675 }
1676 return $url;
1677 }
1678
1679 /**
1680 * Add another form to the stack; clicking 'update' will go to this form
1681 * rather than closing the ajax pad.
1682 */
1683 function views_ui_add_form_to_stack($key, &$view, $display_id, $args, $top = FALSE) {
1684 if (empty($view->stack)) {
1685 $view->stack = array();
1686 }
1687
1688 $stack = array(views_ui_build_identifier($key, $view, $display_id, $args), $key, &$view, $display_id, $args);
1689 if ($top) {
1690 array_unshift($view->stack, $stack);
1691 }
1692 else {
1693 $view->stack[] = $stack;
1694 }
1695 }
1696
1697 /**
1698 * Generic entry point to handle forms.
1699 *
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.
1704 */
1705 function views_ui_ajax_form($js, $key, $view, $display_id) {
1706 $form = views_ui_ajax_forms($key);
1707 if (empty($form)) {
1708 return drupal_not_found();
1709 }
1710
1711 views_include('ajax');
1712 $args = func_get_args();
1713 // Remove the known args
1714 array_splice($args, 0, 4);
1715
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
1719 // now irrelevant.
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();
1725 }
1726 }
1727
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);
1733 }
1734
1735 $output = views_ajax_form_wrapper($form_state['form_id'], $form_state);
1736
1737 if (!$output) {
1738 // Sometimes we need to re-generate the form for multi-step type operations.
1739 $object = NULL;
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.
1749 if (!$js) {
1750 return drupal_goto(views_ui_build_form_url($form_state));
1751 }
1752 $object = views_ajax_form_wrapper($form_state['form_id'], $form_state);
1753 $object->url = url(views_ui_build_form_url($form_state));
1754 }
1755 else if (!$js) {
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");
1758 }
1759 // regenerate all tabs because changes to the default tab could ripple.
1760 return views_ui_regenerate_tabs($view, NULL, $object);
1761 }
1762
1763 return ($js) ? views_ajax_render($output) : $output;
1764 }
1765
1766 /**
1767 * AJAX callback to add a display.
1768 */
1769 function views_ui_add_display($js, $view) {
1770 views_include('ajax');
1771 $form_state = array(
1772 'view' => &$view,
1773 'ajax' => $js,
1774 );
1775
1776 $output = views_ajax_form_wrapper('views_ui_add_display_form', $form_state);
1777
1778 if ($js) {
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'];
1782
1783 // Make sure the new display is active
1784 if (!$view->set_display('default')) {
1785 views_ajax_render(t('Unable to initialize default display'));
1786 }
1787
1788 // Render the new display
1789 list($title, $body) = views_ui_display_tab($view, $view->display[$id]);
1790
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));
1794 }
1795 // Render the command object. This automatically exits.
1796 views_ajax_render($output);
1797 }
1798
1799 // But the non-js variant will return output if it didn't redirect us.
1800 return $output;
1801 }
1802
1803 /**
1804 * Form to add a display to a view.
1805 */
1806 function views_ui_add_display_form(&$form_state) {
1807 $view = &$form_state['view'];
1808
1809 $form['display']['display'] = array(
1810 '#type' => 'select',
1811 '#options' => views_fetch_plugin_names('display'),
1812 '#default_value' => 'page',
1813 );
1814
1815 $form['display']['add_display'] = array(
1816 '#type' => 'submit',
1817 '#value' => t('Add display'),
1818 '#submit' => array('views_ui_add_display_form_submit'),
1819 );
1820
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");
1824
1825 return $form;
1826 }
1827
1828 /**
1829 * Submit handler to add a display to a view.
1830 */
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);
1835
1836 // Store in cache
1837 views_ui_cache_set($form_state['view']);
1838
1839 // Send it back
1840 $form_state['redirect'] = array('admin/build/views/edit/' . $form_state['view']->name, NULL, 'views-tab-' . $form_state['id']);
1841 }
1842
1843 /**
1844 * AJAX callback to add a display.
1845 */
1846 function views_ui_clone_display($js, $view, $id) {
1847 views_include('ajax');
1848 $form_state = array(
1849 'view' => &$view,
1850 'ajax' => $js,
1851 'display_id' => $id,
1852 );
1853
1854 $output = views_ajax_form_wrapper('views_ui_clone_display_form', $form_state);
1855
1856 if ($js) {
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'];
1860
1861 // Make sure the new display is active
1862 if (!$view->set_display('default')) {
1863 views_ajax_render(t('Unable to initialize default display'));
1864 }
1865
1866 // Render the new display
1867 list($title, $body) = views_ui_display_tab($view, $view->display[$id]);
1868
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));
1872 }
1873 // Render the command object. This automatically exits.
1874 views_ajax_render($output);
1875 }
1876
1877 // But the non-js variant will return output if it didn't redirect us.
1878 return $output;
1879 }
1880
1881 /**
1882 * From to clone a display from a view.
1883 */
1884 function views_ui_clone_display_form(&$form_state) {
1885 $view = &$form_state['view'];
1886 $display_id = $form_state['display_id'];
1887
1888 $form['clone_display'] = array(
1889 '#type' => 'submit',
1890 '#value' => t('Clone display'),
1891 '#submit' => array('views_ui_clone_display_form_submit'),
1892 );
1893
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');
1897
1898 return $form;
1899 }
1900
1901 /**
1902 * Submit handler to add a clone to a display from a view.
1903 */
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];
1908
1909 $new_id = $form_state['view']->add_display($display->display_plugin);
1910 $form_state['id'] = $new_id;
1911
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;
1915
1916 // Store in cache
1917 views_ui_cache_set($form_state['view']);
1918
1919 // Send it back
1920 $form_state['redirect'] = array('admin/build/views/edit/' . $form_state['view']->name, NULL, 'views-tab-' . $new_id);
1921 }
1922
1923 /**
1924 * Form to remove a display from a view.
1925 */
1926 function views_ui_remove_display_form(&$form_state) {
1927 $view = &$form_state['view'];
1928 $display_id = $form_state['display_id'];
1929
1930 if (empty($view->display[$display_id]->deleted)) {
1931 $form['display'] = array(
1932 '#prefix' => '<div class="display-button remove-display">',
1933 '#suffix' => '</div>',
1934 );
1935 $form['remove_display'] = array(
1936 '#type' => 'submit',
1937 '#value' => t('Remove display'),
1938 '#submit' => array('views_ui_remove_display_form_submit'),
1939 );
1940 }
1941 else {
1942 $form['display'] = array(
1943 '#prefix' => '<div class="display-button restore-display">',
1944 '#suffix' => '</div>',
1945 );
1946 $form['restore_display'] = array(
1947 '#type' => 'submit',
1948 '#value' => t('Restore display'),
1949 '#submit' => array('views_ui_remove_display_form_restore'),
1950 );
1951 }
1952 $form['#action'] = url("admin/build/views/nojs/remove-display/$view->name/$display_id");
1953 $form['#attributes'] = array('class' => 'views-ajax-form');
1954
1955 return $form;
1956 }
1957
1958 /**
1959 * Submit handler to add a remove to a display from a view.
1960 */
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;
1967
1968 // Store in cache
1969 views_ui_cache_set($form_state['view']);
1970 }
1971 }
1972
1973 /**
1974 * Submit handler to add a restore a removed display to a view.
1975 */
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;
1980
1981 // Store in cache
1982 views_ui_cache_set($form_state['view']);
1983 }
1984
1985 /**
1986 * Page callback to display analysis information on a view.
1987 */
1988 function views_ui_analyze_view($js, $view) {
1989 views_include('ajax');
1990 $form_state = array(
1991 'view' => &$view,
1992 'ajax' => $js,
1993 );
1994
1995 $output = views_ajax_form_wrapper('views_ui_analyze_view_form', $form_state);
1996
1997 if ($js) {
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);
2001 }
2002 return views_ajax_render($output);
2003
2004 }
2005 return $output;
2006 }
2007
2008 /**
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.
2011 *
2012 * It has no submit or anything, as we will never actually submit this form
2013 * where the form is placed.
2014 */
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'),
2021 );
2022
2023 return $form;
2024 }
2025
2026 /**
2027 * Form constructor callback to display analysis information on a view
2028 */
2029 function views_ui_analyze_view_form(&$form_state) {
2030 $view = &$form_state['view'];
2031
2032 $form['#title'] = t('View analysis');
2033 $form['#section'] = 'analyze';
2034
2035 views_include('analyze');
2036 $messages = views_analyze_view($view);
2037
2038 $form['analysis'] = array(
2039 '#prefix' => '<div class="form-item">',
2040 '#suffix' => '</div>',
2041 '#value' => views_analyze_format_result($view, $messages),
2042 );
2043
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');
2047 return $form;
2048 }
2049
2050 /**
2051 * Submit handler for views_ui_analyze_view_form
2052 */
2053 function views_ui_analyze_view_form_submit($form, &$form_state) {
2054 $form_state['redirect'] = 'admin/build/views/edit/' . $form_state['view']->name;
2055 }
2056
2057 /**
2058 * Page callback to display analysis information on a view.
2059 */
2060 function views_ui_reorder_view($js, $view) {
2061 views_include('ajax');
2062 $form_state = array(
2063 'view' => &$view,
2064 'ajax' => $js,
2065 );
2066
2067 $output = views_ajax_form_wrapper('views_ui_reorder_displays_form', $form_state);
2068
2069 if ($js) {
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);
2080 }
2081 return views_ajax_render($output);
2082 }
2083
2084 return $output;
2085 }
2086
2087 /**
2088 * Form constructor callback to reorder displays on a view
2089 */
2090 function views_ui_reorder_displays_form(&$form_state) {
2091 $view = &$form_state['view'];
2092
2093 $form['view'] = array('#type' => 'value', '#value' => $view);
2094
2095 $form['#tree'] = TRUE;
2096
2097 $last_display = end($view->display);
2098
2099 foreach ($view->display as $display) {
2100 $form[$display->id] = array(
2101 'title' => array('#value' => $display->display_title),
2102 'weight' => array(
2103 '#type' => 'weight',
2104 '#value' => $display->position,
2105 '#delta' => $last_display->position,
2106 ),
2107 '#tree' => TRUE,
2108 '#display' => $display,
2109 'removed' => array(
2110 '#type' => 'checkbox',
2111 '#id' => 'display-removed-' . $display->id,
2112 '#attributes' => array('class' => 'views-remove-checkbox'),
2113 '#default_value' => isset($display->deleted),
2114 ),
2115 );
2116
2117 if (isset($display->deleted) && $display->deleted) {
2118 $form[$display->id]['deleted'] = array('#type' => 'value', '#value' => TRUE);
2119 }
2120 if ($display->id === 'default') {
2121 unset($form[$display->id]['weight']);
2122 unset($form[$display->id]['removed']);
2123 }
2124
2125 }
2126
2127 $form['#title'] = t('Displays Reorder');
2128 $form['#section'] = 'reorder';
2129
2130 // Add javascript settings that will be added via $.extend for tabledragging
2131 $form['#js']['tableDrag']['reorder-displays']['weight'][0] = array(
2132 'target' => 'weight',
2133 'source' => NULL,
2134 'relationship' => 'sibling',
2135 'action' => 'order',
2136 'hidden' => TRUE,
2137 'limit' => 0,
2138 );
2139
2140
2141 $form['#action'] = url('admin/build/views/nojs/reorder-display/'. $view->name);
2142
2143 views_ui_standard_form_buttons($form, $form_state, 'views_ui_reorder_displays_form');
2144
2145 return $form;
2146 }
2147
2148
2149 /**
2150 * Display position sorting function
2151 */
2152 function _views_position_sort($display1, $display2) {
2153 if ($display1->position != $display2->position) {
2154 return $display1->position < $display2->position ? -1 : 1;
2155 }
2156
2157 return 0;
2158 }
2159
2160 /**
2161 * Submit handler for rearranging display form
2162 */
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'];
2169 }
2170 }
2171
2172 // Sort the order array
2173 asort($order);
2174
2175 // Fixing up positions
2176 $position = 2;
2177
2178 foreach(array_keys($order) as $display) {
2179 $order[$display] = $position++;
2180 }
2181
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') {
2187 continue;
2188 }
2189 if (isset($order[$display_id])) {
2190 $form_state['view']->display[$display_id]->position = $order[$display_id];
2191 }
2192 else {
2193 $form_state['view']->display[$display_id]->deleted = TRUE;
2194 }
2195 }
2196
2197 // Sorting back the display array as the position is not enough
2198 uasort($form_state['view']->display, '_views_position_sort');
2199
2200 // Store in cache
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');
2203 }
2204
2205 /**
2206 * Turn the reorder form into a proper table
2207 */
2208 function theme_views_ui_reorder_displays_form($form) {
2209 $rows = array();
2210 foreach (element_children($form) as $key) {
2211 if (isset($form[$key]['#display'])) {
2212 $display = &$form[$key];
2213
2214 $row = array();
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()',
2222 array(
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')),
2228 'html' => TRUE));
2229 }
2230 else {
2231 $row[] = '';
2232 }
2233 $class = array();
2234 $styles = array();
2235 if (isset($form[$key]['weight']['#type'])) {
2236 $class[] = 'draggable';
2237 }
2238 if (isset($form[$key]['deleted']['#value']) && $form[$key]['deleted']['#value']) {
2239 $styles[] = 'display: none;';
2240 }
2241 $rows[] = array('data' => $row, 'class' => implode(' ', $class), 'id' => 'display-row-' . $key, 'style' => implode(' ', $styles));
2242 }
2243 }
2244
2245 $header = array(t('Display'), t('Weight'), t('Remove'));
2246 $output = '';
2247 drupal_add_tabledrag('reorder-displays', 'order', 'sibling', 'weight');
2248
2249 $output = drupal_render($form['override']);
2250 $output .= theme('table', $header, $rows, array('id' => 'reorder-displays'));
2251 $output .= drupal_render($form);
2252
2253 return $output;
2254 }
2255
2256 /**
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.
2259 *
2260 * It has no submit or anything, as we will never actually submit this form
2261 * where the form is placed.
2262 */
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'),
2269 );
2270
2271 return $form;
2272 }
2273
2274 /**
2275 * Page callback to edit details of a view.
2276 */
2277 function views_ui_edit_details($property, $js, $view) {
2278 views_include('ajax');
2279 $form_state = array(
2280 'view' => &$view,
2281 'ajax' => $js,
2282 'property' => $property,
2283 );
2284
2285 $output = views_ajax_form_wrapper('views_ui_edit_details_form', $form_state);
2286
2287 if ($js) {
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);
2291 }
2292 return views_ajax_render($output);
2293
2294 }
2295 return $output;
2296 }
2297
2298 /**
2299 * Form constructor callback to edit details of a view
2300 */
2301 function views_ui_edit_details_form(&$form_state) {
2302 $view = &$form_state['view'];
2303
2304 $form['#title'] = t('View details');
2305 $form['#section'] = 'details';
2306
2307 switch ($form_state['property']) {
2308 case 'human_name':
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,
2314 );
2315 break;
2316
2317 case 'tag':
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',
2324 );
2325 break;
2326
2327 case 'description':
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,
2333 );
2334 break;
2335 }
2336
2337 views_ui_standard_form_buttons($form, $form_state, 'views_ui_edit_details_form');
2338 return $form;
2339 }
2340
2341 /**
2342 * Submit handler for views_ui_edit_details_form
2343 */
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;
2348 }
2349
2350 /**
2351 * Form constructor callback to edit display of a view
2352 */
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'];
2357
2358 if (!$view->set_display($display_id)) {
2359 views_ajax_render(t('Invalid display id @display', array('@display' => $display_id)));
2360 }
2361 $display = &$view->display[$display_id];
2362
2363 // Get form from the handler.
2364 $display->handler->options_form($form, $form_state);
2365 $name = NULL;
2366 if (isset($form_state['update_name'])) {
2367 $name = $form_state['update_name'];
2368 }
2369
2370 views_ui_standard_form_buttons($form, $form_state, 'views_ui_edit_display_form', $name);
2371 return $form;
2372 }
2373
2374 /**
2375 * Validate handler for views_ui_edit_display_form
2376 */
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);
2380 }
2381
2382 /**
2383 * Submit handler for views_ui_edit_display_form
2384 */
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);
2388
2389 views_ui_cache_set($form_state['view']);
2390 }
2391
2392 /**
2393 * Override handler for views_ui_edit_display_form
2394 */
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);
2398
2399 views_ui_cache_set($form_state['view']);
2400 $form_state['rerender'] = TRUE;
2401 $form_state['rebuild'] = TRUE;
2402 }
2403 /**
2404 * Override handler and submit views_ui_edit_display_form
2405 */
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']);
2411 }
2412
2413 /**
2414 * Override handler and submit views_ui_edit_display_form
2415 */
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']);
2419 }
2420
2421 /**
2422 * Form to config items in the views UI.
2423 */
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'];
2428
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)));
2432 }
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';
2437
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']);
2441 }
2442
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);
2447 }
2448
2449 $name = NULL;
2450 if (isset($form_state['update_name'])) {
2451 $name = $form_state['update_name'];
2452 }
2453
2454 views_ui_standard_form_buttons($form, $form_state, 'views_ui_config_type_form', $name);
2455 return $form;
2456 }
2457
2458 /**
2459 * Submit handler for type configuration form
2460 */
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']];
2464
2465 // Store in cache
2466 views_ui_cache_set($form_state['view']);
2467 }
2468
2469 /**
2470 * Configure settings particular to filters.
2471 */
2472 function views_ui_config_filters_form(&$form, &$form_state) {
2473
2474 }
2475
2476 /**
2477 * Form to rearrange items in the views UI.
2478 */
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'];
2483
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)));
2487 }
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';
2492
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']);
2496 }
2497
2498 $count = 0;
2499
2500 // Get relationship labels
2501 $relationships = array();
2502 foreach ($display->handler->get_handlers('relationship') as $id => $handler) {
2503 $relationships[$id] = $handler->label();
2504 }
2505
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,
2511 );
2512 $handler = $display->handler->get_handler($type, $id);
2513 if ($handler) {
2514 $name = $handler->ui_name() . ' ' . $handler->admin_summary();
2515 if (!empty($field['relationship']) && !empty($relationships[$field['relationship']])) {
2516 $name = '(' . $relationships[$field['relationship']] . ') ' . $name;
2517 }
2518
2519 $form['fields'][$id]['name'] = array(
2520 '#value' => $name,
2521 );
2522 }
2523 else {
2524 $form['fields'][$id]['name'] = array('#value' => t('Broken field @id', array('@id' => $id)));
2525 }
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,
2531 );
2532 }
2533
2534 // Add javascript settings that will be added via $.extend for tabledragging
2535 $form['#js']['tableDrag']['arrange']['weight'][0] = array(
2536 'target' => 'weight',
2537 'source' => NULL,
2538 'relationship' => 'sibling',
2539 'action' => 'order',
2540 'hidden' => TRUE,
2541 'limit' => 0,
2542 );
2543
2544 $name = NULL;
2545 if (isset($form_state['update_name'])) {
2546 $name = $form_state['update_name'];
2547 }
2548
2549 views_ui_standard_form_buttons($form, $form_state, 'views_ui_rearrange_form');
2550 return $form;
2551 }
2552
2553 /**
2554 * Turn the rearrange form into a proper table
2555 */
2556 function theme_views_ui_rearrange_form($form) {
2557 $rows = array();
2558 foreach (element_children($form['fields']) as $id) {
2559 if (isset($form['fields'][$id]['name'])) {
2560 $row = array();
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));
2565
2566 $rows[] = array('data' => $row, 'class' => 'draggable', 'id' => 'views-row-' . $id);
2567 }
2568 }
2569 if (empty($rows)) {
2570 $rows[] = array(array('data' => t('No fields available.'), 'colspan' => '2'));
2571 }
2572
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);
2578 return $output;
2579
2580 }
2581
2582 /**
2583 * Submit handler for rearranging form
2584 */
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']];
2588
2589 $old_fields = $display->handler->get_option($types[$form_state['type']]['plural']);
2590 $new_fields = $order = array();
2591
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'];
2598 }
2599 }
2600
2601 // Sort the array
2602 asort($order);
2603
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];
2607 }
2608 $display->handler->set_option($types[$form_state['type']]['plural'], $new_fields);
2609
2610 // Store in cache
2611 views_ui_cache_set($form_state['view']);
2612 }
2613
2614 /**
2615 * Form to rearrange items in the views UI.
2616 */
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'];
2621
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)));
2625 }
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';
2630
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']);
2634 }
2635
2636 if (!empty($view->form_cache)) {
2637 $groups = $view->form_cache['groups'];
2638 $handlers = $view->form_cache['handlers'];
2639 }
2640 else {
2641 $groups = $display->handler->get_option('filter_groups');
2642 $handlers = $display->handler->get_option($types[$type]['plural']);
2643 }
2644 $count = 0;
2645
2646 // Get relationship labels
2647 $relationships = array();
2648 foreach ($display->handler->get_handlers('relationship') as $id => $handler) {
2649 $relationships[$id] = $handler->label();
2650 }
2651
2652 $group_options = array();
2653
2654 /**
2655 * Filter groups is an array that contains:
2656 * array(
2657 * 'operator' => 'and' || 'or',
2658 * 'groups' => array(
2659 * $group_id => 'and' || 'or',
2660 * ),
2661 * );
2662 */
2663
2664 $grouping = count(array_keys($groups['groups'])) > 1;
2665
2666 $form['filter_groups']['#tree'] = TRUE;
2667 $form['filter_groups']['operator'] = array(
2668 '#type' => 'select',
2669 '#options' => array (
2670 'AND' => t('And'),
2671 'OR' => t('Or'),
2672 ),
2673 '#default_value' => $groups['operator'],
2674 '#attributes' => array(
2675 'class' => 'warning-on-change',
2676 ),
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,
2680 );
2681
2682 $form['remove_groups']['#tree'] = TRUE;
2683
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(
2689 'AND' => t('And'),
2690 'OR' => t('Or'),
2691 ),
2692 '#default_value' => $group,
2693 '#attributes' => array(
2694 'class' => 'warning-on-change',
2695 ),
2696 );
2697
2698 $form['remove_groups'][$id] = array(); // to prevent a notice
2699 if ($id != 0) {
2700 $form['remove_groups'][$id] = array(
2701 '#type' => 'submit',
2702 '#value' => t('Remove group @group', array('@group' => $id)),
2703 '#group' => $id,
2704 );
2705 }
2706 $group_options[$id] = $id == 0 ? t('Default group') : t('Group @group', array('@group' => $id));
2707 $form['#group_renders'][$id] = array();
2708 }
2709
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;
2719 }
2720
2721 $handler = $display->handler->get_handler($type, $id);
2722 if ($grouping && $handler && !$handler->can_group()) {
2723 $field['group'] = 'ungroupable';
2724 }
2725
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
2728 // own group:
2729 if (!$grouping && $field['group'] == 'ungroupable') {
2730 $field['group'] = 0;
2731 }
2732
2733 // Place this item into the proper group for rendering.
2734 $form['#group_renders'][$field['group']][] = $id;
2735
2736 $form['filters'][$id]['weight'] = array(
2737 '#type' => 'textfield',
2738 '#default_value' => ++$count,
2739 );
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,
2746 ),
2747 '#access' => $field['group'] !== 'ungroupable',
2748 );
2749
2750 if ($handler) {
2751 $name = $handler->ui_name() . ' ' . $handler->admin_summary();
2752 if (!empty($field['relationship']) && !empty($relationships[$field['relationship']])) {
2753 $name = '(' . $relationships[$field['relationship']] . ') ' . $name;
2754 }
2755
2756 $form['filters'][$id]['name'] = array(
2757 '#value' => $name,
2758 );
2759 }
2760 else {
2761 $form['filters'][$id]['name'] = array('#value' => t('Broken field @id', array('@id' => $id)));
2762 }
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,
2768 );
2769 }
2770
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',
2775 'source' => NULL,
2776 'relationship' => 'sibling',
2777 'action' => 'order',
2778 'hidden' => TRUE,
2779 'limit' => 0,
2780 );
2781
2782 $form['#js']['tableDrag']['ungroupable_arrange']['weight'][0] = array(
2783 'target' => 'weight',
2784 'source' => NULL,
2785 'relationship' => 'sibling',
2786 'action' => 'order',
2787 'hidden' => TRUE,
2788 'limit' => 0,
2789 );
2790
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',
2799 'hidden' => FALSE,
2800 'limit' => 0,
2801 );
2802 }
2803
2804 if (isset($form_state['update_name'])) {
2805 $name = $form_state['update_name'];
2806 }
2807
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',
2813 '#group' => 'add',
2814 );
2815
2816 return $form;
2817 }
2818
2819 /**
2820 * Turn the rearrange form into a proper table
2821 */
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;
2826
2827 foreach ($form['#group_renders'] as $group_id => $contents) {
2828 // Header row for the group.
2829 if ($grouping && $group_id !== 'ungroupable') {
2830 $row = array();
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:
2835 $row = array();
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);
2840 }
2841
2842 foreach ($contents as $id) {
2843 if (isset($form['filters'][$id]['name'])) {
2844 $row = array();
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));
2851
2852 $row = array('data' => $row, 'class' => 'draggable', 'id' => 'views-row-' . $id);
2853 if ($group_id !== 'ungroupable') {
2854 $rows[] = $row;
2855 }
2856 else {
2857 $ungroupable_rows[] = $row;
2858 }
2859 }
2860 }
2861 }
2862 if (empty($rows)) {
2863 $rows[] = array(array('data' => t('No fields available.'), 'colspan' => '2'));
2864 }
2865
2866 $output = drupal_render($form['override']);
2867 if ($grouping) {
2868 $output .= drupal_render($form['filter_groups']['operator']);
2869 }
2870 else {
2871 $form['filter_groups']['groups'][0]['#title'] = t('Operator');
2872 $output .= drupal_render($form['filter_groups']['groups'][0]);
2873 }
2874
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'));
2879 }
2880
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);
2885 return $output;
2886
2887 }
2888
2889 /**
2890 * Submit handler for rearranging form
2891 */
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();
2896
2897 if (!empty($form_state['view']->form_cache)) {
2898 $old_fields = $form_state['view']->form_cache['handlers'];
2899 }
2900 else {
2901 $old_fields = $display->handler->get_option($types[$form_state['type']]['plural']);
2902 }
2903 $count = 0;
2904
2905 $groups = $form_state['values']['filter_groups'];
2906 // Whatever button was clicked, re-calculate field information.
2907 $new_fields = $order = array();
2908
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'];
2916 }
2917
2918 if (isset($info['group'])) {
2919 $old_fields[$field]['group'] = $info['group'];
2920 $remember_groups[$info['group']][] = $field;
2921 }
2922 }
2923 }
2924
2925 // Sort the array
2926 asort($order);
2927
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];
2931 }
2932
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') {
2936 // Add a new group
2937 $groups['groups'][] = 'AND';
2938 }
2939 else {
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;
2949 }
2950 }
2951 }
2952 else {
2953 // If this is the last one, just unset it.
2954 unset($groups['groups'][$group_id]);
2955 }
2956 }
2957 }
2958 }
2959 // Update our cache with values so that cancel still works the way
2960 // people expect.
2961 $form_state['view']->form_cache = array(
2962 'key' => 'rearrange-filter',
2963 'groups' => $groups,
2964 'handlers' => $new_fields,
2965 );
2966
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']));
2969 }
2970 else {
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);
2976 }
2977 }
2978
2979 // Store in cache
2980 views_ui_cache_set($form_state['view']);
2981 }
2982
2983 /**
2984 * Form to add_item items in the views UI.
2985 */
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'];
2990
2991 if (!$view->set_display($display_id)) {
2992 views_ajax_render(t('Invalid display id @display', array('@display' => $display_id)));
2993 }
2994 $display = &$view->display[$display_id];
2995
2996 $types = views_object_types();
2997 $ltitle = $types[$type]['ltitle'];
2998
2999 if (!empty($types[$type]['type'])) {
3000 $type = $types[$type]['type'];
3001 }
3002
3003 $form['#title'] = check_plain($display->display_title) . ': ';
3004 $form['#title'] .= t('Add @type', array('@type' => $ltitle));
3005 $form['#section'] = $display_id . 'add-item';
3006
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());
3010
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'),
3018 );
3019
3020 $form['name'] = array(
3021 '#prefix' => '<div class="views-radio-box form-checkboxes">',
3022 '#suffix' => '</div>',
3023 '#tree' => TRUE,
3024 '#default_value' => 'all',
3025 );
3026
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;
3034 }
3035
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,
3044 );
3045 }
3046 $form['name'][$group . '_end'] = array('#type' => 'markup', '#value' => '</div>');
3047 }
3048
3049 $form['group']['#options'] = $groups;
3050 }
3051 else {
3052 $form['markup'] = array(
3053 '#value' => '<div class="form-item">' . t('There are no @types available to add.', array('@types' => $ltitle)) . '</div>',
3054 );
3055 }
3056 views_ui_standard_form_buttons($form, $form_state, 'views_ui_add_item_form', t('Add'));
3057
3058 return $form;
3059 }
3060
3061 /**
3062 * Submit handler for adding new item(s) to a view.
3063 */
3064 function views_ui_add_item_form_submit($form, &$form_state) {
3065 $type = $form_state['type'];
3066 $types = views_object_types();
3067
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);
3073
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));
3077 }
3078
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));
3083 }
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));
3086 }
3087 }
3088
3089 if (isset($form_state['view']->form_cache)) {
3090 unset($form_state['view']->form_cache);
3091 }
3092
3093 // Store in cache
3094 views_ui_cache_set($form_state['view']);
3095 }
3096