Projet

Général

Profil

Paste
Télécharger (28 ko) Statistiques
| Branche: | Révision:

root / htmltest / sites / all / modules / views / views_ui.module @ a5572547

1 85ad3d82 Assos Assos
<?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_edit_item' => array(
190
      'variables' => array('type' => NULL, 'view' => NULL, 'display' => NULL, 'no_fields' => FALSE),
191
      'template' => 'views-ui-edit-item',
192
      'path' => "$path/theme",
193
    ),
194
    'views_ui_rearrange_form' => array(
195
      'render element' => 'form',
196
    ),
197
    'views_ui_rearrange_filter_form' => array(
198
      'render element' => 'form',
199
      'file' => 'includes/admin.inc',
200
    ),
201
    'views_ui_expose_filter_form' => array(
202
      'render element' => 'form',
203
      'file' => 'includes/admin.inc',
204
    ),
205
206
    // list views
207
    'views_ui_view_info' => array(
208
      'variables' => array('view' => NULL, 'base' => NULL),
209
      'file' => "includes/admin.inc",
210
    ),
211
212
    // Group of filters.
213
    'views_ui_build_group_filter_form' => array(
214
      'render element' => 'form',
215
      'file' => 'includes/admin.inc',
216
    ),
217
218
    // tab themes
219
    'views_tabset' => array(
220
      'variables' => array('tabs' => NULL),
221
    ),
222
    'views_tab' => array(
223
      'variables' => array('body' => NULL),
224
    ),
225
    'views_ui_reorder_displays_form' => array(
226
      'render element' => 'form',
227
      'file' => 'includes/admin.inc',
228
    ),
229
230
231
    // On behalf of a plugin
232
    'views_ui_style_plugin_table' => array(
233
      'render element' => 'form',
234
    ),
235
236
    // When previewing a view.
237
    'views_ui_view_preview_section' => array(
238
      'variables' => array('view' => NULL, 'section' => NULL, 'content' => NULL, 'links' => ''),
239
    ),
240
241
    // Generic container wrapper, to use instead of theme_container when an id
242
    // is not desired.
243
    'views_container' => array(
244
      'render element' => 'element',
245
      'file' => 'theme/theme.inc',
246
    ),
247
  );
248
}
249
250
/**
251
 * Impements hook_custom_theme()
252
 */
253
function views_ui_custom_theme() {
254
  $theme = variable_get('views_ui_custom_theme', '_default');
255
256
  if ($theme != '_default') {
257
    $available = list_themes();
258
259
    if (isset($available[$theme]) && $available[$theme]->status && preg_match('/^admin\/structure\/views/', current_path())) {
260
      return $theme;
261
    }
262
  }
263
}
264
265
/**
266
 * Page title callback for the Edit View page.
267
 */
268
function views_ui_edit_page_title($view) {
269
  module_load_include('inc', 'views_ui', 'includes/admin');
270
  $bases = views_fetch_base_tables();
271
  $name = $view->get_human_name();
272
  if (isset($bases[$view->base_table])) {
273
    $name .= ' (' . $bases[$view->base_table]['title'] . ')';
274
  }
275
276
  return $name;
277
}
278
279
/**
280
 * Specialized menu callback to load a view and check its locked status.
281
 *
282
 * @param $name
283
 *   The machine name of the view.
284
 *
285
 * @return
286
 *   The view object, with a "locked" property indicating whether or not
287
 *   someone else is already editing the view.
288
 */
289
function views_ui_cache_load($name) {
290
  ctools_include('object-cache');
291
  views_include('view');
292
  $view = ctools_object_cache_get('view', $name);
293
  $original_view = views_get_view($name);
294
295
  if (empty($view)) {
296
    $view = $original_view;
297
    if (!empty($view)) {
298
      // Check to see if someone else is already editing this view.
299
      $view->locked = ctools_object_cache_test('view', $view->name);
300
      // Set a flag to indicate that this view is being edited.
301
      // This flag will be used e.g. to determine whether strings
302
      // should be localized.
303
      $view->editing = TRUE;
304
    }
305
  }
306
  else {
307
    // Keep disabled/enabled status real.
308
    if ($original_view) {
309
      $view->disabled = !empty($original_view->disabled);
310
    }
311
  }
312
313
  if (empty($view)) {
314
    return FALSE;
315
  }
316
317
  else {
318
    return $view;
319
  }
320
}
321
322
/**
323
 * Specialized cache function to add a flag to our view, include an appropriate
324
 * include, and cache more easily.
325
 */
