Projet

Général

Profil

Paste
Télécharger (27,7 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / views / views_ui.module @ 651307cd

1
<?php
2

    
3
/**
4
 * @file
5
 * Provide structure for the administrative interface to Views.
6
 */
7

    
8
/**
9
 * Implements hook_menu().
10
 */
11
function views_ui_menu() {
12
  $items = array();
13

    
14
  // Minor code reduction technique.
15
  $base = array(
16
    'access callback' => 'user_access',
17
    'access arguments' => array('administer views'),
18
    'file' => 'includes/admin.inc',
19
  );
20

    
21
  // Top-level Views module pages (not tied to a particular View).
22
  $items['admin/structure/views/add'] = array(
23
    'title' => 'Add new view',
24
    'page callback' => 'views_ui_add_page',
25
    'type' => MENU_LOCAL_ACTION,
26
  ) + $base;
27

    
28
  // Top-level Views module pages (not tied to a particular View).
29
  $items['admin/structure/views/add-template'] = array(
30
    'title' => 'Add view from template',
31
    'page callback' => 'views_ui_add_template_page',
32
    // Don't show a local action link if there aren't any templates.
33
    'type' => views_get_all_templates() ? MENU_LOCAL_ACTION : MENU_VISIBLE_IN_BREADCRUMB,
34
  ) + $base;
35

    
36
  $items['admin/structure/views/import'] = array(
37
    'title' => 'Import',
38
    'page callback' => 'drupal_get_form',
39
    'page arguments' => array('views_ui_import_page'),
40
    'access callback' => 'views_import_access',
41
    'type' => MENU_LOCAL_ACTION,
42
  ) + $base;
43

    
44
  $items['admin/structure/views/settings'] = array(
45
    'title' => 'Settings',
46
    'page callback' => 'drupal_get_form',
47
    'page arguments' => array('views_ui_admin_settings_basic'),
48
    'type' => MENU_LOCAL_TASK,
49
  ) + $base;
50
  $items['admin/structure/views/settings/basic'] = array(
51
    'title' => 'Basic',
52
    'page arguments' => array('views_ui_admin_settings_basic'),
53
    'type' => MENU_DEFAULT_LOCAL_TASK,
54
  ) + $base;
55
  $items['admin/structure/views/settings/advanced'] = array(
56
    'title' => 'Advanced',
57
    'page arguments' => array('views_ui_admin_settings_advanced'),
58
    'type' => MENU_LOCAL_TASK,
59
    'weight' => 1,
60
  ) + $base;
61

    
62
  // The primary Edit View page. Secondary tabs for each Display are added in
63
  // views_ui_menu_local_tasks_alter().
64
  $items['admin/structure/views/view/%views_ui_cache'] = array(
65
    'title callback' => 'views_ui_edit_page_title',
66
    'title arguments' => array(4),
67
    'page callback' => 'views_ui_edit_page',
68
    'page arguments' => array(4),
69
  ) + $base;
70
  $items['admin/structure/views/view/%views_ui_cache/edit'] = array(
71
    'title' => 'Edit view',
72
    'type' => MENU_DEFAULT_LOCAL_TASK,
73
    'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
74
    'weight' => -10,
75
    'theme callback' => 'ajax_base_page_theme',
76
  ) + $base;
77
  $items['admin/structure/views/view/%views_ui_cache/edit/%/ajax'] = array(
78
    'page callback' => 'views_ui_ajax_get_form',
79
    'page arguments' => array('views_ui_edit_form', 4, 6),
80
    'delivery callback' => 'ajax_deliver',
81
    'theme callback' => 'ajax_base_page_theme',
82
    'type' => MENU_CALLBACK,
83
  ) + $base;
84
  $items['admin/structure/views/view/%views_ui_cache/preview/%'] = array(
85
    'page callback' => 'views_ui_build_preview',
86
    'page arguments' => array(4, 6),
87
    'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
88
    'type' => MENU_VISIBLE_IN_BREADCRUMB,
89
  ) + $base;
90
  $items['admin/structure/views/view/%views_ui_cache/preview/%/ajax'] = array(
91
    'page callback' => 'views_ui_build_preview',
92
    'page arguments' => array(4, 6),
93
    'delivery callback' => 'ajax_deliver',
94
    'theme callback' => 'ajax_base_page_theme',
95
    'type' => MENU_CALLBACK,
96
  ) + $base;
97

    
98
  // Additional pages for acting on a View.
99

    
100
  $items['admin/structure/views/view/%views_ui_cache/break-lock'] = array(
101
    'title' => 'Break lock',
102
    'page callback' => 'drupal_get_form',
103
    'page arguments' => array('views_ui_break_lock_confirm', 4),
104
    'type' => MENU_VISIBLE_IN_BREADCRUMB,
105
  ) + $base;
106

    
107
  // NoJS/AJAX callbacks that can use the default Views AJAX form system.
108
  $items['admin/structure/views/nojs/%/%views_ui_cache'] = array(
109
    'page callback' => 'views_ui_ajax_form',
110
    'page arguments' => array(FALSE, 4, 5),
111
    'type' => MENU_CALLBACK,
112
  ) + $base;
113
  $items['admin/structure/views/ajax/%/%views_ui_cache'] = array(
114
    'page callback' => 'views_ui_ajax_form',
115
    'page arguments' => array(TRUE, 4, 5),
116
    'delivery callback' => 'ajax_deliver',
117
    'type' => MENU_CALLBACK,
118
  ) + $base;
119

    
120
  // NoJS/AJAX callbacks that require custom page callbacks.
121
  $ajax_callbacks = array(
122
    'preview' => 'views_ui_preview',
123
  );
124
  foreach ($ajax_callbacks as $menu => $menu_callback) {
125
    $items['admin/structure/views/nojs/' . $menu . '/%views_ui_cache/%'] = array(
126
      'page callback' => $menu_callback,
127
      'page arguments' => array(5, 6),
128
    ) + $base;
129
    $items['admin/structure/views/ajax/' . $menu . '/%views_ui_cache/%'] = array(
130
      'page callback' => $menu_callback,
131
      'page arguments' => array(5, 6),
132
      'delivery callback' => 'ajax_deliver',
133
    ) + $base;
134
  }
135

    
136
  // Autocomplete callback for tagging a View.
137
  // Views module uses admin/views/... instead of admin/structure/views/... for
138
  // autocomplete paths, so be consistent with that.
139
  // @todo Change to admin/structure/views/... when the change can be made to
140
  //   Views module as well.
141
  $items['admin/views/ajax/autocomplete/tag'] = array(
142
    'page callback' => 'views_ui_autocomplete_tag',
143
    'type' => MENU_CALLBACK,
144
  ) + $base;
145

    
146
  // A page in the Reports section to show usage of fields in all views
147
  $items['admin/reports/fields/list'] = array(
148
    'title' => 'List',
149
    'type' => MENU_DEFAULT_LOCAL_TASK,
150
    'weight' => -10,
151
  );
152
  $items['admin/reports/fields/views-fields'] = array(
153
    'title' => 'Used in views',
154
    'description' => 'Overview of fields used in all views.',
155
    'page callback' => 'views_ui_field_list',
156
    'type' => MENU_LOCAL_TASK,
157
    'weight' => 0,
158
  ) + $base;
159

    
160
  // A page in the Reports section to show usage of plugins in all views.
161
  $items['admin/reports/views-plugins'] = array(
162
    'title' => 'Views plugins',
163
    'description' => 'Overview of plugins used in all views.',
164
    'page callback' => 'views_ui_plugin_list',
165
  ) + $base;
166

    
167
  return $items;
168
}
169

    
170
/**
171
 * Implements hook_theme().
172
 */
173
function views_ui_theme() {
174
  $path = drupal_get_path('module', 'views');
175
  require_once DRUPAL_ROOT . "/$path/includes/admin.inc";
176

    
177
  return array(
178
    // edit a view
179
    'views_ui_display_tab_setting' => array(
180
      'variables' => array('description' => '', 'link' => '', 'settings_links' => array(), 'overridden' => FALSE, 'defaulted' => FALSE, 'description_separator' => TRUE, 'class' => array()),
181
      'template' => 'views-ui-display-tab-setting',
182
      'path' => "$path/theme",
183
    ),
184
    'views_ui_display_tab_bucket' => array(
185
      'render element' => 'element',
186
      'template' => 'views-ui-display-tab-bucket',
187
      'path' => "$path/theme",
188
    ),
189
    'views_ui_rearrange_form' => array(
190
      'render element' => 'form',
191
    ),
192
    'views_ui_rearrange_filter_form' => array(
193
      'render element' => 'form',
194
      'file' => 'includes/admin.inc',
195
    ),
196
    'views_ui_expose_filter_form' => array(
197
      'render element' => 'form',
198
      'file' => 'includes/admin.inc',
199
    ),
200

    
201
    // list views
202
    'views_ui_view_info' => array(
203
      'variables' => array('view' => NULL, 'base' => NULL),
204
      'file' => "includes/admin.inc",
205
    ),
206

    
207
    // Group of filters.
208
    'views_ui_build_group_filter_form' => array(
209
      'render element' => 'form',
210
      'file' => 'includes/admin.inc',
211
    ),
212

    
213
    // tab themes
214
    'views_tabset' => array(
215
      'variables' => array('tabs' => NULL),
216
    ),
217
    'views_tab' => array(
218
      'variables' => array('body' => NULL),
219
    ),
220
    'views_ui_reorder_displays_form' => array(
221
      'render element' => 'form',
222
      'file' => 'includes/admin.inc',
223
    ),
224

    
225

    
226
    // On behalf of a plugin
227
    'views_ui_style_plugin_table' => array(
228
      'render element' => 'form',
229
    ),
230

    
231
    // When previewing a view.
232
    'views_ui_view_preview_section' => array(
233
      'variables' => array('view' => NULL, 'section' => NULL, 'content' => NULL, 'links' => ''),
234
    ),
235

    
236
    // Generic container wrapper, to use instead of theme_container when an id
237
    // is not desired.
238
    'views_container' => array(
239
      'render element' => 'element',
240
      'file' => 'theme/theme.inc',
241
    ),
242
  );
243
}
244

    
245
/**
246
 * Implements hook_custom_theme().
247
 */
248
function views_ui_custom_theme() {
249
  $theme = variable_get('views_ui_custom_theme', '_default');
250

    
251
  if ($theme != '_default') {
252
    $available = list_themes();
253

    
254
    if (isset($available[$theme]) && $available[$theme]->status && preg_match('/^admin\/structure\/views/', current_path())) {
255
      return $theme;
256
    }
257
  }
258
}
259

    
260
/**
261
 * Page title callback for the Edit View page.
262
 */
263
function views_ui_edit_page_title($view) {
264
  module_load_include('inc', 'views_ui', 'includes/admin');
265
  $bases = views_fetch_base_tables();
266
  $name = $view->get_human_name();
267
  if (isset($bases[$view->base_table])) {
268
    $name .= ' (' . $bases[$view->base_table]['title'] . ')';
269
  }
270

    
271
  return $name;
272
}
273

    
274
/**
275
 * Specialized menu callback to load a view and check its locked status.
276
 *
277
 * @param $name
278
 *   The machine name of the view.
279
 *
280
 * @return
281
 *   The view object, with a "locked" property indicating whether or not
282
 *   someone else is already editing the view.
283
 */
284
function views_ui_cache_load($name) {
285
  ctools_include('object-cache');
286
  views_include('view');
287
  $view = ctools_object_cache_get('view', $name);
288
  $original_view = views_get_view($name);
289

    
290
  if (empty($view)) {
291
    $view = $original_view;
292
    if (!empty($view)) {
293
      // Check to see if someone else is already editing this view.
294
      $view->locked = ctools_object_cache_test('view', $view->name);
295
      // Set a flag to indicate that this view is being edited.
296
      // This flag will be used e.g. to determine whether strings
297
      // should be localized.
298
      $view->editing = TRUE;
299
    }
300
  }
301
  else {
302
    // Keep disabled/enabled status real.
303
    if ($original_view) {
304
      $view->disabled = !empty($original_view->disabled);
305
    }
306
  }
307

    
308
  if (empty($view)) {
309
    return FALSE;
310
  }
311

    
312
  else {
313
    return $view;
314
  }
315
}
316

    
317
/**
318
 * Specialized cache function to add a flag to our view, include an appropriate
319
 * include, and cache more easily.
320
 */
321
function views_ui_cache_set(&$view) {
322
  if (!empty($view->locked)) {
323
    drupal_set_message(t('Changes cannot be made to a locked view.'), 'error');
324
    return;
325
  }
326
  ctools_include('object-cache');
327
  $view->changed = TRUE; // let any future object know that this view has changed.
328

    
329
  if (isset($view->current_display)) {
330
    // Add the knowledge of the changed display, too.
331
    $view->changed_display[$view->current_display] = TRUE;
332
    unset($view->current_display);
333
  }
334

    
335
  // Unset handlers; we don't want to write these into the cache
336
  unset($view->display_handler);
337
  unset($view->default_display);
338
  $view->query = NULL;
339
  foreach (array_keys($view->display) as $id) {
340
    unset($view->display[$id]->handler);
341
    unset($view->display[$id]->default_display);
342
  }
343
  ctools_object_cache_set('view', $view->name, $view);
344
}
345

    
346

    
347
/**
348
 * Specialized menu callback to load a view that is only a default
349
 * view.
350
 */
351
function views_ui_default_load($name) {
352
  $view = views_get_view($name);
353
  if ($view->type == t('Default')) {
354
    return $view;
355
  }
356

    
357
  return FALSE;
358
}
359

    
360
/**
361
 * Theme preprocess for views-view.tpl.php.
362
 */
363
function views_ui_preprocess_views_view(&$vars) {
364
  $view = $vars['view'];
365
  if (!empty($view->views_ui_context) && module_exists('contextual')) {
366
    $view->hide_admin_links = TRUE;
367
    foreach (array('title', 'header', 'exposed', 'rows', 'pager', 'more', 'footer', 'empty', 'attachment_after', 'attachment_before') as $section) {
368
      if (!empty($vars[$section])) {
369
        $vars[$section] = array(
370
          '#theme' => 'views_ui_view_preview_section',
371
          '#view' => $view,
372
          '#section' => $section,
373
          '#content' => is_array($vars[$section]) ? drupal_render($vars[$section]) : $vars[$section],
374
          '#theme_wrappers' => array('views_container'),
375
          '#attributes' => array('class' => 'contextual-links-region'),
376
        );
377
        $vars[$section] = drupal_render($vars[$section]);
378
      }
379
    }
380
  }
381
}
382

    
383
/**
384
 * Theme preprocess for theme_views_ui_view_preview_section().
385
 *
386
 * @TODO
387
 *    Perhaps move this to includes/admin.inc or theme/theme.inc
388
 */
389
function template_preprocess_views_ui_view_preview_section(&$vars) {
390
  switch ($vars['section']) {
391
    case 'title':
392
      $vars['title'] = t('Title');
393
      $links = views_ui_view_preview_section_display_category_links($vars['view'], 'title', $vars['title']);
394
      break;
395
    case 'header':
396
      $vars['title'] = t('Header');
397
      $links = views_ui_view_preview_section_handler_links($vars['view'], $vars['section']);
398
      break;
399
    case 'empty':
400
      $vars['title'] = t('No results behavior');
401
      $links = views_ui_view_preview_section_handler_links($vars['view'], $vars['section']);
402
      break;
403
    case 'exposed':
404
      // @todo Sorts can be exposed too, so we may need a better title.
405
      $vars['title'] = t('Exposed Filters');
406
      $links = views_ui_view_preview_section_display_category_links($vars['view'], 'exposed_form_options', $vars['title']);
407
      break;
408
    case 'rows':
409
      // @todo The title needs to depend on what is being viewed.
410
      $vars['title'] = t('Content');
411
      $links = views_ui_view_preview_section_rows_links($vars['view']);
412
      break;
413
    case 'pager':
414
      $vars['title'] = t('Pager');
415
      $links = views_ui_view_preview_section_display_category_links($vars['view'], 'pager_options', $vars['title']);
416
      break;
417
    case 'more':
418
      $vars['title'] = t('More');
419
      $links = views_ui_view_preview_section_display_category_links($vars['view'], 'use_more', $vars['title']);
420
      break;
421
    case 'footer':
422
      $vars['title'] = t('Footer');
423
      $links = views_ui_view_preview_section_handler_links($vars['view'], $vars['section']);
424
      break;
425
    case 'attachment_before':
426
      // @todo: Add links to the attachment configuration page.
427
      $vars['title'] = t('Attachment before');
428
      break;
429
    case 'attachment_after':
430
      // @todo: Add links to the attachment configuration page.
431
      $vars['title'] = t('Attachment after');
432
      break;
433
  }
434

    
435
  if (isset($links)) {
436
    $build = array(
437
      '#prefix' => '<div class="contextual-links-wrapper">',
438
      '#suffix' => '</div>',
439
      '#theme' => 'links__contextual',
440
      '#links' => $links,
441
      '#attributes' => array('class' => array('contextual-links')),
442
      '#attached' => array(
443
        'library' => array(array('contextual', 'contextual-links')),
444
      ),
445
    );
446
    $vars['links'] = drupal_render($build);
447
  }
448
  $vars['theme_hook_suggestions'][] = 'views_ui_view_preview_section__' . $vars['section'];
449
}
450

    
451
/**
452
 * Returns the HTML for a section of a View being previewed within the Views UI.
453
 */
454
function theme_views_ui_view_preview_section($vars) {
455
  return '<h1 class="section-title">' . $vars['title'] . '</h1>'
456
  . $vars['links']
457
  . '<div class="preview-section">'. $vars['content'] . '</div>';
458
}
459

    
460
/**
461
 * Returns contextual links for each handler of a certain section.
462
 *
463
 * @TODO
464
 *   Bring in relationships
465
 *   Refactor this function to use much stuff of views_ui_edit_form_get_bucket.
466
 *
467
 * @param $title
468
 *   Add a bolded title of this section.
469
 */
470
function views_ui_view_preview_section_handler_links($view, $type, $title = FALSE) {
471
  $display = $view->display_handler->display;
472
  $handlers = $view->display_handler->get_handlers($type);
473
  $links = array();
474

    
475
  $types = views_object_types();
476
  if ($title) {
477
    $links[$type . '-title'] = array(
478
      'title' => $types[$type]['title'],
479
    );
480
  }
481

    
482
  foreach ($handlers as $id => $handler) {
483
    $field_name = $handler->ui_name(TRUE);
484
    $links[$type . '-edit-' . $id] = array(
485
      'title' => t('Edit @section', array('@section' => $field_name)),
486
      'href' => "admin/structure/views/nojs/config-item/$view->name/$display->id/$type/$id",
487
      'attributes' => array('class' => array('views-ajax-link')),
488
    );
489
  }
490
  $links[$type . '-add'] = array(
491
    'title' => t('Add new'),
492
    'href' => "admin/structure/views/nojs/add-item/$view->name/$display->id/$type",
493
    'attributes' => array('class' => array('views-ajax-link')),
494
  );
495

    
496
  return $links;
497
}
498

    
499
/**
500
 * Returns a link to editing a certain display setting.
501
 */
502
function views_ui_view_preview_section_display_category_links($view, $type, $title) {
503
  $display = $view->display_handler->display;
504
  $links = array(
505
    $type . '-edit' => array(
506
      'title' => t('Edit @section', array('@section' => $title)),
507
      'href' => "admin/structure/views/nojs/display/$view->name/$display->id/$type",
508
      'attributes' => array('class' => array('views-ajax-link')),
509
    ),
510
  );
511

    
512
  return $links;
513
}
514

    
515
/**
516
 * Returns all contextual links for the main content part of the view.
517
 */
518
function views_ui_view_preview_section_rows_links($view) {
519
  $display = $view->display_handler->display;
520
  $links = array();
521
  $links = array_merge($links, views_ui_view_preview_section_handler_links($view, 'filter', TRUE));
522
  $links = array_merge($links, views_ui_view_preview_section_handler_links($view, 'field', TRUE));
523
  $links = array_merge($links, views_ui_view_preview_section_handler_links($view, 'sort', TRUE));
524
  $links = array_merge($links, views_ui_view_preview_section_handler_links($view, 'argument', TRUE));
525
  $links = array_merge($links, views_ui_view_preview_section_handler_links($view, 'relationship', TRUE));
526

    
527
  return $links;
528
}
529

    
530

    
531
/**
532
 * Implments hook_ctools_plugin_directory().
533
 *
534
 * Views UI provides wizard plugins on behalf of core base tables.
535
 */
536
function views_ui_ctools_plugin_directory($module, $plugin) {
537
  if ($module == 'views_ui' || ($module == 'ctools' && $plugin == 'export_ui')) {
538
    return 'plugins/' . $plugin;
539
  }
540
}
541

    
542
/**
543
 * Fetch metadata on a specific views ui wizard plugin.
544
 *
545
 * @param $wizard_type
546
 *   Name of a wizard, or name of a base table.
547
 *
548
 * @return
549
 *   An array with information about the requested wizard type.
550
 */
551
function views_ui_get_wizard($wizard_type) {
552
  ctools_include('plugins');
553
  $wizard = ctools_get_plugins('views_ui', 'views_wizard', $wizard_type);
554
  // @todo - handle this via an alter hook instead.
555
  if (!$wizard) {
556
    // Must be a base table using the default wizard plugin.
557
    $base_tables = views_fetch_base_tables();
558
    if (!empty($base_tables[$wizard_type])) {
559
      $wizard = views_ui_views_wizard_defaults();
560
      $wizard['base_table'] = $wizard_type;
561
      $wizard['title'] = $base_tables[$wizard_type]['title'];
562
    }
563
    // The plugin is neither a base table nor an existing wizard.
564
    else {
565
      vpr('Views Wizard: @wizard does not exist. Be sure to implement hook_ctools_plugin_directory.', array('@wizard' => $wizard_type));
566
    }
567
  }
568
  return $wizard;
569
}
570

    
571
/**
572
 * Fetch metadata for all content_type plugins.
573
 *
574
 * @return
575
 *   An array of arrays with information about all available views wizards.
576
 */
577
function views_ui_get_wizards() {
578
  ctools_include('plugins');
579
  $wizard_plugins = ctools_get_plugins('views_ui', 'views_wizard');
580
  $wizard_tables = array();
581
  foreach ($wizard_plugins as $name => $info) {
582
    $wizard_tables[$info['base_table']] = TRUE;
583
  }
584
  $base_tables = views_fetch_base_tables();
585
  $default_wizard = views_ui_views_wizard_defaults();
586
  // Find base tables with no wizard.
587
  // @todo - handle this via an alter hook for plugins?
588
  foreach ($base_tables as $table => $info) {
589
    if (!isset($wizard_tables[$table])) {
590
      $wizard = $default_wizard;
591
      $wizard['title'] = $info['title'];
592
      $wizard['base_table'] = $table;
593
      $wizard_plugins[$table] = $wizard;
594
    }
595
  }
596
  return $wizard_plugins;
597
}
598

    
599
/**
600
 * Helper function to define the default values for a Views wizard plugin.
601
 *
602
 * @return
603
 *   An array of defaults for a views wizard.
604
 */
605
function views_ui_views_wizard_defaults() {
606
  return array(
607
    // The children may, for example, be a different variant for each node type.
608
    'get children' => NULL,
609
    'get child' => NULL,
610
    // title and base table must be populated.  They are empty here just
611
    // so they are documented.
612
    'title' => '',
613
    'base_table' => NULL,
614
    // This is a callback that takes the wizard as argument and returns
615
    // an instantiazed Views UI form wizard object.
616
    'get_instance' => 'views_ui_get_form_wizard_instance',
617
    'form_wizard_class' => array(
618
      'file' => 'views_ui_base_views_wizard',
619
      'class' => 'ViewsUiBaseViewsWizard',
620
    ),
621
  );
622
}
623

    
624
/**
625
 * Inform CTools that the Views wizard plugin can have child plugins.
626
 */
627
function views_ui_ctools_plugin_type() {
628
  return array(
629
    'views_wizard' => array(
630
      'child plugins' => TRUE,
631
      'classes' => array(
632
        'form_wizard_class',
633
      ),
634
      'defaults' => views_ui_views_wizard_defaults(),
635
    ),
636
  );
637
}
638

    
639
function views_ui_get_form_wizard_instance($wizard) {
640
  if (isset($wizard['form_wizard_class']['class'])) {
641
    $class = $wizard['form_wizard_class']['class'];
642
    return new $class($wizard);
643
  }
644
  else {
645
    return new ViewsUiBaseViewsWizard($wizard);
646
  }
647
}
648

    
649
/**
650
 * Implements hook_views_plugins_alter().
651
 */
652
function views_ui_views_plugins_alter(&$plugins) {
653
  // Attach contextual links to each display plugin. The links will point to
654
  // paths underneath "admin/structure/views/view/{$view->name}" (i.e., paths
655
  // for editing and performing other contextual actions on the view).
656
  foreach ($plugins['display'] as &$display) {
657
    $display['contextual links']['views_ui'] = array(
658
      'parent path' => 'admin/structure/views/view',
659
      'argument properties' => array('name'),
660
    );
661
  }
662
}
663

    
664
/**
665
 * Implements hook_contextual_links_view_alter().
666
 */
667
function views_ui_contextual_links_view_alter(&$element, $items) {
668
  // Remove contextual links from being rendered, when so desired, such as
669
  // within a View preview.
670
  if (views_ui_contextual_links_suppress()) {
671
    $element['#links'] = array();
672
  }
673
  // Append the display ID to the Views UI edit links, so that clicking on the
674
  // contextual link takes you directly to the correct display tab on the edit
675
  // screen.
676
  elseif (!empty($element['#links']['views-ui-edit']) && !empty($element['#element']['#views_contextual_links_info']['views_ui']['view_display_id'])) {
677
    $display_id = $element['#element']['#views_contextual_links_info']['views_ui']['view_display_id'];
678
    $element['#links']['views-ui-edit']['href'] .= '/' . $display_id;
679
  }
680
}
681

    
682
/**
683
 * Sets a static variable for controlling whether contextual links are rendered.
684
 *
685
 * @see views_ui_contextual_links_view_alter()
686
 */
687
function views_ui_contextual_links_suppress($set = NULL) {
688
  $suppress = &drupal_static(__FUNCTION__);
689
  if (isset($set)) {
690
    $suppress = $set;
691
  }
692
  return $suppress;
693
}
694

    
695
/**
696
 * Increments the views_ui_contextual_links_suppress() static variable.
697
 *
698
 * When this function is added to the #pre_render of an element, and
699
 * 'views_ui_contextual_links_suppress_pop' is added to the #post_render of the
700
 * same element, then all contextual links within the element and its
701
 * descendants are suppressed from being rendered. This is used, for example,
702
 * during a View preview, when it is not desired for nodes in the Views result
703
 * to have contextual links.
704
 *
705
 * @see views_ui_contextual_links_suppress_pop()
706
 */
707
function views_ui_contextual_links_suppress_push() {
708
  views_ui_contextual_links_suppress(((int) views_ui_contextual_links_suppress())+1);
709
}
710

    
711
/**
712
 * Decrements the views_ui_contextual_links_suppress() static variable.
713
 *
714
 * @see views_ui_contextual_links_suppress_push()
715
 */
716
function views_ui_contextual_links_suppress_pop() {
717
  views_ui_contextual_links_suppress(((int) views_ui_contextual_links_suppress())-1);
718
}
719

    
720
/**
721
 * Menu callback; handles AJAX form submissions similar to ajax_form_callback(), but can be used for uncached forms.
722
 *
723
 * ajax_form_callback(), the menu callback for the system/ajax path, requires
724
 * the form to be retrievable from the form cache, because it lacks a trusted
725
 * $form_id argument with which to call drupal_retrieve_form(). When AJAX is
726
 * wanted on a non-cacheable form, #ajax['path'] can be set to a path whose
727
 * menu router item's 'page callback' is this function, and whose
728
 * 'page arguments' is the form id, optionally followed by additional build
729
 * arguments, as expected by drupal_get_form().
730
 *
731
 * The same caution must be used when defining a hook_menu() entry with this
732
 * page callback as is used when defining a hook_menu() entry with the
733
 * 'drupal_get_form' page callback: a 'page arguments' must be specified with a
734
 * literal value as the first argument, because $form_id determines which form
735
 * builder function gets called, so must be safe from user tampering.
736
 *
737
 * @see drupal_get_form()
738
 * @see ajax_form_callback()
739
 * @see http://drupal.org/node/774876
740
 */
741
function views_ui_ajax_get_form($form_id) {
742
  // @see ajax_get_form()
743
  $form_state = array(
744
    'no_redirect' => TRUE,
745
  );
746
  $form_state['rebuild_info']['copy']['#build_id'] = TRUE;
747
  $form_state['rebuild_info']['copy']['#action'] = TRUE;
748

    
749
  // @see drupal_get_form()
750
  $args = func_get_args();
751
  array_shift($args);
752
  $form_state['build_info']['args'] = $args;
753
  $form = drupal_build_form($form_id, $form_state);
754

    
755
  // @see ajax_form_callback()
756
  if (!empty($form_state['triggering_element'])) {
757
    $callback = $form_state['triggering_element']['#ajax']['callback'];
758
  }
759
  if (!empty($callback) && function_exists($callback)) {
760
    return $callback($form, $form_state);
761
  }
762
}
763
// @todo move these when we can
764

    
765

    
766
/**
767
 * Helper function to get a list of paths assigned to a view.
768
 *
769
 * @param $view
770
 *   The view.
771
 *
772
 * @return
773
 *   An array of links to this view's display paths.
774
 */
775
function _views_ui_get_paths($view) {
776
  $all_paths = array();
777
  if (empty($view->display)) {
778
    $all_paths[] = t('Edit this view to add a display.');
779
  }
780
  else {
781
    $view->init_display();   // Make sure all the handlers are set up
782
    foreach ($view->display as $display) {
783
      if (!empty($display->handler) && $display->handler->has_path()) {
784
        $one_path = $display->handler->get_option('path');
785
        if (empty($path_sort)) {
786
          $path_sort = strtolower($one_path);
787
        }
788
        if (empty($view->disabled) && strpos($one_path, '%') === FALSE) {
789
          $all_paths[] = l('/' . $one_path, $one_path);
790
        }
791
        else {
792
          $all_paths[] = check_plain('/' . $one_path);
793
        }
794
      }
795
    }
796
  }
797

    
798
  return array_unique($all_paths);
799
}
800

    
801
/**
802
 * Helper function to get a list of displays included in a view.
803
 *
804
 * @param $view
805
 *   The view.
806
 *
807
 * @return
808
 *   An array of display types that this view includes.
809
 */
810
function _views_ui_get_displays_list($view) {
811
  $displays = array();
812
  foreach ($view->display as $display) {
813
    if (!empty($display->handler->definition['admin'])) {
814
      $displays[$display->handler->definition['admin']] = TRUE;
815
    }
816
  }
817

    
818
  if ($displays) {
819
    ksort($displays);
820
    $displays = array_keys($displays);
821
  }
822
  return $displays;
823
}
824

    
825
/**
826
 * This is part of a patch to address a jQueryUI bug.  The bug is responsible
827
 * for the inability to scroll a page when a modal dialog is active. If the content
828
 * of the dialog extends beyond the bottom of the viewport, the user is only able
829
 * to scroll with a mousewheel or up/down keyboard keys.
830
 *
831
 * @see http://bugs.jqueryui.com/ticket/4671
832
 * @see https://bugs.webkit.org/show_bug.cgi?id=19033
833
 * @see /js/jquery.ui.dialog.patch.js
834
 * @see /js/jquery.ui.dialog.min.js
835
 *
836
 * The javascript patch overwrites the $.ui.dialog.overlay.events object to remove
837
 * the mousedown, mouseup and click events from the list of events that are bound
838
 * in $.ui.dialog.overlay.create.
839
 */
840

    
841
function views_ui_library_alter(&$libraries, $module) {
842
  if ($module == 'system' && isset($libraries['ui.dialog'])) {
843
    // Only apply the fix, if we don't have an up to date jQueryUI version.
844
    if (version_compare($libraries['ui.dialog']['version'], '1.7.2', '>=') && version_compare($libraries['ui.dialog']['version'], '1.10.0', '<')) {
845
      $libraries['ui.dialog']['js'][drupal_get_path('module', 'views') . '/js/jquery.ui.dialog.patch.js'] = array();
846
    }
847
  }
848
}
849

    
850
/**
851
 * Truncate strings to a set length and provide a ... if they truncated.
852
 *
853
 * This is often used in the UI to ensure long strings fit.
854
 */
855
function views_ui_truncate($string, $length) {
856
  if (drupal_strlen($string) > $length) {
857
    $string = drupal_substr($string, 0, $length);
858
    $string .= '...';
859
  }
860

    
861
  return $string;
862
}