#1274300 by dereine, tobiasb: Validate that the exposed items per page contains the...
[project/views.git] / views_ui.module
CommitLineData
31dd0540 1<?php
31dd0540
TGGM
2/**
3 * @file views_ui.module
4 * Provide structure for the administrative interface to Views.
5 */
6
cd12e28f 7/**
2dd76fe0 8 * Implements hook_menu().
31dd0540
TGGM
9 */
10function views_ui_menu() {
11 $items = array();
12
22367374 13 //module_load_include('inc', 'views_ui', 'includes/admin');
a185aad8 14
bf6e7a4a
EM
15 // Drupal core loads the module file when uninstall a module. So views.module is not loaded.
16 // There is a core bug filled, remove it once the bug is fixed.
17 // @see http://drupal.org/node/1029606
18 if (!module_exists('views')) {
19 return;
20 }
21
62abf345 22 // Minor code reduction technique.
31dd0540
TGGM
23 $base = array(
24 'access callback' => 'user_access',
25 'access arguments' => array('administer views'),
26 'file' => 'includes/admin.inc',
27 );
28
62abf345 29 // Top-level Views module pages (not tied to a particular View).
62abf345 30 $items['admin/structure/views/add'] = array(
88b394ee 31 'title' => 'Add new view',
31dd0540 32 'page callback' => 'views_ui_add_page',
88b394ee 33 'type' => MENU_LOCAL_ACTION,
62abf345 34 ) + $base;
ced80ad3 35
ce174eb6
EM
36 // Top-level Views module pages (not tied to a particular View).
37 $items['admin/structure/views/add-template'] = array(
38 'title' => 'Add view from template',
39 'page callback' => 'views_ui_add_template_page',
40a9bf06 40 // Don't show a local action link if there aren't any templates.
41 'type' => views_get_all_templates() ? MENU_LOCAL_ACTION : MENU_VISIBLE_IN_BREADCRUMB,
ce174eb6
EM
42 ) + $base;
43
2b31a2d7 44 $items['admin/structure/views/import'] = array(
dce0d189 45 'title' => 'Import',
31dd0540
TGGM
46 'page callback' => 'drupal_get_form',
47 'page arguments' => array('views_ui_import_page'),
2b31a2d7 48 'access callback' => 'views_import_access',
88b394ee 49 'type' => MENU_LOCAL_ACTION,
2b31a2d7 50 ) + $base;
ced80ad3 51
aceb5217
KS
52 $items['admin/structure/views/settings'] = array(
53 'title' => 'Settings',
31dd0540 54 'page callback' => 'drupal_get_form',
f83ae55f 55 'page arguments' => array('views_ui_admin_settings_basic'),
62abf345 56 'type' => MENU_LOCAL_TASK,
57 ) + $base;
f83ae55f
EM
58 $items['admin/structure/views/settings/basic'] = array(
59 'title' => 'Basic',
60 'page arguments' => array('views_ui_admin_settings_basic'),
24192e23 61 'type' => MENU_DEFAULT_LOCAL_TASK,
aceb5217 62 ) + $base;
9b96c90e
KS
63 $items['admin/structure/views/settings/advanced'] = array(
64 'title' => 'Advanced',
65 'page arguments' => array('views_ui_admin_settings_advanced'),
31dd0540
TGGM
66 'type' => MENU_LOCAL_TASK,
67 'weight' => 1,
aceb5217 68 ) + $base;
62abf345 69
a185aad8 70 // The primary Edit View page. Secondary tabs for each Display are added in
e362b13e 71 // views_ui_menu_local_tasks_alter().
62abf345 72 $items['admin/structure/views/view/%views_ui_cache'] = array(
73 'title callback' => 'views_ui_edit_page_title',
74 'title arguments' => array(4),
31e25092
DW
75 'page callback' => 'views_ui_edit_page',
76 'page arguments' => array(4),
62abf345 77 ) + $base;
78 $items['admin/structure/views/view/%views_ui_cache/edit'] = array(
613984f1 79 'title' => 'Edit view',
62abf345 80 'type' => MENU_DEFAULT_LOCAL_TASK,
613984f1 81 'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
62abf345 82 'weight' => -10,
48336fba 83 'theme callback' => 'ajax_base_page_theme',
62abf345 84 ) + $base;
e362b13e 85 $items['admin/structure/views/view/%views_ui_cache/edit/%/ajax'] = array(
efae7a9e 86 'page callback' => 'views_ui_ajax_get_form',
e362b13e 87 'page arguments' => array('views_ui_edit_form', 4, 6),
efae7a9e 88 'delivery callback' => 'ajax_deliver',
89 'theme callback' => 'ajax_base_page_theme',
31dd0540 90 'type' => MENU_CALLBACK,
efae7a9e 91 ) + $base;
43fb73e0
DW
92 $items['admin/structure/views/view/%views_ui_cache/preview/%'] = array(
93 'page callback' => 'views_ui_build_preview',
94 'page arguments' => array(4, 6),
95 'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
96 'type' => MENU_VISIBLE_IN_BREADCRUMB,
97 ) + $base;
44ffdd18
EM
98 $items['admin/structure/views/view/%views_ui_cache/preview/%/ajax'] = array(
99 'page callback' => 'views_ui_build_preview',
100 'page arguments' => array(4, 6),
101 'delivery callback' => 'ajax_deliver',
102 'theme callback' => 'ajax_base_page_theme',
103 'type' => MENU_CALLBACK,
104 ) + $base;
a185aad8 105
a185aad8 106 // Additional pages for acting on a View.
31dd0540 107
62abf345 108 $items['admin/structure/views/view/%views_ui_cache/break-lock'] = array(
109 'title' => 'Break lock',
31dd0540
TGGM
110 'page callback' => 'drupal_get_form',
111 'page arguments' => array('views_ui_break_lock_confirm', 4),
87add604 112 'type' => MENU_VISIBLE_IN_BREADCRUMB,
62abf345 113 ) + $base;
31dd0540 114
36779205 115 // NoJS/AJAX callbacks that can use the default Views AJAX form system.
883f4d03
KS
116 $items['admin/structure/views/nojs/%/%views_ui_cache'] = array(
117 'page callback' => 'views_ui_ajax_form',
118 'page arguments' => array(FALSE, 4, 5),
119 'type' => MENU_CALLBACK,
120 ) + $base;
121 $items['admin/structure/views/ajax/%/%views_ui_cache'] = array(
122 'page callback' => 'views_ui_ajax_form',
123 'page arguments' => array(TRUE, 4, 5),
124 'delivery callback' => 'ajax_deliver',
125 'type' => MENU_CALLBACK,
126 ) + $base;
5ed42115 127
36779205 128 // NoJS/AJAX callbacks that require custom page callbacks.
8a3d76c3 129 $ajax_callbacks = array(
8a3d76c3
EM
130 'preview' => 'views_ui_preview',
131 );
8a3d76c3 132 foreach ($ajax_callbacks as $menu => $menu_callback) {
bca4c48c 133 $items['admin/structure/views/nojs/' . $menu . '/%views_ui_cache/%'] = array(
2dd76fe0 134 'page callback' => $menu_callback,
bca4c48c
BZ
135 'page arguments' => array(5, 6),
136 ) + $base;
137 $items['admin/structure/views/ajax/' . $menu . '/%views_ui_cache/%'] = array(
2dd76fe0 138 'page callback' => $menu_callback,
bca4c48c
BZ
139 'page arguments' => array(5, 6),
140 'delivery callback' => 'ajax_deliver',
141 ) + $base;
2dd76fe0 142 }
31dd0540 143
62abf345 144 // Autocomplete callback for tagging a View.
36779205 145 // Views module uses admin/views/... instead of admin/structure/views/... for
146 // autocomplete paths, so be consistent with that.
147 // @todo Change to admin/structure/views/... when the change can be made to
148 // Views module as well.
62abf345 149 $items['admin/views/ajax/autocomplete/tag'] = array(
31dd0540 150 'page callback' => 'views_ui_autocomplete_tag',
2dd76fe0 151 'type' => MENU_CALLBACK,
62abf345 152 ) + $base;
31dd0540 153
bdcab761
DW
154 // A page in the Reports section to show usage of fields in all views
155 $items['admin/reports/views-fields'] = array(
156 'title' => 'Fields used in views',
157 'description' => 'Overview of fields used in all views.',
158 'page callback' => 'views_ui_field_list',
159 ) + $base;
160
31dd0540
TGGM
161 return $items;
162}
163
cd12e28f 164/**
2dd76fe0 165 * Implements hook_theme().
31dd0540
TGGM
166 */
167function views_ui_theme() {
168 $path = drupal_get_path('module', 'views');
2dd76fe0 169 require_once DRUPAL_ROOT . "/$path/includes/admin.inc";
31dd0540
TGGM
170
171 return array(
172 // edit a view
6631a572 173 'views_ui_display_tab_setting' => array(
6c56994b 174 'variables' => array('description' => '', 'link' => '', 'settings_links' => array(), 'overridden' => FALSE, 'defaulted' => FALSE, 'description_separator' => TRUE, 'class' => array()),
6631a572 175 'template' => 'views-ui-display-tab-setting',
31dd0540
TGGM
176 'path' => "$path/theme",
177 ),
e0695ad0 178 'views_ui_display_tab_bucket' => array(
6631a572 179 'render element' => 'element',
e0695ad0 180 'template' => 'views-ui-display-tab-bucket',
31dd0540
TGGM
181 'path' => "$path/theme",
182 ),
183 'views_ui_edit_item' => array(
3dd4aaf0 184 'variables' => array('type' => NULL, 'view' => NULL, 'display' => NULL, 'no_fields' => FALSE),
31dd0540
TGGM
185 'template' => 'views-ui-edit-item',
186 'path' => "$path/theme",
187 ),
188 'views_ui_rearrange_form' => array(
3dd4aaf0 189 'render element' => 'form',
31dd0540 190 ),
dc4e7922
DW
191 'views_ui_rearrange_filter_form' => array(
192 'render element' => 'form',
193 'file' => 'includes/admin.inc',
194 ),
c9fc5940
BZ
195 'views_ui_expose_filter_form' => array(
196 'render element' => 'form',
197 'file' => 'includes/admin.inc',
198 ),
31dd0540
TGGM
199
200 // list views
e28cdb45 201 'views_ui_view_info' => array(
ced80ad3 202 'variables' => array('view' => NULL, 'base' => NULL),
e28cdb45 203 'file' => "includes/admin.inc",
31dd0540 204 ),
31dd0540
TGGM
205
206 // tab themes
207 'views_tabset' => array(
3dd4aaf0 208 'variables' => array('tabs' => NULL),
31dd0540
TGGM
209 ),
210 'views_tab' => array(
3dd4aaf0 211 'variables' => array('body' => NULL),
31dd0540 212 ),
758f9361
DW
213 'views_ui_reorder_displays_form' => array(
214 'render element' => 'form',
215 'file' => 'includes/admin.inc',
216 ),
217
31dd0540
TGGM
218
219 // On behalf of a plugin
220 'views_ui_style_plugin_table' => array(
3dd4aaf0 221 'render element' => 'form',
31dd0540 222 ),
09cf411d 223
224 // When previewing a view.
225 'views_ui_view_preview_section' => array(
040fee74 226 'variables' => array('view' => NULL, 'section' => NULL, 'content' => NULL, 'links' => ''),
09cf411d 227 ),
99048386
KS
228
229 // Generic container wrapper, to use instead of theme_container when an id
230 // is not desired.
231 'views_container' => array(
232 'render element' => 'element',
94bfcb70 233 'file' => 'theme/theme.inc',
99048386 234 ),
31dd0540
TGGM
235 );
236}
237
238/**
bb9c7e69
DW
239 * Impements hook_custom_theme()
240 */
241function views_ui_custom_theme() {
242 $theme = variable_get('views_ui_custom_theme', '_default');
243
244 if ($theme != '_default') {
245 $available = list_themes();
246
247 if (isset($available[$theme]) && $available[$theme]->status && preg_match('/^admin\/structure\/views/', current_path())) {
248 return $theme;
249 }
250 }
251}
252
253/**
87add604
EM
254 * Page title callback for the Edit View page.
255 */
2f011518 256function views_ui_edit_page_title($view) {
87add604
EM
257 module_load_include('inc', 'views_ui', 'includes/admin');
258 $bases = views_fetch_base_tables();
259 $name = $view->get_human_name();
260 if (isset($bases[$view->base_table])) {
261 $name .= ' (' . $bases[$view->base_table]['title'] . ')';
262 }
263
264 return $name;
265}
266
267/**
e28cdb45
KS
268 * Specialized menu callback to load a view and check its locked status.
269 *
270 * @param $name
271 * The machine name of the view.
272 *
273 * @return
274 * The view object, with a "locked" property indicating whether or not
275 * someone else is already editing the view.
31dd0540
TGGM
276 */
277function views_ui_cache_load($name) {
f0bb22cd 278 ctools_include('object-cache');
31dd0540 279 views_include('view');
f0bb22cd 280 $view = ctools_object_cache_get('view', $name);
3c841b5c 281 $original_view = views_get_view($name);
31dd0540
TGGM
282
283 if (empty($view)) {
3c841b5c 284 $view = $original_view;
31dd0540
TGGM
285 if (!empty($view)) {
286 // Check to see if someone else is already editing this view.
f0bb22cd 287 $view->locked = ctools_object_cache_test('view', $view->name);
4b5c3a6a
DW
288 // Set a flag to indicate that this view is being edited.
289 // This flag will be used e.g. to determine whether strings
290 // should be localized.
291 $view->editing = TRUE;
31dd0540
TGGM
292 }
293 }
3c841b5c
EM
294 else {
295 // Keep disabled/enabled status real.
296 if ($original_view) {
9adb6f0d 297 $view->disabled = !empty($original_view->disabled);
3c841b5c
EM
298 }
299 }
31dd0540
TGGM
300
301 if (empty($view)) {
302 return FALSE;
303 }
304
305 else {
306 return $view;
307 }
308}
309
31dd0540
TGGM
310/**
311 * Specialized cache function to add a flag to our view, include an appropriate
312 * include, and cache more easily.
313 */
2f011518 314function views_ui_cache_set(&$view) {
31dd0540
TGGM
315 if (!empty($view->locked)) {
316 drupal_set_message(t('Changes cannot be made to a locked view.'), 'error');
317 return;
318 }
f0bb22cd 319 ctools_include('object-cache');
31dd0540
TGGM
320 $view->changed = TRUE; // let any future object know that this view has changed.
321
ce174eb6
EM
322 if (isset($view->current_display)) {
323 // Add the knowledge of the changed display, too.
324 $view->changed_display[$view->current_display] = TRUE;
325 unset($view->current_display);
326 }
5ed42115 327
31dd0540
TGGM
328 // Unset handlers; we don't want to write these into the cache
329 unset($view->display_handler);
31dd0540 330 unset($view->default_display);
a8f0683e 331 $view->query = NULL;
31dd0540
TGGM
332 foreach (array_keys($view->display) as $id) {
333 unset($view->display[$id]->handler);
334 unset($view->display[$id]->default_display);
335 }
f0bb22cd 336 ctools_object_cache_set('view', $view->name, $view);
31dd0540
TGGM
337}
338
339
340/**
341 * Specialized menu callback to load a view that is only a default
342 * view.
343 */
344function views_ui_default_load($name) {
345 $view = views_get_view($name);
346 if ($view->type == t('Default')) {
347 return $view;
348 }
349
350 return FALSE;
351}
352
353/**
09cf411d 354 * Theme preprocess for views-view.tpl.php.
355 */
356function views_ui_preprocess_views_view(&$vars) {
357 $view = $vars['view'];
24cbc9fb 358 if (!empty($view->views_ui_context) && module_exists('contextual')) {
09cf411d 359 $view->hide_admin_links = TRUE;
5a873017 360 foreach (array('title', 'header', 'exposed', 'rows', 'pager', 'more', 'footer', 'empty', 'attachment_after', 'attachment_before') as $section) {
09cf411d 361 if (!empty($vars[$section])) {
f37fd0dd
DW
362 $vars[$section] = array(
363 '#theme' => 'views_ui_view_preview_section',
364 '#view' => $view,
365 '#section' => $section,
61cc8b63 366 '#content' => is_array($vars[$section]) ? drupal_render($vars[$section]) : $vars[$section],
f37fd0dd
DW
367 '#theme_wrappers' => array('views_container'),
368 '#attributes' => array('class' => 'contextual-links-region'),
369 );
370 $vars[$section] = drupal_render($vars[$section]);
09cf411d 371 }
372 }
373 }
374}
375
376/**
377 * Theme preprocess for theme_views_ui_view_preview_section().
2dd76fe0 378 *
040fee74
DW
379 * @TODO
380 * Perhaps move this to includes/admin.inc or theme/theme.inc
31dd0540 381 */
09cf411d 382function template_preprocess_views_ui_view_preview_section(&$vars) {
383 switch ($vars['section']) {
5a873017
DW
384 case 'title':
385 $vars['title'] = t('Title');
386 $links = views_ui_view_preview_section_display_category_links($vars['view'], 'title', $vars['title']);
387 break;
09cf411d 388 case 'header':
389 $vars['title'] = t('Header');
040fee74
DW
390 $links = views_ui_view_preview_section_handler_links($vars['view'], $vars['section']);
391 break;
392 case 'empty':
393 $vars['title'] = t('No results behavior');
394 $links = views_ui_view_preview_section_handler_links($vars['view'], $vars['section']);
09cf411d 395 break;
396 case 'exposed':
397 // @todo Sorts can be exposed too, so we may need a better title.
398 $vars['title'] = t('Exposed Filters');
97f6eba4 399 $links = views_ui_view_preview_section_display_category_links($vars['view'], 'exposed_form_options', $vars['title']);
09cf411d 400 break;
401 case 'rows':
98d300a2
BZ
402 // @todo The title needs to depend on what is being viewed.
403 $vars['title'] = t('Content');
1eb00c3b 404 $links = views_ui_view_preview_section_rows_links($vars['view']);
09cf411d 405 break;
406 case 'pager':
407 $vars['title'] = t('Pager');
97f6eba4 408 $links = views_ui_view_preview_section_display_category_links($vars['view'], 'pager_options', $vars['title']);
09cf411d 409 break;
410 case 'more':
411 $vars['title'] = t('More');
7e201f9a 412 $links = views_ui_view_preview_section_display_category_links($vars['view'], 'use_more', $vars['title']);
09cf411d 413 break;
414 case 'footer':
415 $vars['title'] = t('Footer');
040fee74 416 $links = views_ui_view_preview_section_handler_links($vars['view'], $vars['section']);
09cf411d 417 break;
8ee02877
DW
418 case 'attachment_before':
419 // @todo: Add links to the attachment configuration page.
420 $vars['title'] = t('Attachment before');
421 break;
422 case 'attachment_after':
423 // @todo: Add links to the attachment configuration page.
424 $vars['title'] = t('Attachment after');
09cf411d 425 break;
426 }
3afefdca 427
e860b5f2 428 if (isset($links)) {
040fee74
DW
429 $build = array(
430 '#prefix' => '<div class="contextual-links-wrapper">',
431 '#suffix' => '</div>',
432 '#theme' => 'links__contextual',
433 '#links' => $links,
434 '#attributes' => array('class' => array('contextual-links')),
435 '#attached' => array(
6352223c 436 'library' => array(array('contextual', 'contextual-links')),
040fee74
DW
437 ),
438 );
439 $vars['links'] = drupal_render($build);
2dd76fe0 440 }
09cf411d 441 $vars['theme_hook_suggestions'][] = 'views_ui_view_preview_section__' . $vars['section'];
442}
443
444/**
445 * Returns the HTML for a section of a View being previewed within the Views UI.
446 */
447function theme_views_ui_view_preview_section($vars) {
18d9d056 448 return '<h1 class="section-title">' . $vars['title'] . '</h1>'
97e89d71 449 . $vars['links']
bbc6915f 450 . '<div class="preview-section">'. $vars['content'] . '</div>';
040fee74
DW
451}
452
453/**
454 * Returns contextual links for each handler of a certain section.
455 *
456 * @TODO
457 * Bring in relationships
458 * Refactor this function to use much stuff of views_ui_edit_form_get_bucket.
1eb00c3b
DW
459 *
460 * @param $title
461 * Add a bolded title of this section.
040fee74 462 */
2f011518 463function views_ui_view_preview_section_handler_links($view, $type, $title = FALSE) {
040fee74
DW
464 $display = $view->display_handler->display;
465 $handlers = $view->display_handler->get_handlers($type);
bf6426ed 466 $links = array();
040fee74 467
1eb00c3b
DW
468 $types = views_object_types();
469 if ($title) {
470 $links[$type . '-title'] = array(
471 'title' => $types[$type]['title'],
472 );
2dd76fe0 473 }
1eb00c3b 474
040fee74
DW
475 foreach ($handlers as $id => $handler) {
476 $field_name = $handler->ui_name(TRUE);
477 $links[$type . '-edit-' . $id] = array(
1eb00c3b 478 'title' => t('Edit @section', array('@section' => $field_name)),
040fee74 479 'href' => "admin/structure/views/nojs/config-item/$view->name/$display->id/$type/$id",
033d4e25 480 'attributes' => array('class' => array('views-ajax-link')),
040fee74
DW
481 );
482 }
1eb00c3b
DW
483 $links[$type . '-add'] = array(
484 'title' => t('Add new'),
485 'href' => "admin/structure/views/nojs/add-item/$view->name/$display->id/$type",
033d4e25 486 'attributes' => array('class' => array('views-ajax-link')),
1eb00c3b
DW
487 );
488
489 return $links;
09cf411d 490}
913b92b2 491
492/**
7e201f9a
DW
493 * Returns a link to editing a certain display setting.
494 */
2f011518 495function views_ui_view_preview_section_display_category_links($view, $type, $title) {
7e201f9a
DW
496 $display = $view->display_handler->display;
497 $links = array(
498 $type . '-edit' => array(
499 'title' => t('Edit @section', array('@section' => $title)),
500 'href' => "admin/structure/views/nojs/display/$view->name/$display->id/$type",
501 'attributes' => array('class' => array('views-ajax-link')),
502 ),
503 );
c9fc5940 504
7e201f9a
DW
505 return $links;
506}
507
508/**
1eb00c3b
DW
509 * Returns all contextual links for the main content part of the view.
510 */
2f011518 511function views_ui_view_preview_section_rows_links($view) {
1eb00c3b
DW
512 $display = $view->display_handler->display;
513 $links = array();
71c4265a
DW
514 $links = array_merge($links, views_ui_view_preview_section_handler_links($view, 'filter', TRUE));
515 $links = array_merge($links, views_ui_view_preview_section_handler_links($view, 'field', TRUE));
516 $links = array_merge($links, views_ui_view_preview_section_handler_links($view, 'sort', TRUE));
517 $links = array_merge($links, views_ui_view_preview_section_handler_links($view, 'argument', TRUE));
518 $links = array_merge($links, views_ui_view_preview_section_handler_links($view, 'relationship', TRUE));
040fee74
DW
519
520 return $links;
09cf411d 521}
913b92b2 522
040fee74 523
913b92b2 524/**
525 * Implments hook_ctools_plugin_directory().
526 *
527 * Views UI provides wizard plugins on behalf of core base tables.
528 */
529function views_ui_ctools_plugin_directory($module, $plugin) {
ced80ad3 530 if ($module == 'views_ui' || ($module == 'ctools' && $plugin == 'export_ui')) {
913b92b2 531 return 'plugins/' . $plugin;
532 }
533}
534
535/**
536 * Fetch metadata on a specific views ui wizard plugin.
537 *
d6bea670 538 * @param $wizard_type
539 * Name of a wizard, or name of a base table.
913b92b2 540 *
541 * @return
542 * An array with information about the requested wizard type.
543 */
d6bea670 544function views_ui_get_wizard($wizard_type) {
913b92b2 545 ctools_include('plugins');
a397d7b4 546 $wizard = ctools_get_plugins('views_ui', 'views_wizard', $wizard_type);
d6bea670 547 // @todo - handle this via an alter hook instead.
a397d7b4 548 if (!$wizard) {
d6bea670 549 // Must be a base table using the default wizard plugin.
550 $base_tables = views_fetch_base_tables();
551 if (!empty($base_tables[$wizard_type])) {
a397d7b4
DW
552 $wizard = views_ui_views_wizard_defaults();
553 $wizard['base_table'] = $wizard_type;
554 $wizard['title'] = $base_tables[$wizard_type]['title'];
3afefdca 555 }
f0c08154
DW
556 // The plugin is neither a base table nor an existing wizard.
557 else {
558 vpr('Views Wizard: @wizard does not exist. Be sure to implement hook_ctools_plugin_directory.', array('@wizard' => $wizard_type));
559 }
d6bea670 560 }
a397d7b4 561 return $wizard;
913b92b2 562}
563
564/**
565 * Fetch metadata for all content_type plugins.
566 *
567 * @return
568 * An array of arrays with information about all available views wizards.
569 */
570function views_ui_get_wizards() {
571 ctools_include('plugins');
d6bea670 572 $wizard_plugins = ctools_get_plugins('views_ui', 'views_wizard');
a50d6ba6 573 $wizard_tables = array();
574 foreach ($wizard_plugins as $name => $info) {
575 $wizard_tables[$info['base_table']] = TRUE;
576 }
d6bea670 577 $base_tables = views_fetch_base_tables();
578 $default_wizard = views_ui_views_wizard_defaults();
579 // Find base tables with no wizard.
580 // @todo - handle this via an alter hook for plugins?
581 foreach ($base_tables as $table => $info) {
a50d6ba6 582 if (!isset($wizard_tables[$table])) {
3116f109
DW
583 $wizard = $default_wizard;
584 $wizard['title'] = $info['title'];
585 $wizard['base_table'] = $table;
586 $wizard_plugins[$table] = $wizard;
2dd76fe0
EM
587 }
588 }
d6bea670 589 return $wizard_plugins;
590}
591
592/**
593 * Helper function to define the default values for a Views wizard plugin.
594 *
595 * @return
596 * An array of defaults for a views wizard.
597 */
598function views_ui_views_wizard_defaults() {
599 return array(
600 // The children may, for example, be a different variant for each node type.
601 'get children' => NULL,
602 'get child' => NULL,
603 // title and base table must be populated. They are empty here just
604 // so they are documented.
605 'title' => '',
606 'base_table' => NULL,
607 // This is a callback that takes the wizard as argument and returns
608 // an instantiazed Views UI form wizard object.
609 'get_instance' => 'views_ui_get_form_wizard_instance',
610 'form_wizard_class' => array(
611 'file' => 'views_ui_base_views_wizard',
612 'class' => 'ViewsUiBaseViewsWizard',
613 ),
614 );
913b92b2 615}
616
617/**
618 * Inform CTools that the Views wizard plugin can have child plugins.
619 */
620function views_ui_ctools_plugin_type() {
621 return array(
622 'views_wizard' => array(
623 'child plugins' => TRUE,
46160a0d 624 'classes' => array(
d6bea670 625 'form_wizard_class',
913b92b2 626 ),
d6bea670 627 'defaults' => views_ui_views_wizard_defaults(),
913b92b2 628 ),
629 );
630}
631
d6bea670 632function views_ui_get_form_wizard_instance($wizard) {
633 if (isset($wizard['form_wizard_class']['class'])) {
634 $class = $wizard['form_wizard_class']['class'];
635 return new $class($wizard);
636 }
2dd76fe0 637 else {
d6bea670 638 return new ViewsUiBaseViewsWizard($wizard);
639 }
46160a0d 640}
bfb5bc38
JB
641
642/**
613984f1
DR
643 * Implements hook_views_plugins_alter().
644 */
645function views_ui_views_plugins_alter(&$plugins) {
646 // Attach contextual links to each display plugin. The links will point to
647 // paths underneath "admin/structure/views/view/{$view->name}" (i.e., paths
648 // for editing and performing other contextual actions on the view).
649 foreach ($plugins['display'] as &$display) {
650 $display['contextual links']['views_ui'] = array(
651 'parent path' => 'admin/structure/views/view',
652 'argument properties' => array('name'),
653 );
31dd0540 654 }
613984f1 655}
0895a002 656
613984f1 657/**
d4993110 658 * Implements hook_contextual_links_view_alter().
bfb5bc38 659 */
d4993110 660function views_ui_contextual_links_view_alter(&$element, $items) {
613984f1
DR
661 // Remove contextual links from being rendered, when so desired, such as
662 // within a View preview.
d4993110 663 if (views_ui_contextual_links_suppress()) {
664 $element['#links'] = array();
0895a002 665 }
613984f1
DR
666 // Append the display ID to the Views UI edit links, so that clicking on the
667 // contextual link takes you directly to the correct display tab on the edit
668 // screen.
669 elseif (!empty($element['#links']['views-ui-edit']) && !empty($element['#element']['#views_contextual_links_info']['views_ui']['view_display_id'])) {
670 $display_id = $element['#element']['#views_contextual_links_info']['views_ui']['view_display_id'];
671 $element['#links']['views-ui-edit']['href'] .= '/' . $display_id;
672 }
b0dcf5d1 673}
efae7a9e 674
675/**
d4993110 676 * Sets a static variable for controlling whether contextual links are rendered.
677 *
678 * @see views_ui_contextual_links_view_alter()
679 */
680function views_ui_contextual_links_suppress($set = NULL) {
681 $suppress = &drupal_static(__FUNCTION__);
682 if (isset($set)) {
683 $suppress = $set;
0895a002 684 }
d4993110 685 return $suppress;
31dd0540 686}
d4993110 687
688/**
689 * Increments the views_ui_contextual_links_suppress() static variable.
690 *
691 * When this function is added to the #pre_render of an element, and
692 * 'views_ui_contextual_links_suppress_pop' is added to the #post_render of the
693 * same element, then all contextual links within the element and its
694 * descendants are suppressed from being rendered. This is used, for example,
695 * during a View preview, when it is not desired for nodes in the Views result
696 * to have contextual links.
697 *
698 * @see views_ui_contextual_links_suppress_pop()
699 */
700function views_ui_contextual_links_suppress_push() {
701 views_ui_contextual_links_suppress(((int) views_ui_contextual_links_suppress())+1);
702}
703
704/**
705 * Decrements the views_ui_contextual_links_suppress() static variable.
706 *
707 * @see views_ui_contextual_links_suppress_push()
708 */
709function views_ui_contextual_links_suppress_pop() {
710 views_ui_contextual_links_suppress(((int) views_ui_contextual_links_suppress())-1);
711}
712
713/**
7126c33a 714 * Menu callback; handles AJAX form submissions similar to ajax_form_callback(), but can be used for uncached forms.
efae7a9e 715 *
716 * ajax_form_callback(), the menu callback for the system/ajax path, requires
717 * the form to be retrievable from the form cache, because it lacks a trusted
7126c33a 718 * $form_id argument with which to call drupal_retrieve_form(). When AJAX is
efae7a9e 719 * wanted on a non-cacheable form, #ajax['path'] can be set to a path whose
720 * menu router item's 'page callback' is this function, and whose
721 * 'page arguments' is the form id, optionally followed by additional build
722 * arguments, as expected by drupal_get_form().
723 *
724 * The same caution must be used when defining a hook_menu() entry with this
725 * page callback as is used when defining a hook_menu() entry with the
726 * 'drupal_get_form' page callback: a 'page arguments' must be specified with a
727 * literal value as the first argument, because $form_id determines which form
728 * builder function gets called, so must be safe from user tampering.
729 *
730 * @see drupal_get_form()
731 * @see ajax_form_callback()
732 * @see http://drupal.org/node/774876
733 */
734function views_ui_ajax_get_form($form_id) {
735 // @see ajax_get_form()
736 $form_state = array(
737 'no_redirect' => TRUE,
738 );
739 $form_state['rebuild_info']['copy']['#build_id'] = TRUE;
740 $form_state['rebuild_info']['copy']['#action'] = TRUE;
741
742 // @see drupal_get_form()
743 $args = func_get_args();
744 array_shift($args);
745 $form_state['build_info']['args'] = $args;
746 $form = drupal_build_form($form_id, $form_state);
747
748 // @see ajax_form_callback()
749 if (!empty($form_state['triggering_element'])) {
750 $callback = $form_state['triggering_element']['#ajax']['callback'];
751 }
752 if (!empty($callback) && function_exists($callback)) {
753 return $callback($form, $form_state);
754 }
755}
ced80ad3
EM
756// @todo move these when we can
757
758
759/**
760 * Helper function to get a list of paths assigned to a view.
761 *
762 * @param $view
763 * The view.
764 *
765 * @return
766 * An array of links to this view's display paths.
767 */
2f011518 768function _views_ui_get_paths($view) {
ced80ad3
EM
769 $all_paths = array();
770 if (empty($view->display)) {
771 $all_paths[] = t('Edit this view to add a display.');
772 }
773 else {
774 $view->init_display(); // Make sure all the handlers are set up
775 foreach ($view->display as $display) {
776 if (!empty($display->handler) && $display->handler->has_path()) {
777 $one_path = $display->handler->get_option('path');
778 if (empty($path_sort)) {
779 $path_sort = strtolower($one_path);
780 }
781 if (empty($view->disabled) && strpos($one_path, '%') === FALSE) {
fbd4d0f6 782 $all_paths[] = l('/' . $one_path, $one_path);
ced80ad3
EM
783 }
784 else {
fbd4d0f6 785 $all_paths[] = check_plain('/' . $one_path);
ced80ad3
EM
786 }
787 }
788 }
789 }
790
791 return array_unique($all_paths);
792}
793
794/**
795 * Helper function to get a list of displays included in a view.
796 *
797 * @param $view
798 * The view.
799 *
800 * @return
801 * An array of display types that this view includes.
802 */
2f011518 803function _views_ui_get_displays_list($view) {
ced80ad3
EM
804 $displays = array();
805 foreach ($view->display as $display) {
806 if (!empty($display->handler->definition['admin'])) {
807 $displays[$display->handler->definition['admin']] = TRUE;
808 }
809 }
810
811 if ($displays) {
812 ksort($displays);
813 $displays = array_keys($displays);
814 }
815 return $displays;
816}
817
edb447fb
EM
818/**
819 * This is part of a patch to address a jQueryUI bug. The bug is responsible
820 * for the inability to scroll a page when a modal dialog is active. If the content
821 * of the dialog extends beyond the bottom of the viewport, the user is only able
822 * to scroll with a mousewheel or up/down keyboard keys.
823 *
824 * @see http://bugs.jqueryui.com/ticket/4671
825 * @see https://bugs.webkit.org/show_bug.cgi?id=19033
826 * @see /js/jquery.ui.dialog.patch.js
827 * @see /js/jquery.ui.dialog.min.js
828 *
829 * The javascript patch overwrites the $.ui.dialog.overlay.events object to remove
830 * the mousedown, mouseup and click events from the list of events that are bound
831 * in $.ui.dialog.overlay.create.
832 */
833
834function views_ui_library_alter(&$libraries, $module) {
835 if ($module == 'system' && isset($libraries['ui.dialog'])) {
836 if (version_compare($libraries['ui.dialog']['version'], '1.7.2', '>=')) {
86f2a668 837 $libraries['ui.dialog']['js'][drupal_get_path('module', 'views') . '/js/jquery.ui.dialog.patch.js'] = array();
edb447fb
EM
838 }
839 }
840}
c64a9ebb
EM
841
842/**
843 * Handle bad updates from alpha versions to beta versions.
844 *
845 * This function will ONLY be called if there is an old menu entry for
846 * this hanging around. This will force a menu rebuild and a cache clear
847 * which should resolve the problem.
848 */
849function views_ui_list_views() {
850 drupal_flush_all_caches();
851 menu_rebuild();
852 drupal_goto($_GET['q']);
853}
0be2cb32
EM
854
855/**
856 * Truncate strings to a set length and provide a ... if they truncated.
857 *
858 * This is often used in the UI to ensure long strings fit.
859 */
860function views_ui_truncate($string, $length) {
861 if (drupal_strlen($string) > $length) {
862 $string = drupal_substr($string, 0, $length);
863 $string .= '...';
864 }
865
866 return $string;
867}