326
function views_ui_cache_set(&$view) {
327
  if (!empty($view->locked)) {
328
    drupal_set_message(t('Changes cannot be made to a locked view.'), 'error');
329
    return;
330
  }
331
  ctools_include('object-cache');
332
  $view->changed = TRUE; // let any future object know that this view has changed.
333
334
  if (isset($view->current_display)) {
335
    // Add the knowledge of the changed display, too.
336
    $view->changed_display[$view->current_display] = TRUE;
337
    unset($view->current_display);
338
  }
339
340
  // Unset handlers; we don't want to write these into the cache
341
  unset($view->display_handler);
342
  unset($view->default_display);
343
  $view->query = NULL;
344
  foreach (array_keys($view->display) as $id) {
345
    unset($view->display[$id]->handler);
346
    unset($view->display[$id]->default_display);
347
  }
348
  ctools_object_cache_set('view', $view->name, $view);
349
}
350
351
352
/**
353
 * Specialized menu callback to load a view that is only a default
354
 * view.
355
 */
356
function views_ui_default_load($name) {
357
  $view = views_get_view($name);
358
  if ($view->type == t('Default')) {
359
    return $view;
360
  }
361
362
  return FALSE;
363
}
364
365
/**
366
 * Theme preprocess for views-view.tpl.php.
367
 */
368
function views_ui_preprocess_views_view(&$vars) {
369
  $view = $vars['view'];
370
  if (!empty($view->views_ui_context) && module_exists('contextual')) {
371
    $view->hide_admin_links = TRUE;
372
    foreach (array('title', 'header', 'exposed', 'rows', 'pager', 'more', 'footer', 'empty', 'attachment_after', 'attachment_before') as $section) {
373
      if (!empty($vars[$section])) {
374
        $vars[$section] = array(
375
          '#theme' => 'views_ui_view_preview_section',
376
          '#view' => $view,
377
          '#section' => $section,
378
          '#content' => is_array($vars[$section]) ? drupal_render($vars[$section]) : $vars[$section],
379
          '#theme_wrappers' => array('views_container'),
380
          '#attributes' => array('class' => 'contextual-links-region'),
381
        );
382
        $vars[$section] = drupal_render($vars[$section]);
383
      }
384
    }
385
  }
386
}
387
388
/**
389
 * Theme preprocess for theme_views_ui_view_preview_section().
390
 *
391
 * @TODO
392
 *    Perhaps move this to includes/admin.inc or theme/theme.inc
393
 */
394
function template_preprocess_views_ui_view_preview_section(&$vars) {
395
  switch ($vars['section']) {
396
    case 'title':
397
      $vars['title'] = t('Title');
398
      $links = views_ui_view_preview_section_display_category_links($vars['view'], 'title', $vars['title']);
399
      break;
400
    case 'header':
401
      $vars['title'] = t('Header');
402
      $links = views_ui_view_preview_section_handler_links($vars['view'], $vars['section']);
403
      break;
404
    case 'empty':
405
      $vars['title'] = t('No results behavior');
406
      $links = views_ui_view_preview_section_handler_links($vars['view'], $vars['section']);
407
      break;
408
    case 'exposed':
409
      // @todo Sorts can be exposed too, so we may need a better title.
410
      $vars['title'] = t('Exposed Filters');
411
      $links = views_ui_view_preview_section_display_category_links($vars['view'], 'exposed_form_options', $vars['title']);
412
      break;
413
    case 'rows':
414
      // @todo The title needs to depend on what is being viewed.
415
      $vars['title'] = t('Content');
416
      $links = views_ui_view_preview_section_rows_links($vars['view']);
417
      break;
418
    case 'pager':
419
      $vars['title'] = t('Pager');
420
      $links = views_ui_view_preview_section_display_category_links($vars['view'], 'pager_options', $vars['title']);
421
      break;
422
    case 'more':
423
      $vars['title'] = t('More');
424
      $links = views_ui_view_preview_section_display_category_links($vars['view'], 'use_more', $vars['title']);
425
      break;
426
    case 'footer':
427
      $vars['title'] = t('Footer');
428
      $links = views_ui_view_preview_section_handler_links($vars['view'], $vars['section']);
429
      break;
430
    case 'attachment_before':
431
      // @todo: Add links to the attachment configuration page.
432
      $vars['title'] = t('Attachment before');
433
      break;
434
    case 'attachment_after':
435
      // @todo: Add links to the attachment configuration page.
436
      $vars['title'] = t('Attachment after');
437
      break;
438
  }
439
440
  if (isset($links)) {
441
    $build = array(
442
      '#prefix' => '<div class="contextual-links-wrapper">',
443
      '#suffix' => '</div>',
444
      '#theme' => 'links__contextual',
445
      '#links' => $links,
446
      '#attributes' => array('class' => array('contextual-links')),
447
      '#attached' => array(
448
        'library' => array(array('contextual', 'contextual-links')),
449
      ),
450
    );
451
    $vars['links'] = drupal_render($build);
452
  }
453
  $vars['theme_hook_suggestions'][] = 'views_ui_view_preview_section__' . $vars['section'];
454
}
455
456
/**
457
 * Returns the HTML for a section of a View being previewed within the Views UI.
458
 */
459
function theme_views_ui_view_preview_section($vars) {
460
  return '<h1 class="section-title">' . $vars['title'] . '</h1>'
461
  . $vars['links']
462
  . '<div class="preview-section">'. $vars['content'] . '</div>';
463
}
464
465
/**
466
 * Returns contextual links for each handler of a certain section.
467
 *
468
 * @TODO
469
 *   Bring in relationships
470
 *   Refactor this function to use much stuff of views_ui_edit_form_get_bucket.
471
 *
472
 * @param $title
473
 *   Add a bolded title of this section.
474
 */
475
function views_ui_view_preview_section_handler_links($view, $type, $title = FALSE) {
476
  $display = $view->display_handler->display;
477
  $handlers = $view->display_handler->get_handlers($type);
478
  $links = array();
479
480
  $types = views_object_types();
481
  if ($title) {
482
    $links[$type . '-title'] = array(
483
      'title' => $types[$type]['title'],
484
    );
485
  }
486
487
  foreach ($handlers as $id => $handler) {
488
    $field_name = $handler->ui_name(TRUE);
489
    $links[$type . '-edit-' . $id] = array(
490
      'title' => t('Edit @section', array('@section' => $field_name)),
491
      'href' => "admin/structure/views/nojs/config-item/$view->name/$display->id/$type/$id",
492
      'attributes' => array('class' => array('views-ajax-link')),
493
    );
494
  }
495
  $links[$type . '-add'] = array(
496
    'title' => t('Add new'),
497
    'href' => "admin/structure/views/nojs/add-item/$view->name/$display->id/$type",
498
    'attributes' => array('class' => array('views-ajax-link')),
499
  );
500
501
  return $links;
502
}
503
504
/**
505
 * Returns a link to editing a certain display setting.
506
 */
507
function views_ui_view_preview_section_display_category_links($view, $type, $title) {
508
  $display = $view->display_handler->display;
509
  $links = array(
510
    $type . '-edit' => array(
511
      'title' => t('Edit @section', array('@section' => $title)),
512
      'href' => "admin/structure/views/nojs/display/$view->name/$display->id/$type",
513
      'attributes' => array('class' => array('views-ajax-link')),
514
    ),
515
  );
516
517
  return $links;
518
}
519
520
/**
521
 * Returns all contextual links for the main content part of the view.
522
 */
523
function views_ui_view_preview_section_rows_links($view) {
524
  $display = $view->display_handler->display;
525
  $links = array();
526
  $links = array_merge($links, views_ui_view_preview_section_handler_links($view, 'filter', TRUE));
527
  $links = array_merge($links, views_ui_view_preview_section_handler_links($view, 'field', TRUE));
528
  $links = array_merge($links, views_ui_view_preview_section_handler_links($view, 'sort', TRUE));
529
  $links = array_merge($links, views_ui_view_preview_section_handler_links($view, 'argument', TRUE));
530
  $links = array_merge($links, views_ui_view_preview_section_handler_links($view, 'relationship', TRUE));
531
532
  return $links;
533
}
534
535
536
/**
537
 * Implments hook_ctools_plugin_directory().
538
 *
539
 * Views UI provides wizard plugins on behalf of core base tables.
540
 */
541
function views_ui_ctools_plugin_directory($module, $plugin) {
542
  if ($module == 'views_ui' || ($module == 'ctools' && $plugin == 'export_ui')) {
543
    return 'plugins/' . $plugin;
544
  }
545
}
546
547
/**
548
 * Fetch metadata on a specific views ui wizard plugin.
549
 *
550
 * @param $wizard_type
551
 *   Name of a wizard, or name of a base table.
552
 *
553
 * @return
554
 *   An array with information about the requested wizard type.
555
 */
556
function views_ui_get_wizard($wizard_type) {
557
  ctools_include('plugins');
558
  $wizard = ctools_get_plugins('views_ui', 'views_wizard', $wizard_type);
559
  // @todo - handle this via an alter hook instead.
560
  if (!$wizard) {
561
    // Must be a base table using the default wizard plugin.
562
    $base_tables = views_fetch_base_tables();
563
    if (!empty($base_tables[$wizard_type])) {
564
      $wizard = views_ui_views_wizard_defaults();
565
      $wizard['base_table'] = $wizard_type;
566
      $wizard['title'] = $base_tables[$wizard_type]['title'];
567
    }
568
    // The plugin is neither a base table nor an existing wizard.
569
    else {
570
      vpr('Views Wizard: @wizard does not exist. Be sure to implement hook_ctools_plugin_directory.', array('@wizard' => $wizard_type));
571
    }
572
  }
573
  return $wizard;
574
}
575
576
/**
577
 * Fetch metadata for all content_type plugins.
578
 *
579
 * @return
580
 *   An array of arrays with information about all available views wizards.
581
 */
582
function views_ui_get_wizards() {
583
  ctools_include('plugins');
584
  $wizard_plugins = ctools_get_plugins('views_ui', 'views_wizard');
585
  $wizard_tables = array();
586
  foreach ($wizard_plugins as $name => $info) {
587
    $wizard_tables[$info['base_table']] = TRUE;
588
  }
589
  $base_tables = views_fetch_base_tables();
590
  $default_wizard = views_ui_views_wizard_defaults();
591
  // Find base tables with no wizard.
592
  // @todo - handle this via an alter hook for plugins?
593
  foreach ($base_tables as $table => $info) {
594
    if (!isset($wizard_tables[$table])) {
595
      $wizard = $default_wizard;
596
      $wizard['title'] = $info['title'];
597
      $wizard['base_table'] = $table;
598
      $wizard_plugins[$table] = $wizard;
599
    }
600
  }
601
  return $wizard_plugins;
602
}
603
604
/**
605
 * Helper function to define the default values for a Views wizard plugin.
606
 *
607
 * @return
608
 *   An array of defaults for a views wizard.
609
 */
610
function views_ui_views_wizard_defaults() {
611
  return array(
612
    // The children may, for example, be a different variant for each node type.
613
    'get children' => NULL,
614
    'get child' => NULL,
615
    // title and base table must be populated.  They are empty here just
616
    // so they are documented.
617
    'title' => '',
618
    'base_table' => NULL,
619
    // This is a callback that takes the wizard as argument and returns
620
    // an instantiazed Views UI form wizard object.
621
    'get_instance' => 'views_ui_get_form_wizard_instance',
622
    'form_wizard_class' => array(
623
      'file' => 'views_ui_base_views_wizard',
624
      'class' => 'ViewsUiBaseViewsWizard',
625
    ),
626
  );
627
}
628
629
/**
630
 * Inform CTools that the Views wizard plugin can have child plugins.
631
 */
632
function views_ui_ctools_plugin_type() {
633
  return array(
634
    'views_wizard' => array(
635
      'child plugins' => TRUE,
636
      'classes' => array(
637
        'form_wizard_class',
638
      ),
639
      'defaults' => views_ui_views_wizard_defaults(),
640
    ),
641
  );
642
}
643
644
function views_ui_get_form_wizard_instance($wizard) {
645
  if (isset($wizard['form_wizard_class']['class'])) {
646
    $class = $wizard['form_wizard_class']['class'];
647
    return new $class($wizard);
648
  }
649
  else {
650
    return new ViewsUiBaseViewsWizard($wizard);
651
  }
652
}
653
654
/**
655
 * Implements hook_views_plugins_alter().
656
 */
657
function views_ui_views_plugins_alter(&$plugins) {
658
  // Attach contextual links to each display plugin. The links will point to
659
  // paths underneath "admin/structure/views/view/{$view->name}" (i.e., paths
660
  // for editing and performing other contextual actions on the view).
661
  foreach ($plugins['display'] as &$display) {
662
    $display['contextual links']['views_ui'] = array(
663
      'parent path' => 'admin/structure/views/view',
664
      'argument properties' => array('name'),
665
    );
666
  }
667
}
668
669
/**
670
 * Implements hook_contextual_links_view_alter().
671
 */
672
function views_ui_contextual_links_view_alter(&$element, $items) {
673
  // Remove contextual links from being rendered, when so desired, such as
674
  // within a View preview.
675
  if (views_ui_contextual_links_suppress()) {
676
    $element['#links'] = array();
677
  }
678
  // Append the display ID to the Views UI edit links, so that clicking on the
679
  // contextual link takes you directly to the correct display tab on the edit
680
  // screen.
681
  elseif (!empty($element['#links']['views-ui-edit']) && !empty($element['#element']['#views_contextual_links_info']['views_ui']['view_display_id'])) {
682
    $display_id = $element['#element']['#views_contextual_links_info']['views_ui']['view_display_id'];
683
    $element['#links']['views-ui-edit']['href'] .= '/' . $display_id;
684
  }
685
}
686
687
/**
688
 * Sets a static variable for controlling whether contextual links are rendered.
689
 *
690
 * @see views_ui_contextual_links_view_alter()
691
 */
692
function views_ui_contextual_links_suppress($set = NULL) {
693
  $suppress = &drupal_static(__FUNCTION__);
694
  if (isset($set)) {
695
    $suppress = $set;
696
  }
697
  return $suppress;
698
}
699
700
/**
701
 * Increments the views_ui_contextual_links_suppress() static variable.
702
 *
703
 * When this function is added to the #pre_render of an element, and
704
 * 'views_ui_contextual_links_suppress_pop' is added to the #post_render of the
705
 * same element, then all contextual links within the element and its
706
 * descendants are suppressed from being rendered. This is used, for example,
707
 * during a View preview, when it is not desired for nodes in the Views result
708
 * to have contextual links.
709
 *
710
 * @see views_ui_contextual_links_suppress_pop()
711
 */
712
function views_ui_contextual_links_suppress_push() {
713
  views_ui_contextual_links_suppress(((int) views_ui_contextual_links_suppress())+1);
714
}
715
716
/**
717
 * Decrements the views_ui_contextual_links_suppress() static variable.
718
 *
719
 * @see views_ui_contextual_links_suppress_push()
720
 */
721
function views_ui_contextual_links_suppress_pop() {
722
  views_ui_contextual_links_suppress(((int) views_ui_contextual_links_suppress())-1);
723
}
724
725
/**
726
 * Menu callback; handles AJAX form submissions similar to ajax_form_callback(), but can be used for uncached forms.
727
 *
728
 * ajax_form_callback(), the menu callback for the system/ajax path, requires
729
 * the form to be retrievable from the form cache, because it lacks a trusted
730
 * $form_id argument with which to call drupal_retrieve_form(). When AJAX is
731
 * wanted on a non-cacheable form, #ajax['path'] can be set to a path whose
732
 * menu router item's 'page callback' is this function, and whose
733
 * 'page arguments' is the form id, optionally followed by additional build
734
 * arguments, as expected by drupal_get_form().
735
 *
736
 * The same caution must be used when defining a hook_menu() entry with this
737
 * page callback as is used when defining a hook_menu() entry with the
738
 * 'drupal_get_form' page callback: a 'page arguments' must be specified with a
739
 * literal value as the first argument, because $form_id determines which form
740
 * builder function gets called, so must be safe from user tampering.
741
 *
742
 * @see drupal_get_form()
743
 * @see ajax_form_callback()
744
 * @see http://drupal.org/node/774876
745
 */
746
function views_ui_ajax_get_form($form_id) {
747
  // @see ajax_get_form()
748
  $form_state = array(
749
    'no_redirect' => TRUE,
750
  );
751
  $form_state['rebuild_info']['copy']['#build_id'] = TRUE;
752
  $form_state['rebuild_info']['copy']['#action'] = TRUE;
753
754
  // @see drupal_get_form()
755
  $args = func_get_args();
756
  array_shift($args);
757
  $form_state['build_info']['args'] = $args;
758
  $form = drupal_build_form($form_id, $form_state);
759
760
  // @see ajax_form_callback()
761
  if (!empty($form_state['triggering_element'])) {
762
    $callback = $form_state['triggering_element']['#ajax']['callback'];
763
  }
764
  if (!empty($callback) && function_exists($callback)) {
765
    return $callback($form, $form_state);
766
  }
767
}
768
// @todo move these when we can
769
770
771
/**
772
 * Helper function to get a list of paths assigned to a view.
773
 *
774
 * @param $view
775
 *   The view.
776
 *
777
 * @return
778
 *   An array of links to this view's display paths.
779
 */
780
function _views_ui_get_paths($view) {
781
  $all_paths = array();
782
  if (empty($view->display)) {
783
    $all_paths[] = t('Edit this view to add a display.');
784
  }
785
  else {
786
    $view->init_display();   // Make sure all the handlers are set up
787
    foreach ($view->display as $display) {
788
      if (!empty($display->handler) && $display->handler->has_path()) {
789
        $one_path = $display->handler->get_option('path');
790
        if (empty($path_sort)) {
791
          $path_sort = strtolower($one_path);
792
        }
793
        if (empty($view->disabled) && strpos($one_path, '%') === FALSE) {
794
          $all_paths[] = l('/' . $one_path, $one_path);
795
        }
796
        else {
797
          $all_paths[] = check_plain('/' . $one_path);
798
        }
799
      }
800
    }
801
  }
802
803
  return array_unique($all_paths);
804
}
805
806
/**
807
 * Helper function to get a list of displays included in a view.
808
 *
809
 * @param $view
810
 *   The view.
811
 *
812
 * @return
813
 *   An array of display types that this view includes.
814
 */
815
function _views_ui_get_displays_list($view) {
816
  $displays = array();
817
  foreach ($view->display as $display) {
818
    if (!empty($display->handler->definition['admin'])) {
819
      $displays[$display->handler->definition['admin']] = TRUE;
820
    }
821
  }
822
823
  if ($displays) {
824
    ksort($displays);
825
    $displays = array_keys($displays);
826
  }
827
  return $displays;
828
}
829
830
/**
831
 * This is part of a patch to address a jQueryUI bug.  The bug is responsible
832
 * for the inability to scroll a page when a modal dialog is active. If the content
833
 * of the dialog extends beyond the bottom of the viewport, the user is only able
834
 * to scroll with a mousewheel or up/down keyboard keys.
835
 *
836
 * @see http://bugs.jqueryui.com/ticket/4671
837
 * @see https://bugs.webkit.org/show_bug.cgi?id=19033
838
 * @see /js/jquery.ui.dialog.patch.js
839
 * @see /js/jquery.ui.dialog.min.js
840
 *
841
 * The javascript patch overwrites the $.ui.dialog.overlay.events object to remove
842
 * the mousedown, mouseup and click events from the list of events that are bound
843
 * in $.ui.dialog.overlay.create.
844
 */
845
846
function views_ui_library_alter(&$libraries, $module) {
847
  if ($module == 'system' && isset($libraries['ui.dialog'])) {
848
    // Only apply the fix, if we don't have an up to date jQueryUI version.
849
    if (version_compare($libraries['ui.dialog']['version'], '1.7.2', '>=') && version_compare($libraries['ui.dialog']['version'], '1.10.0', '<')) {
850
      $libraries['ui.dialog']['js'][drupal_get_path('module', 'views') . '/js/jquery.ui.dialog.patch.js'] = array();
851
    }
852
  }
853
}
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
 */
860
function 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
}