Projet

Général

Profil

Paste
Télécharger (90,3 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / views / views.module @ 4003efde

1
<?php
2

    
3
/**
4
 * @file
5
 * Primarily Drupal hooks and global API functions to manipulate views.
6
 *
7
 * This is the main module file for Views. The main entry points into
8
 * this module are views_page() and views_block(), where it handles
9
 * incoming page and block requests.
10
 */
11

    
12
/**
13
 * Implements hook_help().
14
 */
15
function views_help($path, $arg) {
16
  switch ($path) {
17
    case 'admin/help#views':
18
      $output = '';
19
      $output .= '<h3>' . t('About') . '</h3>';
20
      $output .= '<p>' . t('The Views module provides a back end to fetch information from content, user accounts, taxonomy terms, and other entities from the database and present it to the user as a grid, HTML list, table, unformatted list, etc. The resulting displays are known generally as views.') . '</p>';
21
      $output .= '<p>' . t('For more information, see the <a href="@views" target="blank">online documentation for the Views</a>.', array('@views' => 'https://www.drupal.org/documentation/modules/views')) . '</p>';
22
      $output .= '<p>' . t('In order to create and modify your own views using the administration and configuration user interface, you will need to enable either the Views UI module in core or a contributed module that provides a user interface for Views. See the <a href="/admin/structure/views">Views UI module help page</a> for more information.') . '</p>';
23

    
24
      $output .= '<h3>' . t('Uses') . '</h3>';
25
      $output .= '<dl>';
26
      $output .= '<dt>' . t('Adding functionality to administrative pages') . '</dt>';
27
      $output .= '<dd>' . t('The Views module adds functionality to some core administration pages. For example, <em>admin/content</em> uses Views to filter and sort content. With Views uninstalled, <em>admin/content</em> is more limited.') . '</dd>';
28

    
29
      $output .= '<dt>' . t('Expanding Views functionality') . '</dt>';
30
      $output .= '<dd>' . t('Contributed projects that support the Views module can be found in the  <a href="@views-related" target="blank">online documentation for Views-related contributed modules.</a>.', array('@views-related' => 'https://www.drupal.org/documentation/modules/views/add-ons')) . '</dd>';
31

    
32
      $output .= '<dt>' . t('Improving table accessibility') . '</dt>';
33
      $output .= '<dd>' . t('Views tables include semantic markup to improve accessibility. Data cells are automatically associated with header cells through id and header attributes. To improve the accessibility of your tables you can add descriptive elements within the Views table settings. The caption element can introduce context for a table, making it easier to understand. The summary element can provide an overview of how the data has been organized and how to navigate the table. Both the caption and summary are visible by default and also implemented according to HTML5 guidelines.') . '</dd>';
34
      return $output;
35
  }
36
}
37

    
38
/**
39
 * Advertise the current views api version.
40
 */
41
function views_api_version() {
42
  return '3.0';
43
}
44

    
45
/**
46
 * Implements hook_forms().
47
 *
48
 * To provide distinct form IDs for Views forms, the View name and
49
 * specific display name are appended to the base ID,
50
 * views_form_views_form. When such a form is built or submitted, this
51
 * function will return the proper callback function to use for the given form.
52
 */
53
function views_forms($form_id, $args) {
54
  if (strpos($form_id, 'views_form_') === 0) {
55
    return array(
56
      $form_id => array(
57
        'callback' => 'views_form',
58
      ),
59
    );
60
  }
61
}
62

    
63
/**
64
 * Returns a form ID for a Views form using the name and display of the View.
65
 */
66
function views_form_id($view) {
67
  $parts = array(
68
    'views_form',
69
    $view->name,
70
    $view->current_display,
71
  );
72

    
73
  return implode('_', $parts);
74
}
75

    
76
/**
77
 * Views will not load plugins advertising a version older than this.
78
 */
79
function views_api_minimum_version() {
80
  return '2';
81
}
82

    
83
/**
84
 * Implements hook_theme().
85
 *
86
 * Register views theming functions.
87
 */
88
function views_theme($existing, $type, $theme, $path) {
89
  $path = drupal_get_path('module', 'views');
90
  ctools_include('theme', 'views', 'theme');
91

    
92
  // Some quasi clever array merging here.
93
  $base = array(
94
    'file' => 'theme.inc',
95
    'path' => $path . '/theme',
96
  );
97

    
98
  // Our extra version of pager from pager.inc.
99
  $hooks['views_mini_pager'] = $base + array(
100
    'variables' => array(
101
      'tags' => array(),
102
      'element' => 0,
103
      'parameters' => array(),
104
    ),
105
    'pattern' => 'views_mini_pager__',
106
  );
107

    
108
  $variables = array(
109
    // For displays, we pass in a dummy array as the first parameter, since
110
    // $view is an object but the core contextual_preprocess() function only
111
    // attaches contextual links when the primary theme argument is an array.
112
    'display' => array('view_array' => array(), 'view' => NULL),
113
    'style' => array(
114
      'view' => NULL,
115
      'options' => NULL,
116
      'rows' => NULL,
117
      'title' => NULL,
118
    ),
119
    'row' => array(
120
      'view' => NULL,
121
      'options' => NULL,
122
      'row' => NULL,
123
      'field_alias' => NULL,
124
    ),
125
    'exposed_form' => array('view' => NULL, 'options' => NULL),
126
    'pager' => array(
127
      'view' => NULL,
128
      'options' => NULL,
129
      'tags' => array(),
130
      'quantity' => 10,
131
      'element' => 0,
132
      'parameters' => array(),
133
    ),
134
  );
135

    
136
  // Default view themes.
137
  $hooks['views_view_field'] = $base + array(
138
    'pattern' => 'views_view_field__',
139
    'variables' => array('view' => NULL, 'field' => NULL, 'row' => NULL),
140
  );
141
  $hooks['views_view_grouping'] = $base + array(
142
    'pattern' => 'views_view_grouping__',
143
    'variables' => array(
144
      'view' => NULL,
145
      'grouping' => NULL,
146
      'grouping_level' => NULL,
147
      'rows' => NULL,
148
      'title' => NULL,
149
    ),
150
  );
151

    
152
  $plugins = views_fetch_plugin_data();
153

    
154
  // Register theme functions for all style plugins.
155
  foreach ($plugins as $type => $info) {
156
    foreach ($info as $plugin => $def) {
157
      if (isset($def['theme']) && (!isset($def['register theme']) || !empty($def['register theme']))) {
158
        $hooks[$def['theme']] = array(
159
          'pattern' => $def['theme'] . '__',
160
          'file' => $def['theme file'],
161
          'path' => $def['theme path'],
162
          'variables' => $variables[$type],
163
        );
164

    
165
        $include = DRUPAL_ROOT . '/' . $def['theme path'] . '/' . $def['theme file'];
166
        if (file_exists($include)) {
167
          require_once $include;
168
        }
169

    
170
        if (!function_exists('theme_' . $def['theme'])) {
171
          $hooks[$def['theme']]['template'] = drupal_clean_css_identifier($def['theme']);
172
        }
173
      }
174
      if (isset($def['additional themes'])) {
175
        foreach ($def['additional themes'] as $theme => $theme_type) {
176
          if (empty($theme_type)) {
177
            $theme = $theme_type;
178
            $theme_type = $type;
179
          }
180

    
181
          $hooks[$theme] = array(
182
            'pattern' => $theme . '__',
183
            'file' => $def['theme file'],
184
            'path' => $def['theme path'],
185
            'variables' => $variables[$theme_type],
186
          );
187

    
188
          if (!function_exists('theme_' . $theme)) {
189
            $hooks[$theme]['template'] = drupal_clean_css_identifier($theme);
190
          }
191
        }
192
      }
193
    }
194
  }
195

    
196
  $hooks['views_form_views_form'] = $base + array(
197
    'render element' => 'form',
198
  );
199

    
200
  $hooks['views_exposed_form'] = $base + array(
201
    'template' => 'views-exposed-form',
202
    'pattern' => 'views_exposed_form__',
203
    'render element' => 'form',
204
  );
205

    
206
  $hooks['views_more'] = $base + array(
207
    'template' => 'views-more',
208
    'pattern' => 'views_more__',
209
    'variables' => array(
210
      'more_url' => NULL,
211
      'link_text' => 'more',
212
      'view' => NULL,
213
    ),
214
  );
215

    
216
  // Add theme suggestions which are part of modules.
217
  foreach (views_get_module_apis() as $info) {
218
    if (isset($info['template path'])) {
219
      $hooks += _views_find_module_templates($hooks, $info['template path']);
220
    }
221
  }
222
  return $hooks;
223
}
224

    
225
/**
226
 * Scans a directory of a module for template files.
227
 *
228
 * @param array $cache
229
 *   The existing cache of theme hooks to test against.
230
 * @param string $path
231
 *   The path to search.
232
 *
233
 * @see drupal_find_theme_templates()
234
 */
235
function _views_find_module_templates($cache, $path) {
236
  $templates = array();
237
  $regex = '/\.tpl\.php$/';
238

    
239
  // Because drupal_system_listing works the way it does, we check for real
240
  // templates separately from checking for patterns.
241
  $files = drupal_system_listing($regex, $path, 'name', 0);
242
  foreach ($files as $template => $file) {
243
    // Chop off the remaining extensions if there are any. $template already
244
    // has the rightmost extension removed, but there might still be more,
245
    // such as with .tpl.php, which still has .tpl in $template at this point.
246
    if (($pos = strpos($template, '.')) !== FALSE) {
247
      $template = substr($template, 0, $pos);
248
    }
249
    // Transform - in filenames to _ to match function naming scheme
250
    // for the purposes of searching.
251
    $hook = strtr($template, '-', '_');
252
    if (isset($cache[$hook])) {
253
      $templates[$hook] = array(
254
        'template' => $template,
255
        'path' => dirname($file->filename),
256
        'includes' => isset($cache[$hook]['includes']) ? $cache[$hook]['includes'] : NULL,
257
      );
258
    }
259
    // Ensure that the pattern is maintained from base themes to its sub-themes.
260
    // Each sub-theme will have their templates scanned so the pattern must be
261
    // held for subsequent runs.
262
    if (isset($cache[$hook]['pattern'])) {
263
      $templates[$hook]['pattern'] = $cache[$hook]['pattern'];
264
    }
265
  }
266

    
267
  $patterns = array_keys($files);
268

    
269
  foreach ($cache as $hook => $info) {
270
    if (!empty($info['pattern'])) {
271
      // Transform _ in pattern to - to match file naming scheme
272
      // for the purposes of searching.
273
      $pattern = strtr($info['pattern'], '_', '-');
274

    
275
      $matches = preg_grep('/^' . $pattern . '/', $patterns);
276
      if ($matches) {
277
        foreach ($matches as $match) {
278
          $file = substr($match, 0, strpos($match, '.'));
279
          // Put the underscores back in for the hook name and register this
280
          // pattern.
281
          $templates[strtr($file, '-', '_')] = array(
282
            'template' => $file,
283
            'path' => dirname($files[$match]->uri),
284
            'variables' => isset($info['variables']) ? $info['variables'] : NULL,
285
            'render element' => isset($info['render element']) ? $info['render element'] : NULL,
286
            'base hook' => $hook,
287
            'includes' => isset($info['includes']) ? $info['includes'] : NULL,
288
          );
289
        }
290
      }
291
    }
292
  }
293

    
294
  return $templates;
295
}
296

    
297
/**
298
 * Returns a list of plugins and metadata about them.
299
 *
300
 * @return array
301
 *   An array keyed by PLUGIN_TYPE:PLUGIN_NAME, like 'display:page' or
302
 *   'pager:full', containing an array with the following keys:
303
 *   - title: The plugin's title.
304
 *   - type: The plugin type.
305
 *   - module: The module providing the plugin.
306
 *   - views: An array of enabled Views that are currently using this plugin,
307
 *     keyed by machine name.
308
 */
309
function views_plugin_list() {
310
  $plugin_data = views_fetch_plugin_data();
311
  $plugins = array();
312
  foreach (views_get_enabled_views() as $view) {
313
    foreach ($view->display as $display_id => $display) {
314
      foreach ($plugin_data as $type => $info) {
315
        if ($type == 'display' && isset($display->display_plugin)) {
316
          $name = $display->display_plugin;
317
        }
318
        elseif (isset($display->display_options["{$type}_plugin"])) {
319
          $name = $display->display_options["{$type}_plugin"];
320
        }
321
        elseif (isset($display->display_options[$type]['type'])) {
322
          $name = $display->display_options[$type]['type'];
323
        }
324
        else {
325
          continue;
326
        }
327

    
328
        // Key first by the plugin type, then the name.
329
        $key = $type . ':' . $name;
330
        // Add info for this plugin.
331
        if (!isset($plugins[$key])) {
332
          $plugins[$key] = array(
333
            'type' => $type,
334
            'title' => check_plain($info[$name]['title']),
335
            'module' => check_plain($info[$name]['module']),
336
            'views' => array(),
337
          );
338
        }
339

    
340
        // Add this view to the list for this plugin.
341
        $plugins[$key]['views'][$view->name] = $view->name;
342
      }
343
    }
344
  }
345
  return $plugins;
346
}
347

    
348
/**
349
 * Preprocess a node.
350
 *
351
 * A theme preprocess function to automatically allow view-based node
352
 * templates if called from a view.
353
 *
354
 * The 'modules/node.views.inc' file is a better place for this, but
355
 * we haven't got a chance to load that file before Drupal builds the
356
 * node portion of the theme registry.
357
 */
358
function views_preprocess_node(&$vars) {
359
  // The 'view' attribute of the node is added in views_preprocess_node()
360
  if (!empty($vars['node']->view) && !empty($vars['node']->view->name)) {
361
    $vars['view'] = $vars['node']->view;
362
    $vars['theme_hook_suggestions'][] = 'node__view__' . $vars['node']->view->name;
363
    if (!empty($vars['node']->view->current_display)) {
364
      $vars['theme_hook_suggestions'][] = 'node__view__' . $vars['node']->view->name . '__' . $vars['node']->view->current_display;
365

    
366
      // If a node is being rendered in a view, and the view does not have a
367
      // path, prevent drupal from accidentally setting the $page variable.
368
      if ($vars['page'] && $vars['view_mode'] == 'full' && !$vars['view']->display_handler->has_path()) {
369
        $vars['page'] = FALSE;
370
      }
371
    }
372
  }
373

    
374
  // Allow to alter comments and links based on the settings in the row plugin.
375
  if (!empty($vars['view']->style_plugin->row_plugin) && get_class($vars['view']->style_plugin->row_plugin) == 'views_plugin_row_node_view') {
376
    node_row_node_view_preprocess_node($vars);
377
  }
378
}
379

    
380
/**
381
 * Preprocess a comment.
382
 *
383
 * A theme preprocess function to automatically allow view-based node
384
 * templates if called from a view.
385
 */
386
function views_preprocess_comment(&$vars) {
387
  // The 'view' attribute of the node is added in
388
  // template_preprocess_views_view_row_comment().
389
  if (!empty($vars['node']->view) && !empty($vars['node']->view->name)) {
390
    $vars['view'] = &$vars['node']->view;
391
    $vars['theme_hook_suggestions'][] = 'comment__view__' . $vars['node']->view->name;
392
    if (!empty($vars['node']->view->current_display)) {
393
      $vars['theme_hook_suggestions'][] = 'comment__view__' . $vars['node']->view->name . '__' . $vars['node']->view->current_display;
394
    }
395
  }
396
}
397

    
398
/**
399
 * Implements hook_permission().
400
 */
401
function views_permission() {
402
  return array(
403
    'administer views' => array(
404
      'title' => t('Administer views'),
405
      'description' => t('Access the views administration pages.'),
406
      'restrict access' => TRUE,
407
    ),
408
    'access all views' => array(
409
      'title' => t('Bypass views access control'),
410
      'description' => t('Bypass access control when accessing views.'),
411
      'restrict access' => TRUE,
412
    ),
413
  );
414
}
415

    
416
/**
417
 * Implements hook_menu().
418
 */
419
function views_menu() {
420
  $items = array();
421
  $items['views/ajax'] = array(
422
    'title' => 'Views',
423
    'page callback' => 'views_ajax',
424
    'theme callback' => 'ajax_base_page_theme',
425
    'delivery callback' => 'ajax_deliver',
426
    'access callback' => TRUE,
427
    'description' => 'Ajax callback for view loading.',
428
    'type' => MENU_CALLBACK,
429
    'file' => 'includes/ajax.inc',
430
  );
431
  // Path is not admin/structure/views due to menu complications with the
432
  // wildcards from the generic ajax callback.
433
  $items['admin/views/ajax/autocomplete/user'] = array(
434
    'page callback' => 'views_ajax_autocomplete_user',
435
    'theme callback' => 'ajax_base_page_theme',
436
    'access callback' => 'user_access',
437
    'access arguments' => array('access user profiles'),
438
    'type' => MENU_CALLBACK,
439
    'file' => 'includes/ajax.inc',
440
  );
441
  // Define another taxonomy autocomplete because the default one of drupal
442
  // does not support a vid a argument anymore.
443
  $items['admin/views/ajax/autocomplete/taxonomy'] = array(
444
    'page callback' => 'views_ajax_autocomplete_taxonomy',
445
    'theme callback' => 'ajax_base_page_theme',
446
    'access callback' => 'user_access',
447
    'access arguments' => array('access content'),
448
    'type' => MENU_CALLBACK,
449
    'file' => 'includes/ajax.inc',
450
  );
451
  return $items;
452
}
453

    
454
/**
455
 * Implements hook_menu_alter().
456
 */
457
function views_menu_alter(&$callbacks) {
458
  $our_paths = array();
459
  $views = views_get_applicable_views('uses hook menu');
460
  foreach ($views as $data) {
461
    list($view, $display_id) = $data;
462
    $result = $view->execute_hook_menu($display_id, $callbacks);
463
    if (is_array($result)) {
464
      // The menu system doesn't support having two otherwise identical paths
465
      // with different placeholders. So we want to remove the existing items
466
      // from the menu whose paths would conflict with ours. First, we must find
467
      // any existing menu items that may conflict. We use a regular expression
468
      // because we don't know what placeholders they might use. Note that we
469
      // first construct the regex itself by replacing %views_arg in the display
470
      // path, then we use this constructed regex (which will be something like
471
      // '#^(foo/%[^/]*/bar)$#') to search through the existing paths.
472
      $regex = '#^(' . preg_replace('#%views_arg#', '%[^/]*', implode('|', array_keys($result))) . ')$#';
473
      $matches = preg_grep($regex, array_keys($callbacks));
474

    
475
      // Remove any conflicting items that were found.
476
      foreach ($matches as $path) {
477
        // Don't remove the paths we just added!
478
        if (!isset($our_paths[$path])) {
479
          unset($callbacks[$path]);
480
        }
481
      }
482
      foreach ($result as $path => $item) {
483
        if (!isset($callbacks[$path])) {
484
          // Add a new item, possibly replacing (and thus effectively
485
          // overriding) one that we removed above.
486
          $callbacks[$path] = $item;
487
        }
488
        else {
489
          // This item already exists, so it must be one that we added.
490
          // We change the various callback arguments to pass an array
491
          // of possible display IDs instead of a single ID.
492
          $callbacks[$path]['page arguments'][1] = (array) $callbacks[$path]['page arguments'][1];
493
          $callbacks[$path]['page arguments'][1][] = $display_id;
494
          $callbacks[$path]['access arguments'][] = $item['access arguments'][0];
495
          $callbacks[$path]['load arguments'][1] = (array) $callbacks[$path]['load arguments'][1];
496
          $callbacks[$path]['load arguments'][1][] = $display_id;
497
        }
498
        $our_paths[$path] = TRUE;
499
      }
500
    }
501
  }
502

    
503
  // Save memory: Destroy those views.
504
  foreach ($views as $data) {
505
    list($view, $display_id) = $data;
506
    $view->destroy();
507
  }
508
}
509

    
510
/**
511
 * Load a views argument.
512
 *
513
 * Helper function for menu loading. This will automatically be
514
 * called in order to 'load' a views argument; primarily it
515
 * will be used to perform validation.
516
 *
517
 * @param string $value
518
 *   The actual value passed.
519
 * @param string $name
520
 *   The name of the view. This needs to be specified in the 'load function'
521
 *   of the menu entry.
522
 * @param string $display_id
523
 *   The display id that will be loaded for this menu item.
524
 * @param int $index
525
 *   The menu argument index. This counts from 1.
526
 */
527
function views_arg_load($value, $name, $display_id, $index) {
528
  static $views = array();
529

    
530
  $display_ids = is_array($display_id) ? $display_id : array($display_id);
531
  $display_id = reset($display_ids);
532

    
533
  foreach ($display_ids as $id) {
534
    // Make sure we haven't already loaded this views argument for a similar
535
    // menu item elsewhere. Since access is always checked for the current user,
536
    // we are sure that the static cache contains valid entries.
537
    $key = $name . ':' . $id . ':' . $value . ':' . $index;
538
    if (isset($views[$key])) {
539
      return $views[$key];
540
    }
541
    // Lazy load the view object to avoid unnecessary work.
542
    if (!isset($view)) {
543
      $view = views_get_view($name);
544
    }
545
    // Pick the first display we have access to.
546
    if ($view && count($display_ids) > 1 && $view->access($id)) {
547
      $display_id = $id;
548
      break;
549
    }
550
  }
551

    
552
  if ($view) {
553
    $view->set_display($display_id);
554
    $view->init_handlers();
555

    
556
    $ids = array_keys($view->argument);
557

    
558
    $indexes = array();
559
    $path = explode('/', $view->get_path());
560

    
561
    foreach ($path as $id => $piece) {
562
      if ($piece == '%' && !empty($ids)) {
563
        $indexes[$id] = array_shift($ids);
564
      }
565
    }
566

    
567
    if (isset($indexes[$index])) {
568
      if (isset($view->argument[$indexes[$index]])) {
569
        $arg = $view->argument[$indexes[$index]]->validate_argument($value) ? $value : FALSE;
570
        $view->destroy();
571

    
572
        // Store the output in case we load this same menu item again.
573
        $views[$key] = $arg;
574
        return $arg;
575
      }
576
    }
577
    $view->destroy();
578
  }
579
}
580

    
581
/**
582
 * Page callback: Displays a page view, given a name and display id.
583
 *
584
 * @param string $name
585
 *   The name of a view.
586
 * @param string $display_id
587
 *   The display id of a view.
588
 *
589
 * @return string|int
590
 *   Either the HTML of a fully-executed view, or MENU_NOT_FOUND.
591
 */
592
function views_page($name, $display_id) {
593
  $args = func_get_args();
594
  // Remove $name and $display_id from the arguments.
595
  array_shift($args);
596
  array_shift($args);
597

    
598
  // Load the view and render it.
599
  if ($view = views_get_view($name)) {
600
    return $view->execute_display($display_id, $args);
601
  }
602

    
603
  // Fallback; if we get here no view was found or handler was not valid.
604
  return MENU_NOT_FOUND;
605
}
606

    
607
/**
608
 * Implements hook_page_alter().
609
 */
610
function views_page_alter(&$page) {
611
  // If the main content of this page contains a view, attach its contextual
612
  // links to the overall page array. This allows them to be rendered directly
613
  // next to the page title.
614
  $view = views_get_page_view();
615
  if (!empty($view)) {
616
    // If a module is still putting in the display like we used to, catch that.
617
    if (is_subclass_of($view, 'views_plugin_display')) {
618
      $view = $view->view;
619
    }
620

    
621
    views_add_contextual_links($page, 'page', $view, $view->current_display);
622
  }
623
}
624

    
625
/**
626
 * Implements MODULE_preprocess_HOOK() for html.tpl.php.
627
 */
628
function views_preprocess_html(&$variables) {
629
  // If the page contains a view as its main content, contextual links may have
630
  // been attached to the page as a whole; for example, by views_page_alter().
631
  // This allows them to be associated with the page and rendered by default
632
  // next to the page title (which we want). However, it also causes the
633
  // Contextual Links module to treat the wrapper for the entire page (i.e.,
634
  // the <body> tag) as the HTML element that these contextual links are
635
  // associated with. This we don't want; for better visual highlighting, we
636
  // prefer a smaller region to be chosen. The region we prefer differs from
637
  // theme to theme and depends on the details of the theme's markup in
638
  // page.tpl.php, so we can only find it using JavaScript. We therefore remove
639
  // the "contextual-links-region" class from the <body> tag here and add
640
  // JavaScript that will insert it back in the correct place.
641
  if (!empty($variables['page']['#views_contextual_links_info'])) {
642
    $key = array_search('contextual-links-region', $variables['classes_array']);
643
    if ($key !== FALSE) {
644
      $variables['classes_array'] = array_diff($variables['classes_array'], array('contextual-links-region'));
645
      // Add the JavaScript, with a group and weight such that it will run
646
      // before modules/contextual/contextual.js.
647
      drupal_add_js(drupal_get_path('module', 'views') . '/js/views-contextual.js', array('group' => JS_LIBRARY, 'weight' => -1));
648
    }
649
  }
650
}
651

    
652
/**
653
 * Implements hook_preprocess_HOOK() for page.tpl.php.
654
 */
655
function views_preprocess_page(&$variables) {
656
  // If the page contains a view as its main content, contextual links may have
657
  // been attached to the page as a whole; for example, by views_page_alter().
658
  // This allows them to be associated with the page and rendered by default
659
  // next to the page title (which we want). However, it also causes the
660
  // Contextual Links module to treat the wrapper for the entire page (i.e.,
661
  // the <body> tag) as the HTML element that these contextual links are
662
  // associated with. This we don't want; for better visual highlighting, we
663
  // prefer a smaller region to be chosen. The region we prefer differs from
664
  // theme to theme and depends on the details of the theme's markup in
665
  // page.tpl.php, so we can only find it using JavaScript. We therefore remove
666
  // the "contextual-links-region" class from the <body> tag here and add
667
  // JavaScript that will insert it back in the correct place.
668
  if (!empty($variables['page']['#views_contextual_links_info'])) {
669
    $variables['classes_array'] = array_diff($variables['classes_array'], array('contextual-links-region'));
670
  }
671
}
672

    
673
/**
674
 * Implements hook_contextual_links_view_alter().
675
 */
676
function views_contextual_links_view_alter(&$element, $items) {
677
  // If we are rendering views-related contextual links attached to the overall
678
  // page array, add a class to the list of contextual links. This will be used
679
  // by the JavaScript added in views_preprocess_html().
680
  if (!empty($element['#element']['#views_contextual_links_info']) && !empty($element['#element']['#type']) && $element['#element']['#type'] == 'page') {
681
    $element['#attributes']['class'][] = 'views-contextual-links-page';
682
  }
683
}
684

    
685
/**
686
 * Implements hook_block_info().
687
 */
688
function views_block_info() {
689
  // Try to avoid instantiating all the views just to get the blocks info.
690
  views_include('cache');
691
  $cache = views_cache_get('views_block_items', TRUE);
692
  if ($cache && is_array($cache->data)) {
693
    return $cache->data;
694
  }
695

    
696
  $items = array();
697
  $views = views_get_all_views();
698
  foreach ($views as $view) {
699
    // Disabled views get nothing.
700
    if (!empty($view->disabled)) {
701
      continue;
702
    }
703

    
704
    $view->init_display();
705
    foreach ($view->display as $display_id => $display) {
706

    
707
      if (isset($display->handler) && !empty($display->handler->definition['uses hook block'])) {
708
        $result = $display->handler->execute_hook_block_list();
709
        if (is_array($result)) {
710
          $items = array_merge($items, $result);
711
        }
712
      }
713

    
714
      if (isset($display->handler) && $display->handler->get_option('exposed_block')) {
715
        $result = $display->handler->get_special_blocks();
716
        if (is_array($result)) {
717
          $items = array_merge($items, $result);
718
        }
719
      }
720
    }
721
  }
722

    
723
  // block.module has a delta length limit of 32, but our deltas can
724
  // unfortunately be longer because view names can be 32 and display IDs
725
  // can also be 32. So for very long deltas, change to md5 hashes.
726
  $hashes = array();
727

    
728
  // Get the keys because we're modifying the array and we don't want to
729
  // confuse PHP too much.
730
  $keys = array_keys($items);
731
  foreach ($keys as $delta) {
732
    if (strlen($delta) >= 32) {
733
      $hash = md5($delta);
734
      $hashes[$hash] = $delta;
735
      $items[$hash] = $items[$delta];
736
      unset($items[$delta]);
737
    }
738
  }
739

    
740
  // Only save hashes if they have changed.
741
  $old_hashes = variable_get('views_block_hashes', array());
742
  if ($hashes != $old_hashes) {
743
    variable_set('views_block_hashes', $hashes);
744
  }
745
  // Save memory: Destroy those views.
746
  foreach ($views as $view) {
747
    $view->destroy();
748
  }
749

    
750
  views_cache_set('views_block_items', $items, TRUE);
751

    
752
  return $items;
753
}
754

    
755
/**
756
 * Implements hook_block_configure().
757
 */
758
function views_block_configure($delta = '') {
759
  // If there's no Views UI module there's nothing to link to.
760
  if (!module_exists('views_ui')) {
761
    return array();
762
  }
763

    
764
  // If the user doesn't have access to edit the view then don't bother with
765
  // anything else.
766
  if (!user_access('administer views')) {
767
    return array();
768
  }
769

    
770
  // If this is 32, this should be an md5 hash.
771
  if (strlen($delta) == 32) {
772
    $hashes = variable_get('views_block_hashes', array());
773
    if (!empty($hashes[$delta])) {
774
      $delta = $hashes[$delta];
775
    }
776
  }
777

    
778
  // Some added precaution in case the delta is missing values.
779
  list($view_name, $display_id) = explode('-', $delta, 2) + array('', '');
780

    
781
  // If the view name or display ID can't be found, there's something wrong.
782
  if ($view_name === '' || $display_id === '') {
783
    return array();
784
  }
785

    
786
  $view = views_get_view($view_name);
787
  if (empty($view)) {
788
    return array();
789
  }
790

    
791
  if (!isset($view->display[$display_id])) {
792
    return array();
793
  }
794

    
795
  /** @var \views_display $display */
796
  $display = $view->display[$display_id];
797

    
798
  $view_label = $view->get_human_name();
799
  $display_label = $display->display_title;
800

    
801
  $path = "admin/structure/views/view/$view_name/edit/$display_id";
802

    
803
  return array(
804
    'fieldset' => array(
805
      '#type' => 'fieldset',
806
      '#title' => t('Configure this views display'),
807
      'content' => array(
808
        '#markup' => l($view_label . ' - ' . $display_label, $path),
809
      ),
810
    ),
811
  );
812
}
813

    
814
/**
815
 * Implements hook_block_view().
816
 */
817
function views_block_view($delta) {
818
  // If this is 32, this should be an md5 hash.
819
  if (strlen($delta) == 32) {
820
    $hashes = variable_get('views_block_hashes', array());
821
    if (!empty($hashes[$delta])) {
822
      $delta = $hashes[$delta];
823
    }
824
  }
825

    
826
  // This indicates it's a special one.
827
  if (substr($delta, 0, 1) == '-') {
828
    list($nothing, $type, $name, $display_id) = explode('-', $delta);
829
    // Put the - back on.
830
    $type = '-' . $type;
831
    if ($view = views_get_view($name)) {
832
      if ($view->access($display_id)) {
833
        $view->set_display($display_id);
834
        if (isset($view->display_handler)) {
835
          $output = $view->display_handler->view_special_blocks($type);
836
          // Before returning the block output, convert it to a renderable
837
          // array with contextual links.
838
          views_add_block_contextual_links($output, $view, $display_id, 'special_block_' . $type);
839
          $view->destroy();
840
          return $output;
841
        }
842
      }
843
      $view->destroy();
844
    }
845
  }
846

    
847
  // If the delta doesn't contain valid data return nothing.
848
  $explode = explode('-', $delta);
849
  if (count($explode) != 2) {
850
    return;
851
  }
852
  list($name, $display_id) = $explode;
853
  // Load the view.
854
  if ($view = views_get_view($name)) {
855
    if ($view->access($display_id)) {
856
      $output = $view->execute_display($display_id);
857
      // Before returning the block output, convert it to a renderable array
858
      // with contextual links.
859
      views_add_block_contextual_links($output, $view, $display_id);
860
      $view->destroy();
861
      return $output;
862
    }
863
    $view->destroy();
864
  }
865
}
866

    
867
/**
868
 * Converts Views block content to a renderable array with contextual links.
869
 *
870
 * @param array $block
871
 *   An array representing the block, with the same structure as the return
872
 *   value of hook_block_view(). This will be modified so as to force
873
 *   $block['content'] to be a renderable array, containing the optional
874
 *   '#contextual_links' property (if there are any contextual links associated
875
 *   with the block).
876
 * @param object $view
877
 *   The view that was used to generate the block content.
878
 * @param string $display_id
879
 *   The ID of the display within the view that was used to generate the block
880
 *   content.
881
 * @param string $block_type
882
 *   The type of the block. If it's block it's a regular views display,
883
 *   but 'special_block_-exp' exist as well.
884
 */
885
function views_add_block_contextual_links(&$block, $view, $display_id, $block_type = 'block') {
886
  // Do not add contextual links to an empty block.
887
  if (!empty($block['content'])) {
888
    // Contextual links only work on blocks whose content is a renderable
889
    // array, so if the block contains a string of already-rendered markup,
890
    // convert it to an array.
891
    if (is_string($block['content'])) {
892
      $block['content'] = array('#markup' => $block['content']);
893
    }
894
    // Add the contextual links.
895
    views_add_contextual_links($block['content'], $block_type, $view, $display_id);
896
  }
897
}
898

    
899
/**
900
 * Adds contextual links associated with a view display to a renderable array.
901
 *
902
 * This function should be called when a view is being rendered in a particular
903
 * location and you want to attach the appropriate contextual links (e.g.,
904
 * links for editing the view) to it.
905
 *
906
 * The function operates by checking the view's display plugin to see if it has
907
 * defined any contextual links that are intended to be displayed in the
908
 * requested location; if so, it attaches them. The contextual links intended
909
 * for a particular location are defined by the 'contextual links' and
910
 * 'contextual links locations' properties in hook_views_plugins() and
911
 * hook_views_plugins_alter(); as a result, these hook implementations have
912
 * full control over where and how contextual links are rendered for each
913
 * display.
914
 *
915
 * In addition to attaching the contextual links to the passed-in array (via
916
 * the standard #contextual_links property), this function also attaches
917
 * additional information via the #views_contextual_links_info property. This
918
 * stores an array whose keys are the names of each module that provided
919
 * views-related contextual links (same as the keys of the #contextual_links
920
 * array itself) and whose values are themselves arrays whose keys ('location',
921
 * 'view_name', and 'view_display_id') store the location, name of the view,
922
 * and display ID that were passed in to this function. This allows you to
923
 * access information about the contextual links and how they were generated in
924
 * a variety of contexts where you might be manipulating the renderable array
925
 * later on (for example, alter hooks which run later during the same page
926
 * request).
927
 *
928
 * @param array $render_element
929
 *   The renderable array to which contextual links will be added. This array
930
 *   should be suitable for passing in to drupal_render() and will normally
931
 *   contain a representation of the view display whose contextual links are
932
 *   being requested.
933
 * @param string $location
934
 *   The location in which the calling function intends to render the view and
935
 *   its contextual links. The core system supports three options for this
936
 *   parameter:
937
 *   - 'block': Used when rendering a block which contains a view. This
938
 *     retrieves any contextual links intended to be attached to the block
939
 *     itself.
940
 *   - 'page': Used when rendering the main content of a page which contains a
941
 *     view. This retrieves any contextual links intended to be attached to the
942
 *     page itself (for example, links which are displayed directly next to the
943
 *     page title).
944
 *   - 'view': Used when rendering the view itself, in any context. This
945
 *     retrieves any contextual links intended to be attached directly to the
946
 *     view.
947
 *   If you are rendering a view and its contextual links in another location,
948
 *   you can pass in a different value for this parameter. However, you will
949
 *   also need to use hook_views_plugins() or hook_views_plugins_alter() to
950
 *   declare, via the 'contextual links locations' array key, which view
951
 *   displays support having their contextual links rendered in the location
952
 *   you have defined.
953
 * @param object $view
954
 *   The view whose contextual links will be added.
955
 * @param string $display_id
956
 *   The ID of the display within $view whose contextual links will be added.
957
 *
958
 * @see hook_views_plugins()
959
 * @see views_block_view()
960
 * @see views_page_alter()
961
 * @see template_preprocess_views_view()
962
 */
963
function views_add_contextual_links(&$render_element, $location, $view, $display_id) {
964
  // Do not do anything if the view is configured to hide its administrative
965
  // links.
966
  if (empty($view->hide_admin_links)) {
967
    // Also do not do anything if the display plugin has not defined any
968
    // contextual links that are intended to be displayed in the requested
969
    // location.
970
    $plugin = views_fetch_plugin_data('display', $view->display[$display_id]->display_plugin);
971
    // If contextual links locations are not set, provide a sane default. (To
972
    // avoid displaying any contextual links at all, a display plugin can still
973
    // set 'contextual links locations' to, e.g., an empty array).
974
    $plugin += array('contextual links locations' => array('view'));
975
    // On exposed_forms blocks contextual links should always be visible.
976
    $plugin['contextual links locations'][] = 'special_block_-exp';
977
    $has_links = !empty($plugin['contextual links']) && !empty($plugin['contextual links locations']);
978
    if ($has_links && in_array($location, $plugin['contextual links locations'])) {
979
      foreach ($plugin['contextual links'] as $module => $link) {
980
        $args = array();
981
        $valid = TRUE;
982
        if (!empty($link['argument properties'])) {
983
          foreach ($link['argument properties'] as $property) {
984
            // If the plugin is trying to create an invalid contextual link
985
            // (for example, "path/to/{$view->property}", where $view->property
986
            // does not exist), we cannot construct the link, so we skip it.
987
            if (!property_exists($view, $property)) {
988
              $valid = FALSE;
989
              break;
990
            }
991
            else {
992
              $args[] = $view->{$property};
993
            }
994
          }
995
        }
996
        // If the link was valid, attach information about it to the renderable
997
        // array.
998
        if ($valid) {
999
          $render_element['#contextual_links'][$module] = array($link['parent path'], $args);
1000
          $render_element['#views_contextual_links_info'][$module] = array(
1001
            'location' => $location,
1002
            'view' => $view,
1003
            'view_name' => $view->name,
1004
            'view_display_id' => $display_id,
1005
          );
1006
        }
1007
      }
1008
    }
1009
  }
1010
}
1011

    
1012
/**
1013
 * Returns an array of language names.
1014
 *
1015
 * This is a one to one copy of locale_language_list because we can't rely on
1016
 * enabled locale module.
1017
 *
1018
 * @param string $field
1019
 *   Either 'name' for localized names in current language or 'native' for
1020
 *   native names.
1021
 * @param bool $all
1022
 *   Boolean to return all languages or only enabled ones.
1023
 *
1024
 * @see locale_language_list()
1025
 */
1026
function views_language_list($field = 'name', $all = FALSE) {
1027
  if ($all) {
1028
    $languages = language_list();
1029
  }
1030
  else {
1031
    $languages = language_list('enabled');
1032
    $languages = $languages[1];
1033
  }
1034
  $list = array();
1035
  foreach ($languages as $language) {
1036
    $list[$language->language] = ($field == 'name') ? t($language->name) : $language->$field;
1037
  }
1038
  return $list;
1039
}
1040

    
1041
/**
1042
 * Implements hook_flush_caches().
1043
 */
1044
function views_flush_caches() {
1045
  return array('cache_views', 'cache_views_data');
1046
}
1047

    
1048
/**
1049
 * Implements hook_field_create_instance().
1050
 */
1051
function views_field_create_instance($instance) {
1052
  cache_clear_all('*', 'cache_views', TRUE);
1053
  cache_clear_all('*', 'cache_views_data', TRUE);
1054
}
1055

    
1056
/**
1057
 * Implements hook_field_update_instance().
1058
 */
1059
function views_field_update_instance($instance, $prior_instance) {
1060
  cache_clear_all('*', 'cache_views', TRUE);
1061
  cache_clear_all('*', 'cache_views_data', TRUE);
1062
}
1063

    
1064
/**
1065
 * Implements hook_field_delete_instance().
1066
 */
1067
function views_field_delete_instance($instance) {
1068
  cache_clear_all('*', 'cache_views', TRUE);
1069
  cache_clear_all('*', 'cache_views_data', TRUE);
1070
}
1071

    
1072
/**
1073
 * Invalidate the views cache, forcing a rebuild on the next grab of table data.
1074
 *
1075
 * @param string $cid
1076
 *   The cache identifier we want to clear. If no given, it will default to '*'
1077
 *   which will clear the entire cache_views bin.
1078
 */
1079
function views_invalidate_cache($cid = '*') {
1080
  // Clear the views cache.
1081
  cache_clear_all($cid, 'cache_views', TRUE);
1082

    
1083
  // Clear the page and block cache.
1084
  cache_clear_all();
1085

    
1086
  // Set the menu as needed to be rebuilt.
1087
  variable_set('menu_rebuild_needed', TRUE);
1088

    
1089
  // Allow modules to respond to the Views cache being cleared.
1090
  module_invoke_all('views_invalidate_cache', $cid);
1091
}
1092

    
1093
/**
1094
 * Access callback to determine if the user can import Views.
1095
 *
1096
 * View imports require an additional access check because they are PHP
1097
 * code and PHP is more locked down than administer views.
1098
 */
1099
function views_import_access() {
1100
  return user_access('administer views') && user_access('use PHP for settings');
1101
}
1102

    
1103
/**
1104
 * Determine if the logged in user has access to a view.
1105
 *
1106
 * This function should only be called from a menu hook or some other
1107
 * embedded source. Each argument is the result of a call to
1108
 * views_plugin_access::get_access_callback() which is then used
1109
 * to determine if that display is accessible. If *any* argument
1110
 * is accessible, then the view is accessible.
1111
 */
1112
function views_access() {
1113
  $args = func_get_args();
1114
  foreach ($args as $arg) {
1115
    if ($arg === TRUE) {
1116
      return TRUE;
1117
    }
1118

    
1119
    if (!is_array($arg)) {
1120
      continue;
1121
    }
1122

    
1123
    list($callback, $arguments) = $arg;
1124
    $arguments = $arguments ? $arguments : array();
1125
    // Bring dynamic arguments to the access callback.
1126
    foreach ($arguments as $key => $value) {
1127
      if (is_int($value) && isset($args[$value])) {
1128
        $arguments[$key] = $args[$value];
1129
      }
1130
    }
1131
    if (function_exists($callback) && call_user_func_array($callback, $arguments)) {
1132
      return TRUE;
1133
    }
1134
  }
1135

    
1136
  return FALSE;
1137
}
1138

    
1139
/**
1140
 * Access callback for the views_plugin_access_perm access plugin.
1141
 *
1142
 * Determine if the specified user has access to a view on the basis of
1143
 * permissions. If the $account argument is omitted, the current user
1144
 * is used.
1145
 */
1146
function views_check_perm($perms, $account = NULL) {
1147
  // Backward compatibility to ensure also a single permission string is
1148
  // properly processed.
1149
  $perms = is_array($perms) ? $perms : array($perms);
1150
  if (user_access('access all views', $account)) {
1151
    return TRUE;
1152
  }
1153
  // Perms are handled as OR, as soon one permission allows access TRUE is
1154
  // returned.
1155
  foreach ($perms as $perm) {
1156
    if (user_access($perm, $account)) {
1157
      return TRUE;
1158
    }
1159
  }
1160
  return FALSE;
1161
}
1162

    
1163
/**
1164
 * Access callback for the views_plugin_access_role access plugin.
1165
 *
1166
 * Determine if the specified user has access to a view on the basis of any of
1167
 * the requested roles. If the $account argument is omitted, the current user
1168
 * is used.
1169
 */
1170
function views_check_roles($rids, $account = NULL) {
1171
  global $user;
1172
  $account = isset($account) ? $account : $user;
1173
  $roles = array_keys($account->roles);
1174
  $roles[] = $account->uid ? DRUPAL_AUTHENTICATED_RID : DRUPAL_ANONYMOUS_RID;
1175
  return user_access('access all views', $account) || array_intersect(array_filter($rids), $roles);
1176
}
1177

    
1178
/**
1179
 * Set page view.
1180
 *
1181
 * Set the current 'page view' that is being displayed so that it is easy
1182
 * for other modules or the theme to identify.
1183
 */
1184
function &views_set_page_view($view = NULL) {
1185
  static $cache = NULL;
1186
  if (isset($view)) {
1187
    $cache = $view;
1188
  }
1189

    
1190
  return $cache;
1191
}
1192

    
1193
/**
1194
 * Get page view.
1195
 *
1196
 * Find out what, if any, page view is currently in use. Please note that
1197
 * this returns a reference, so be careful! You can unintentionally modify the
1198
 * $view object.
1199
 *
1200
 * @return view
1201
 *   A fully formed, empty $view object.
1202
 */
1203
function &views_get_page_view() {
1204
  return views_set_page_view();
1205
}
1206

    
1207
/**
1208
 * Set current view.
1209
 *
1210
 * Set the current 'current view' that is being built/rendered so that it is
1211
 * easy for other modules or items in drupal_eval to identify.
1212
 *
1213
 * @return view
1214
 *   The current view.
1215
 */
1216
function &views_set_current_view($view = NULL) {
1217
  static $cache = NULL;
1218
  if (isset($view)) {
1219
    $cache = $view;
1220
  }
1221

    
1222
  return $cache;
1223
}
1224

    
1225
/**
1226
 * Get current view.
1227
 *
1228
 * Find out what, if any, current view is currently in use. Please note that
1229
 * this returns a reference, so be careful! You can unintentionally modify the
1230
 * $view object.
1231
 *
1232
 * @return view
1233
 *   The current view.
1234
 */
1235
function &views_get_current_view() {
1236
  return views_set_current_view();
1237
}
1238

    
1239
/**
1240
 * Include views .inc files as necessary.
1241
 */
1242
function views_include($file) {
1243
  static $views_path;
1244
  if (!isset($views_path)) {
1245
    $views_path = DRUPAL_ROOT . '/' . drupal_get_path('module', 'views');
1246
  }
1247
  include_once $views_path . '/includes/' . $file . '.inc';
1248
}
1249

    
1250
/**
1251
 * Load views files on behalf of modules.
1252
 */
1253
function views_module_include($api, $reset = FALSE) {
1254
  if ($reset) {
1255
    $cache = &drupal_static('ctools_plugin_api_info');
1256
    if (isset($cache['views']['views'])) {
1257
      unset($cache['views']['views']);
1258
    }
1259
  }
1260
  ctools_include('plugins');
1261
  return ctools_plugin_api_include('views', $api, views_api_minimum_version(), views_api_version());
1262
}
1263

    
1264
/**
1265
 * Get a list of modules that support the current views API.
1266
 */
1267
function views_get_module_apis($api = 'views', $reset = FALSE) {
1268
  if ($reset) {
1269
    $cache = &drupal_static('ctools_plugin_api_info');
1270
    if (isset($cache['views']['views'])) {
1271
      unset($cache['views']['views']);
1272
    }
1273
  }
1274
  ctools_include('plugins');
1275
  return ctools_plugin_api_info('views', $api, views_api_minimum_version(), views_api_version());
1276
}
1277

    
1278
/**
1279
 * Include views .css files.
1280
 */
1281
function views_add_css($file) {
1282
  // We set preprocess to FALSE because we are adding the files conditionally,
1283
  // and we don't want to generate duplicate cache files.
1284
  // @todo at some point investigate adding some files unconditionally and
1285
  // allowing preprocess.
1286
  drupal_add_css(drupal_get_path('module', 'views') . "/css/$file.css", array('preprocess' => FALSE));
1287
}
1288

    
1289
/**
1290
 * Include views .js files.
1291
 */
1292
function views_add_js($file) {
1293
  // If JavaScript has been disabled by the user, never add js files.
1294
  if (variable_get('views_no_javascript', FALSE)) {
1295
    return;
1296
  }
1297
  static $base = TRUE, $ajax = TRUE;
1298
  if ($base) {
1299
    drupal_add_js(drupal_get_path('module', 'views') . "/js/base.js");
1300
    $base = FALSE;
1301
  }
1302
  if ($ajax && in_array($file, array('ajax', 'ajax_view'))) {
1303
    drupal_add_library('system', 'drupal.ajax');
1304
    drupal_add_library('system', 'jquery.form');
1305
    $ajax = FALSE;
1306
  }
1307
  ctools_add_js($file, 'views');
1308
}
1309

    
1310
/**
1311
 * Load views files on behalf of modules.
1312
 */
1313
function views_include_handlers($reset = FALSE) {
1314
  static $finished = FALSE;
1315
  // Ensure this only gets run once.
1316
  if ($finished && !$reset) {
1317
    return;
1318
  }
1319

    
1320
  views_include('base');
1321
  views_include('handlers');
1322
  views_include('cache');
1323
  views_include('plugins');
1324
  views_module_include('views', $reset);
1325
  $finished = TRUE;
1326
}
1327

    
1328
/**
1329
 * Fetch a handler from the data cache.
1330
 *
1331
 * @param string $table
1332
 *   The name of the table this handler is from.
1333
 * @param string $field
1334
 *   The name of the field this handler is from.
1335
 * @param string $key
1336
 *   The type of handler. i.e, sort, field, argument, filter, relationship.
1337
 * @param mixed $override
1338
 *   Override the actual handler object with this class. Used for aggregation
1339
 *   when the handler is redirected to the aggregation handler.
1340
 *
1341
 * @return views_handler
1342
 *   An instance of a handler object. May be views_handler_broken.
1343
 */
1344
function views_get_handler($table, $field, $key, $override = NULL) {
1345
  static $recursion_protection = array();
1346

    
1347
  $data = views_fetch_data($table, FALSE);
1348
  $handler = NULL;
1349
  views_include('handlers');
1350

    
1351
  // Support conversion on table level.
1352
  if (isset($data['moved to'])) {
1353
    $moved = array($data['moved to'], $field);
1354
  }
1355
  // Support conversion on datafield level.
1356
  if (isset($data[$field]['moved to'])) {
1357
    $moved = $data[$field]['moved to'];
1358
  }
1359
  // Support conversion on handler level.
1360
  if (isset($data[$field][$key]['moved to'])) {
1361
    $moved = $data[$field][$key]['moved to'];
1362
  }
1363

    
1364
  if (isset($data[$field][$key]) || !empty($moved)) {
1365
    if (!empty($moved)) {
1366
      list($moved_table, $moved_field) = $moved;
1367
      if (!empty($recursion_protection[$moved_table][$moved_field])) {
1368
        // Recursion detected!
1369
        return NULL;
1370
      }
1371

    
1372
      $recursion_protection[$moved_table][$moved_field] = TRUE;
1373
      $handler = views_get_handler($moved_table, $moved_field, $key, $override);
1374
      $recursion_protection = array();
1375
      if ($handler) {
1376
        // Store these values so we know what we were originally called.
1377
        $handler->original_table = $table;
1378
        $handler->original_field = $field;
1379
        if (empty($handler->actual_table)) {
1380
          $handler->actual_table = $moved_table;
1381
          $handler->actual_field = $moved_field;
1382
        }
1383
      }
1384
      return $handler;
1385
    }
1386

    
1387
    // Set up a default handler.
1388
    if (empty($data[$field][$key]['handler'])) {
1389
      $data[$field][$key]['handler'] = 'views_handler_' . $key;
1390
    }
1391

    
1392
    if ($override) {
1393
      $data[$field][$key]['override handler'] = $override;
1394
    }
1395

    
1396
    $handler = _views_prepare_handler($data[$field][$key], $data, $field, $key);
1397
  }
1398

    
1399
  if ($handler) {
1400
    return $handler;
1401
  }
1402

    
1403
  // DEBUG -- identify missing handlers.
1404
  $placeholders = array('@table' => $table, '@field' => $field, '@key' => $key);
1405
  vpr("Missing handler: @table @field @key", $placeholders);
1406
  $broken = array(
1407
    'title' => t('Broken handler @table.@field', array('@table' => $table, '@field' => $field)),
1408
    'handler' => 'views_handler_' . $key . '_broken',
1409
    'table' => $table,
1410
    'field' => $field,
1411
  );
1412
  return _views_create_handler($broken, 'handler', $key);
1413
}
1414

    
1415
/**
1416
 * Fetch Views' data from the cache.
1417
 */
1418
function views_fetch_data($table = NULL, $move = TRUE, $reset = FALSE) {
1419
  views_include('cache');
1420
  return _views_fetch_data($table, $move, $reset);
1421
}
1422

    
1423
/**
1424
 * Fetch the plugin data from cache.
1425
 */
1426
function views_fetch_plugin_data($type = NULL, $plugin = NULL, $reset = FALSE) {
1427
  views_include('cache');
1428
  return _views_fetch_plugin_data($type, $plugin, $reset);
1429
}
1430

    
1431
/**
1432
 * Fetch a list of all base tables available.
1433
 *
1434
 * @param string $type
1435
 *   Either 'display', 'style' or 'row'.
1436
 * @param string $key
1437
 *   For style plugins, this is an optional type to restrict to. May be
1438
 *   'normal', 'summary', 'feed' or others based on the needs of the display.
1439
 * @param array $base
1440
 *   An array of possible base tables.
1441
 *
1442
 * @return array
1443
 *   A keyed array of in the form of 'base_table' => 'Description'.
1444
 */
1445
function views_fetch_plugin_names($type, $key = NULL, $base = array()) {
1446
  $data = views_fetch_plugin_data();
1447

    
1448
  $plugins[$type] = array();
1449

    
1450
  foreach ($data[$type] as $id => $plugin) {
1451
    // Skip plugins that don't conform to our key.
1452
    if ($key && (empty($plugin['type']) || $plugin['type'] != $key)) {
1453
      continue;
1454
    }
1455
    if (empty($plugin['no ui']) && (empty($base) || empty($plugin['base']) || array_intersect($base, $plugin['base']))) {
1456
      $plugins[$type][$id] = $plugin['title'];
1457
    }
1458
  }
1459

    
1460
  if (!empty($plugins[$type])) {
1461
    asort($plugins[$type]);
1462
    return $plugins[$type];
1463
  }
1464

    
1465
  // Fall-through.
1466
  return array();
1467
}
1468

    
1469
/**
1470
 * Get a handler for a plugin.
1471
 *
1472
 * @return views_plugin
1473
 *   The created plugin object.
1474
 */
1475
function views_get_plugin($type, $plugin, $reset = FALSE) {
1476
  views_include('handlers');
1477
  $definition = views_fetch_plugin_data($type, $plugin, $reset);
1478
  if (!empty($definition)) {
1479
    return _views_create_handler($definition, $type);
1480
  }
1481
}
1482

    
1483
/**
1484
 * Load the current enabled localization plugin.
1485
 *
1486
 * @return string
1487
 *   The name of the localization plugin.
1488
 */
1489
function views_get_localization_plugin() {
1490
  $plugin = variable_get('views_localization_plugin', '');
1491
  // Provide sane default values for the localization plugin.
1492
  if (empty($plugin)) {
1493
    if (module_exists('locale')) {
1494
      $plugin = 'core';
1495
    }
1496
    else {
1497
      $plugin = 'none';
1498
    }
1499
  }
1500

    
1501
  return $plugin;
1502
}
1503

    
1504
/**
1505
 * Get all view templates.
1506
 *
1507
 * Templates are special in-code views that are never active, but exist only
1508
 * to be cloned into real views as though they were templates.
1509
 */
1510
function views_get_all_templates() {
1511
  $templates = array();
1512
  $modules = views_module_include('views_template');
1513

    
1514
  foreach ($modules as $module => $info) {
1515
    $function = $module . '_views_templates';
1516
    if (function_exists($function)) {
1517
      $new = $function();
1518
      if ($new && is_array($new)) {
1519
        $templates = array_merge($new, $templates);
1520
      }
1521
    }
1522
  }
1523

    
1524
  return $templates;
1525
}
1526

    
1527
/**
1528
 * Create an empty view to work with.
1529
 *
1530
 * @return view
1531
 *   A fully formed, empty $view object. This object must be populated before
1532
 *   it can be successfully saved.
1533
 */
1534
function views_new_view() {
1535
  views_include('view');
1536
  $view = new view();
1537
  $view->vid = 'new';
1538
  $view->add_display('default');
1539

    
1540
  return $view;
1541
}
1542

    
1543
/**
1544
 * Get applicable views.
1545
 *
1546
 * Return a list of all views and display IDs that have a particular setting in
1547
 * their display's plugin settings.
1548
 *
1549
 * @return array
1550
 *   An array with the following structure.
1551
 *   array(
1552
 *     array($view, $display_id),
1553
 *     array($view, $display_id),
1554
 *   );
1555
 */
1556
function views_get_applicable_views($type) {
1557
  // @todo Use a smarter flagging system so that we don't have to
1558
  // load every view for this.
1559
  $result = array();
1560
  $views = views_get_all_views();
1561

    
1562
  foreach ($views as $view) {
1563
    // Skip disabled views.
1564
    if (!empty($view->disabled)) {
1565
      continue;
1566
    }
1567

    
1568
    if (empty($view->display)) {
1569
      // Skip this view as it is broken.
1570
      vsm(t("Skipping broken view @view", array('@view' => $view->name)));
1571
      continue;
1572
    }
1573

    
1574
    // Loop on array keys because something seems to muck with $view->display
1575
    // a bit in PHP4.
1576
    foreach (array_keys($view->display) as $id) {
1577
      $plugin = views_fetch_plugin_data('display', $view->display[$id]->display_plugin);
1578
      if (!empty($plugin[$type])) {
1579
        // This view uses hook menu. Clone it so that different handlers
1580
        // don't trip over each other, and add it to the list.
1581
        $v = $view->clone_view();
1582
        if ($v->set_display($id) && $v->display_handler->get_option('enabled')) {
1583
          $result[] = array($v, $id);
1584
        }
1585
        // In PHP 4.4.7 and presumably earlier, if we do not unset $v
1586
        // here, we will find that it actually overwrites references
1587
        // possibly due to shallow copying issues.
1588
        unset($v);
1589
      }
1590
    }
1591
  }
1592
  return $result;
1593
}
1594

    
1595
/**
1596
 * Return an array of all views as fully loaded $view objects.
1597
 *
1598
 * @param bool $reset
1599
 *   If TRUE, reset the static cache forcing views to be reloaded.
1600
 */
1601
function views_get_all_views($reset = FALSE) {
1602
  ctools_include('export');
1603
  return ctools_export_crud_load_all('views_view', $reset);
1604
}
1605

    
1606
/**
1607
 * Returns an array of all enabled views, as fully loaded $view objects.
1608
 */
1609
function views_get_enabled_views() {
1610
  $views = views_get_all_views();
1611
  return array_filter($views, 'views_view_is_enabled');
1612
}
1613

    
1614
/**
1615
 * Returns an array of all disabled views, as fully loaded $view objects.
1616
 */
1617
function views_get_disabled_views() {
1618
  $views = views_get_all_views();
1619
  return array_filter($views, 'views_view_is_disabled');
1620
}
1621

    
1622
/**
1623
 * Get options array.
1624
 *
1625
 * Return an array of view as options array, that can be used by select,
1626
 * checkboxes and radios as #options.
1627
 *
1628
 * @param bool $views_only
1629
 *   If TRUE, only return views, not displays.
1630
 * @param string $filter
1631
 *   Filters the views on status. Can either be 'all' (default), 'enabled' or
1632
 *   'disabled'.
1633
 * @param mixed $exclude_view
1634
 *   View or current display to exclude
1635
 *   either a
1636
 *   - views object (containing name and current_display)
1637
 *   - views name as string:  e.g. my_view
1638
 *   - views name and display id (separated by ':'): e.g. my_view:default.
1639
 * @param bool $optgroup
1640
 *   If TRUE, returns an array with optgroups for each view (will be ignored for
1641
 *   $views_only = TRUE). Can be used by select.
1642
 * @param bool $sort
1643
 *   If TRUE, the list of views is sorted ascending.
1644
 *
1645
 * @return array
1646
 *   An associative array for use in select.
1647
 *   - key: view name and display id separated by ':', or the view name only
1648
 */
1649
function views_get_views_as_options($views_only = FALSE, $filter = 'all', $exclude_view = NULL, $optgroup = FALSE, $sort = FALSE) {
1650

    
1651
  // Filter the big views array.
1652
  switch ($filter) {
1653
    case 'all':
1654
    case 'disabled':
1655
    case 'enabled':
1656
      $func = "views_get_{$filter}_views";
1657
      $views = $func();
1658
      break;
1659

    
1660
    default:
1661
      return array();
1662
  }
1663

    
1664
  // Prepare exclude view strings for comparison.
1665
  if (empty($exclude_view)) {
1666
    $exclude_view_name = '';
1667
    $exclude_view_display = '';
1668
  }
1669
  elseif (is_object($exclude_view)) {
1670
    $exclude_view_name = $exclude_view->name;
1671
    $exclude_view_display = $exclude_view->current_display;
1672
  }
1673
  else {
1674
    list($exclude_view_name, $exclude_view_display) = explode(':', $exclude_view);
1675
  }
1676

    
1677
  $options = array();
1678
  foreach ($views as $view) {
1679
    // Return only views.
1680
    if ($views_only && $view->name != $exclude_view_name) {
1681
      $options[$view->name] = $view->get_human_name();
1682
    }
1683
    // Return views with display ids.
1684
    else {
1685
      foreach ($view->display as $display_id => $display) {
1686
        if (!($view->name == $exclude_view_name && $display_id == $exclude_view_display)) {
1687
          if ($optgroup) {
1688
            $options[$view->name][$view->name . ':' . $display->id] = t('@view : @display', array('@view' => $view->name, '@display' => $display->id));
1689
          }
1690
          else {
1691
            $options[$view->name . ':' . $display->id] = t('View: @view - Display: @display', array('@view' => $view->name, '@display' => $display->id));
1692
          }
1693
        }
1694
      }
1695
    }
1696
  }
1697
  if ($sort) {
1698
    ksort($options);
1699
  }
1700
  return $options;
1701
}
1702

    
1703
/**
1704
 * Returns TRUE if a view is enabled, FALSE otherwise.
1705
 */
1706
function views_view_is_enabled($view) {
1707
  return empty($view->disabled);
1708
}
1709

    
1710
/**
1711
 * Returns TRUE if a view is disabled, FALSE otherwise.
1712
 */
1713
function views_view_is_disabled($view) {
1714
  return !empty($view->disabled);
1715
}
1716

    
1717
/**
1718
 * Get a view from the database or from default views.
1719
 *
1720
 * This function is just a static wrapper around views::load(). This function
1721
 * isn't called 'views_load()' primarily because it might get a view
1722
 * from the default views which aren't technically loaded from the database.
1723
 *
1724
 * @param string $name
1725
 *   The name of the view.
1726
 * @param bool $reset
1727
 *   If TRUE, reset this entry in the load cache.
1728
 *
1729
 * @return view
1730
 *   A reference to the $view object. Use $reset if you're sure you want
1731
 *   a fresh one.
1732
 */
1733
function views_get_view($name, $reset = FALSE) {
1734
  if ($reset) {
1735
    $cache = &drupal_static('ctools_export_load_object');
1736
    if (isset($cache['views_view'][$name])) {
1737
      unset($cache['views_view'][$name]);
1738
    }
1739
  }
1740

    
1741
  ctools_include('export');
1742
  $view = ctools_export_crud_load('views_view', $name);
1743
  if ($view) {
1744
    $view->update();
1745
    return $view->clone_view();
1746
  }
1747
}
1748

    
1749
/**
1750
 * Find the real location of a table.
1751
 *
1752
 * If a table has moved, find the new name of the table so that we can
1753
 * change its name directly in options where necessary.
1754
 */
1755
function views_move_table($table) {
1756
  $data = views_fetch_data($table, FALSE);
1757
  if (isset($data['moved to'])) {
1758
    $table = $data['moved to'];
1759
  }
1760

    
1761
  return $table;
1762
}
1763

    
1764
/**
1765
 * Export callback to load the view subrecords, which are the displays.
1766
 */
1767
function views_load_display_records(&$views) {
1768
  // Get vids from the views.
1769
  $names = array();
1770
  foreach ($views as $view) {
1771
    if (empty($view->display)) {
1772
      $names[$view->vid] = $view->name;
1773
    }
1774
  }
1775

    
1776
  if (empty($names)) {
1777
    return;
1778
  }
1779

    
1780
  foreach (view::db_objects() as $key) {
1781
    $object_name = "views_$key";
1782
    $result = db_query("SELECT * FROM {{$object_name}} WHERE vid IN (:vids) ORDER BY vid, position",
1783
      array(':vids' => array_keys($names)));
1784

    
1785
    foreach ($result as $data) {
1786
      $object = new $object_name(FALSE);
1787
      $object->load_row($data);
1788

    
1789
      // Because it can get complicated with this much indirection,
1790
      // make a shortcut reference.
1791
      $location = &$views[$names[$object->vid]]->$key;
1792

    
1793
      // If we have a basic id field, load the item onto the view based on
1794
      // this ID, otherwise push it on.
1795
      if (!empty($object->id)) {
1796
        $location[$object->id] = $object;
1797
      }
1798
      else {
1799
        $location[] = $object;
1800
      }
1801
    }
1802
  }
1803
}
1804

    
1805
/**
1806
 * Export CRUD callback to save a view.
1807
 */
1808
function views_save_view(&$view) {
1809
  return $view->save();
1810
}
1811

    
1812
/**
1813
 * Export CRUD callback to delete a view.
1814
 */
1815
function views_delete_view(&$view) {
1816
  return $view->delete(TRUE);
1817
}
1818

    
1819
/**
1820
 * Export CRUD callback to export a view.
1821
 */
1822
function views_export_view(&$view, $indent = '') {
1823
  return $view->export($indent);
1824
}
1825

    
1826
/**
1827
 * Export callback to change view status.
1828
 */
1829
function views_export_status($view, $status) {
1830
  ctools_export_set_object_status($view, $status);
1831
  views_invalidate_cache();
1832
}
1833

    
1834
/**
1835
 * Provide debug output for Views.
1836
 *
1837
 * This relies on devel.module
1838
 * or on the debug() function if you use a simpletest.
1839
 *
1840
 * @param mixed $message
1841
 *   The message/variable which should be debugged.
1842
 *   This either could be
1843
 *     * an array/object which is converted to pretty output
1844
 *     * a translation source string which is used together with the parameter
1845
 *       placeholders.
1846
 * @param array $placeholders
1847
 *   The placeholders which are used for the translation source string.
1848
 */
1849
function views_debug($message, $placeholders = array()) {
1850
  if (!is_string($message)) {
1851
    $output = '<pre>' . var_export($message, TRUE) . '</pre>';
1852
  }
1853
  if (module_exists('devel') && variable_get('views_devel_output', FALSE) && user_access('access devel information')) {
1854
    $devel_region = variable_get('views_devel_region', 'footer');
1855
    if ($devel_region == 'watchdog') {
1856
      $output = $message;
1857
      watchdog('views_logging', $output, $placeholders);
1858
    }
1859
    elseif ($devel_region == 'drupal_debug') {
1860
      $output = empty($output) ? t($message, $placeholders) : $output;
1861
      dd($output);
1862
    }
1863
    else {
1864
      $output = empty($output) ? t($message, $placeholders) : $output;
1865
      dpm($output);
1866
    }
1867
  }
1868
  elseif (isset($GLOBALS['drupal_test_info'])) {
1869
    $output = empty($output) ? t($message, $placeholders) : $output;
1870
    debug($output);
1871
  }
1872
}
1873

    
1874
/**
1875
 * Shortcut to views_debug().
1876
 */
1877
function vpr($message, $placeholders = array()) {
1878
  views_debug($message, $placeholders);
1879
}
1880

    
1881
/**
1882
 * Debug messages.
1883
 */
1884
function vsm($message) {
1885
  if (module_exists('devel')) {
1886
    dpm($message);
1887
  }
1888
}
1889

    
1890
function views_trace() {
1891
  $message = '';
1892
  foreach (debug_backtrace() as $item) {
1893
    $traces = array('vsm_trace', 'vpr_trace', 'views_trace');
1894
    if (!empty($item['file']) && !in_array($item['function'], $traces)) {
1895
      $message .= basename($item['file']) . ": " . (empty($item['class']) ? '' : ($item['class'] . '->')) . "$item[function] line $item[line]" . "\n";
1896
    }
1897
  }
1898
  return $message;
1899
}
1900

    
1901
function vsm_trace() {
1902
  vsm(views_trace());
1903
}
1904

    
1905
function vpr_trace() {
1906
  dpr(views_trace());
1907
}
1908

    
1909
/**
1910
 * Determine whether the view has form elements.
1911
 *
1912
 * Returns TRUE if the passed-in view contains handlers with views form
1913
 * implementations, FALSE otherwise.
1914
 */
1915
function views_view_has_form_elements($view) {
1916
  foreach ($view->field as $field) {
1917
    if (property_exists($field, 'views_form_callback') || method_exists($field, 'views_form')) {
1918
      return TRUE;
1919
    }
1920
  }
1921
  $area_handlers = array_merge(array_values($view->header), array_values($view->footer));
1922
  $empty = empty($view->result);
1923
  foreach ($area_handlers as $area) {
1924
    if (method_exists($area, 'views_form') && !$area->views_form_empty($empty)) {
1925
      return TRUE;
1926
    }
1927
  }
1928
  return FALSE;
1929
}
1930

    
1931
/**
1932
 * This is the entry function. Just gets the form for the current step.
1933
 *
1934
 * The form is always assumed to be multistep, even if it has only one
1935
 * step (the default 'views_form_views_form' step). That way it is actually
1936
 * possible for modules to have a multistep form if they need to.
1937
 */
1938
function views_form($form, &$form_state, $view, $output) {
1939
  $form_state['step'] = isset($form_state['step']) ? $form_state['step'] : 'views_form_views_form';
1940
  // Cache the built form to prevent it from being rebuilt prior to validation
1941
  // and submission, which could lead to data being processed incorrectly,
1942
  // because the views rows (and thus, the form elements as well) have changed
1943
  // in the meantime.
1944
  $form_state['cache'] = TRUE;
1945

    
1946
  $form = array();
1947
  $query = drupal_get_query_parameters($_GET, array('q'));
1948
  $form['#action'] = url($view->get_url(), array('query' => $query));
1949
  // Tell the preprocessor whether it should hide the header, footer, pager...
1950
  $form['show_view_elements'] = array(
1951
    '#type' => 'value',
1952
    '#value' => ($form_state['step'] == 'views_form_views_form') ? TRUE : FALSE,
1953
  );
1954

    
1955
  $form = $form_state['step']($form, $form_state, $view, $output);
1956
  return $form;
1957
}
1958

    
1959
/**
1960
 * Callback for the main step of a Views form.
1961
 *
1962
 * Invoked by views_form().
1963
 */
1964
function views_form_views_form($form, &$form_state, $view, $output) {
1965
  $form['#prefix'] = '<div class="views-form">';
1966
  $form['#suffix'] = '</div>';
1967
  $form['#theme'] = 'views_form_views_form';
1968
  $form['#validate'][] = 'views_form_views_form_validate';
1969
  $form['#submit'][] = 'views_form_views_form_submit';
1970

    
1971
  // Add the output markup to the form array so that it's included when the form
1972
  // array is passed to the theme function.
1973
  $form['output'] = array(
1974
    '#type' => 'markup',
1975
    '#markup' => $output,
1976
    // This way any additional form elements will go before the view
1977
    // (below the exposed widgets).
1978
    '#weight' => 50,
1979
  );
1980

    
1981
  $substitutions = array();
1982
  foreach ($view->field as $field_name => $field) {
1983
    $form_element_name = $field_name;
1984
    if (method_exists($field, 'form_element_name')) {
1985
      $form_element_name = $field->form_element_name();
1986
    }
1987
    $method_form_element_row_id_exists = FALSE;
1988
    if (method_exists($field, 'form_element_row_id')) {
1989
      $method_form_element_row_id_exists = TRUE;
1990
    }
1991

    
1992
    // If the field provides a views form, allow it to modify the $form array.
1993
    $has_form = FALSE;
1994
    if (property_exists($field, 'views_form_callback')) {
1995
      $callback = $field->views_form_callback;
1996
      $callback($view, $field, $form, $form_state);
1997
      $has_form = TRUE;
1998
    }
1999
    elseif (method_exists($field, 'views_form')) {
2000
      $field->views_form($form, $form_state);
2001
      $has_form = TRUE;
2002
    }
2003

    
2004
    // Build the substitutions array for use in the theme function.
2005
    if ($has_form) {
2006
      foreach ($view->result as $row_id => $row) {
2007
        if ($method_form_element_row_id_exists) {
2008
          $form_element_row_id = $field->form_element_row_id($row_id);
2009
        }
2010
        else {
2011
          $form_element_row_id = $row_id;
2012
        }
2013

    
2014
        $substitutions[] = array(
2015
          'placeholder' => '<!--form-item-' . $form_element_name . '--' . $form_element_row_id . '-->',
2016
          'field_name' => $form_element_name,
2017
          'row_id' => $form_element_row_id,
2018
        );
2019
      }
2020
    }
2021
  }
2022

    
2023
  // Give the area handlers a chance to extend the form.
2024
  $area_handlers = array_merge(array_values($view->header), array_values($view->footer));
2025
  $empty = empty($view->result);
2026
  foreach ($area_handlers as $area) {
2027
    if (method_exists($area, 'views_form') && !$area->views_form_empty($empty)) {
2028
      $area->views_form($form, $form_state);
2029
    }
2030
  }
2031

    
2032
  $form['#substitutions'] = array(
2033
    '#type' => 'value',
2034
    '#value' => $substitutions,
2035
  );
2036
  $form['actions'] = array(
2037
    '#type' => 'container',
2038
    '#attributes' => array('class' => array('form-actions')),
2039
    '#weight' => 100,
2040
  );
2041
  $form['actions']['submit'] = array(
2042
    '#type' => 'submit',
2043
    '#value' => t('Save'),
2044
  );
2045

    
2046
  return $form;
2047
}
2048

    
2049
/**
2050
 * Validate handler for the first step of the views form.
2051
 *
2052
 * Calls any existing views_form_validate functions located
2053
 * on the views fields.
2054
 */
2055
function views_form_views_form_validate($form, &$form_state) {
2056
  $view = $form_state['build_info']['args'][0];
2057

    
2058
  // Call the validation method on every field handler that has it.
2059
  foreach ($view->field as $field_name => $field) {
2060
    if (method_exists($field, 'views_form_validate')) {
2061
      $field->views_form_validate($form, $form_state);
2062
    }
2063
  }
2064

    
2065
  // Call the validate method on every area handler that has it.
2066
  foreach (array('header', 'footer') as $area) {
2067
    foreach ($view->{$area} as $area_name => $area_handler) {
2068
      if (method_exists($area_handler, 'views_form_validate')) {
2069
        $area_handler->views_form_validate($form, $form_state);
2070
      }
2071
    }
2072
  }
2073
}
2074

    
2075
/**
2076
 * Submit handler for the first step of the views form.
2077
 *
2078
 * Calls any existing views_form_submit functions located
2079
 * on the views fields.
2080
 */
2081
function views_form_views_form_submit($form, &$form_state) {
2082
  $view = $form_state['build_info']['args'][0];
2083

    
2084
  // Call the submit method on every field handler that has it.
2085
  foreach ($view->field as $field_name => $field) {
2086
    if (method_exists($field, 'views_form_submit')) {
2087
      $field->views_form_submit($form, $form_state);
2088
    }
2089
  }
2090

    
2091
  // Call the submit method on every area handler that has it.
2092
  foreach (array('header', 'footer') as $area) {
2093
    foreach ($view->{$area} as $area_name => $area_handler) {
2094
      if (method_exists($area_handler, 'views_form_submit')) {
2095
        $area_handler->views_form_submit($form, $form_state);
2096
      }
2097
    }
2098
  }
2099
}
2100

    
2101
/**
2102
 * Form builder for the exposed widgets form.
2103
 *
2104
 * Be sure that $view and $display are references.
2105
 */
2106
function views_exposed_form($form, &$form_state) {
2107
  // Don't show the form when batch operations are in progress.
2108
  if ($batch = batch_get() && isset($batch['current_set'])) {
2109
    return array(
2110
      // Set the theme callback to be nothing to avoid errors in
2111
      // template_preprocess_views_exposed_form().
2112
      '#theme' => '',
2113
    );
2114
  }
2115

    
2116
  // Make sure that we validate because this form might be submitted
2117
  // multiple times per page.
2118
  $form_state['must_validate'] = TRUE;
2119
  $view = &$form_state['view'];
2120
  $display = &$form_state['display'];
2121

    
2122
  $form_state['input'] = $view->get_exposed_input();
2123

    
2124
  // Let form plugins know this is for exposed widgets.
2125
  $form_state['exposed'] = TRUE;
2126
  // Check if the form was already created.
2127
  if ($cache = views_exposed_form_cache($view->name, $view->current_display)) {
2128
    return $cache;
2129
  }
2130

    
2131
  $form['#info'] = array();
2132

    
2133
  if (!variable_get('clean_url', FALSE)) {
2134
    $form['q'] = array(
2135
      '#type' => 'hidden',
2136
      '#value' => $view->get_url(),
2137
    );
2138
  }
2139

    
2140
  // Go through each handler and let it generate its exposed widget.
2141
  foreach ($view->display_handler->handlers as $type => $value) {
2142
    foreach ($view->$type as $id => $handler) {
2143
      if ($handler->can_expose() && $handler->is_exposed()) {
2144
        // Grouped exposed filters have their own forms.
2145
        // Instead of render the standard exposed form, a new Select or
2146
        // Radio form field is rendered with the available groups.
2147
        // When an user choose an option the selected value is split
2148
        // into the operator and value that the item represents.
2149
        if ($handler->is_a_group()) {
2150
          $handler->group_form($form, $form_state);
2151
          $id = $handler->options['group_info']['identifier'];
2152
        }
2153
        else {
2154
          $handler->exposed_form($form, $form_state);
2155
        }
2156
        if ($info = $handler->exposed_info()) {
2157
          $form['#info']["$type-$id"] = $info;
2158
        }
2159
      }
2160
    }
2161
  }
2162

    
2163
  // Form submit, #name is an empty string to prevent showing up in $_GET.
2164
  $form['submit'] = array(
2165
    '#name' => '',
2166
    '#type' => 'submit',
2167
    '#value' => t('Apply'),
2168
    '#id' => drupal_html_id('edit-submit-' . $view->name),
2169
  );
2170

    
2171
  $form['#action'] = url($view->display_handler->get_url());
2172
  $form['#theme'] = views_theme_functions('views_exposed_form', $view, $display);
2173
  $form['#id'] = drupal_clean_css_identifier('views_exposed_form-' . check_plain($view->name) . '-' . check_plain($display->id));
2174

    
2175
  // If using AJAX, we need the form plugin.
2176
  if ($view->use_ajax) {
2177
    drupal_add_library('system', 'jquery.form');
2178
  }
2179
  ctools_include('dependent');
2180

    
2181
  $exposed_form_plugin = $form_state['exposed_form_plugin'];
2182
  $exposed_form_plugin->exposed_form_alter($form, $form_state);
2183

    
2184
  // Save the form.
2185
  views_exposed_form_cache($view->name, $view->current_display, $form);
2186

    
2187
  return $form;
2188
}
2189

    
2190
/**
2191
 * Implements hook_form_alter() for views_exposed_form().
2192
 *
2193
 * Since the exposed form is a GET form, we don't want it to send a wide
2194
 * variety of information.
2195
 */
2196
function views_form_views_exposed_form_alter(&$form, &$form_state) {
2197
  $form['form_build_id']['#access'] = FALSE;
2198
  $form['form_token']['#access'] = FALSE;
2199
  $form['form_id']['#access'] = FALSE;
2200

    
2201
  // AJAX behaviors need these data, so we add it back in #after_build.
2202
  $form['#after_build'][] = 'views_exposed_form_ajax_enable';
2203
}
2204

    
2205
/**
2206
 * Checks whether the exposed form will use AJAX.
2207
 *
2208
 * Passes required form information removed in
2209
 * views_form_views_exposed_form_alter().
2210
 *
2211
 * @param array $form
2212
 *   The form being processed.
2213
 * @param array $form_state
2214
 *   The form state.
2215
 *
2216
 * @return array
2217
 *   The form being processed.
2218
 */
2219
function views_exposed_form_ajax_enable(array &$form, array &$form_state) {
2220
  // In order for AJAX to work the form build info is needed. Here check if
2221
  // #ajax has been added to any form elements, and if so, pass this info as
2222
  // settings via JavaScript, which get attached to the submitted form on AJAX
2223
  // form submissions. #ajax property can be set not only for the first level of
2224
  // the form, so look for it in the whole form.
2225
  $ajax_info = array();
2226
  $ajax_elements = views_exposed_form_ajax_lookup_recursive($form);
2227
  // Determine if form is being processed.
2228
  $triggering_element_name = '';
2229
  if (!empty($form_state['input']['_triggering_element_name'])) {
2230
    $triggering_element_name = $form_state['input']['_triggering_element_name'];
2231
  }
2232

    
2233
  // When there are multiple elements with #ajax set on exposed form, pass only
2234
  // triggering element name to JavaScript, otherwise #ajax will work only for
2235
  // the first element.
2236
  if (!empty($triggering_element_name) && !empty($ajax_elements)) {
2237
    // Check if element has #ajax property set.
2238
    if (in_array($triggering_element_name, $ajax_elements)) {
2239
      $ajax_elements = array(
2240
        $triggering_element_name => $triggering_element_name,
2241
      );
2242
    }
2243
    else {
2244
      $ajax_elements = array();
2245
    }
2246
  }
2247

    
2248
  if (!empty($ajax_elements)) {
2249
    $form_info = array(
2250
      'form_id' => $form['#form_id'],
2251
      'form_build_id' => $form['#build_id'],
2252
    );
2253
    // Anonymous users don't get a token.
2254
    if (!empty($form['#token'])) {
2255
      $form_info['form_token'] = drupal_get_token($form['#token']);
2256
    }
2257
    foreach ($ajax_elements as $element_name) {
2258
      $ajax_info[$element_name] = $form_info;
2259
    }
2260
    drupal_add_js(array('ViewsExposedFormInfo' => $ajax_info), 'setting');
2261

    
2262
    // Add the javascript behavior that will handle this data.
2263
    $form['#attached']['js'][] = array(
2264
      'weight' => 100,
2265
      'data' => drupal_get_path('module', 'views') . '/js/exposed-form-ajax.js',
2266
    );
2267
  }
2268

    
2269
  return $form;
2270
}
2271

    
2272
/**
2273
 * Recursively looks for the #ajax property for every form elemet.
2274
 *
2275
 * @param array $elements
2276
 *   The element array to look for #ajax property.
2277
 *
2278
 * @return array
2279
 *   Array of the elements names where #ajax was found.
2280
 */
2281
function views_exposed_form_ajax_lookup_recursive(array $elements) {
2282
  $ajax_elements = array();
2283
  foreach (element_children($elements) as $key) {
2284
    if (!empty($elements[$key]['#name']) && !empty($elements[$key]['#ajax'])) {
2285
      $ajax_elements[$elements[$key]['#name']] = $elements[$key]['#name'];
2286
    }
2287
    // Recursive call to look for #ajax in element's childrens.
2288
    $ajax_elements += views_exposed_form_ajax_lookup_recursive($elements[$key]);
2289
  }
2290
  return $ajax_elements;
2291
}
2292

    
2293
/**
2294
 * Validate handler for exposed filters.
2295
 */
2296
function views_exposed_form_validate(&$form, &$form_state) {
2297
  foreach (array('field', 'filter') as $type) {
2298
    $handlers = &$form_state['view']->$type;
2299
    foreach ($handlers as $key => $handler) {
2300
      $handlers[$key]->exposed_validate($form, $form_state);
2301
    }
2302
  }
2303
  $exposed_form_plugin = $form_state['exposed_form_plugin'];
2304
  $exposed_form_plugin->exposed_form_validate($form, $form_state);
2305
}
2306

    
2307
/**
2308
 * Submit handler for exposed filters.
2309
 */
2310
function views_exposed_form_submit(&$form, &$form_state) {
2311
  foreach (array('field', 'filter') as $type) {
2312
    $handlers = &$form_state['view']->$type;
2313
    foreach ($handlers as $key => $info) {
2314
      $handlers[$key]->exposed_submit($form, $form_state);
2315
    }
2316
  }
2317
  $form_state['view']->exposed_data = $form_state['values'];
2318
  $form_state['view']->exposed_raw_input = array();
2319

    
2320
  $exclude = array(
2321
    'q',
2322
    'submit',
2323
    'form_build_id',
2324
    'form_id',
2325
    'form_token',
2326
    'exposed_form_plugin',
2327
    '',
2328
    'reset',
2329
  );
2330
  $exposed_form_plugin = $form_state['exposed_form_plugin'];
2331
  $exposed_form_plugin->exposed_form_submit($form, $form_state, $exclude);
2332

    
2333
  foreach ($form_state['values'] as $key => $value) {
2334
    if (!in_array($key, $exclude)) {
2335
      $form_state['view']->exposed_raw_input[$key] = $value;
2336
    }
2337
  }
2338
}
2339

    
2340
/**
2341
 * Save the Views exposed form for later use.
2342
 *
2343
 * @param string $views_name
2344
 *   The views name.
2345
 * @param string $display_name
2346
 *   The current view display name.
2347
 * @param array $form_output
2348
 *   An optional form structure. Only needed when inserting the value.
2349
 *
2350
 * @return array|bool
2351
 *   Array. The form structure, if any. Otherwise, return FALSE.
2352
 */
2353
function views_exposed_form_cache($views_name, $display_name, $form_output = NULL) {
2354
  // When running tests for exposed filters, this cache should
2355
  // be cleared between each test.
2356
  $views_exposed = &drupal_static(__FUNCTION__);
2357

    
2358
  // Save the form output.
2359
  if (!empty($form_output)) {
2360
    $views_exposed[$views_name][$display_name] = $form_output;
2361
  }
2362

    
2363
  // Return the form output, if any.
2364
  return empty($views_exposed[$views_name][$display_name]) ? FALSE : $views_exposed[$views_name][$display_name];
2365
}
2366

    
2367
/**
2368
 * Build a list of theme function names for use most everywhere.
2369
 */
2370
function views_theme_functions($hook, $view, $display = NULL) {
2371
  require_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'views') . "/theme/theme.inc";
2372
  return _views_theme_functions($hook, $view, $display);
2373
}
2374

    
2375
/**
2376
 * Substitute current time; this works with cached queries.
2377
 */
2378
function views_views_query_substitutions($view) {
2379
  global $language_content;
2380
  return array(
2381
    '***CURRENT_VERSION***' => VERSION,
2382
    '***CURRENT_TIME***' => REQUEST_TIME,
2383
    '***CURRENT_LANGUAGE***' => $language_content->language,
2384
    '***DEFAULT_LANGUAGE***' => language_default('language'),
2385
  );
2386
}
2387

    
2388
/**
2389
 * Implements hook_query_TAG_alter().
2390
 *
2391
 * This is the hook_query_alter() for queries tagged by Views and is used to
2392
 * add in substitutions from hook_views_query_substitutions().
2393
 */
2394
function views_query_views_alter(QueryAlterableInterface $query) {
2395
  $substitutions = $query->getMetaData('views_substitutions');
2396
  $tables =& $query->getTables();
2397
  $where =& $query->conditions();
2398

    
2399
  // Replaces substitions in tables.
2400
  foreach ($tables as $table_name => $table_metadata) {
2401
    foreach ($table_metadata['arguments'] as $replacement_key => $value) {
2402
      if (isset($substitutions[$value])) {
2403
        $tables[$table_name]['arguments'][$replacement_key] = $substitutions[$value];
2404
      }
2405
    }
2406
  }
2407

    
2408
  // Replaces substitions in filter criterias.
2409
  _views_query_tag_alter_condition($query, $where, $substitutions);
2410
}
2411

    
2412
/**
2413
 * Replaces the substitutions recursive foreach condition.
2414
 */
2415
function _views_query_tag_alter_condition(QueryAlterableInterface $query, &$conditions, $substitutions) {
2416
  foreach ($conditions as $condition_id => &$condition) {
2417
    if (is_numeric($condition_id)) {
2418
      if (is_string($condition['field'])) {
2419
        $condition['field'] = str_replace(array_keys($substitutions), array_values($substitutions), $condition['field']);
2420
      }
2421
      elseif (is_object($condition['field'])) {
2422
        $sub_conditions =& $condition['field']->conditions();
2423
        _views_query_tag_alter_condition($query, $sub_conditions, $substitutions);
2424
      }
2425
      // $condition['value'] is a subquery so alter the subquery recursive.
2426
      // Therefore take sure to get the metadata of the main query.
2427
      if (is_object($condition['value'])) {
2428
        $subquery = $condition['value'];
2429
        $subquery->addMetaData('views_substitutions', $query->getMetaData('views_substitutions'));
2430
        views_query_views_alter($condition['value']);
2431
      }
2432
      elseif (isset($condition['value'])) {
2433
        $condition['value'] = str_replace(array_keys($substitutions), array_values($substitutions), $condition['value']);
2434
      }
2435
    }
2436
  }
2437
}
2438

    
2439
/**
2440
 * Embed a view using a PHP snippet.
2441
 *
2442
 * This function is meant to be called from PHP snippets, should one wish to
2443
 * embed a view in a node or something. It's meant to provide the simplest
2444
 * solution and doesn't really offer a lot of options, but breaking the function
2445
 * apart is pretty easy, and this provides a worthwhile guide to doing so.
2446
 *
2447
 * Note that this function does NOT display the title of the view. If you want
2448
 * to do that, you will need to do what this function does manually, by
2449
 * loading the view, getting the preview and then getting $view->get_title().
2450
 *
2451
 * @param string $name
2452
 *   The name of the view to embed.
2453
 * @param string $display_id
2454
 *   The display id to embed. If unsure, use 'default', as it will always be
2455
 *   valid. But things like 'page' or 'block' should work here.
2456
 * @param ...
2457
 *   Any additional parameters will be passed as arguments.
2458
 */
2459
function views_embed_view($name, $display_id = 'default') {
2460
  $args = func_get_args();
2461
  // Remove $name.
2462
  array_shift($args);
2463
  if (count($args)) {
2464
    // Remove $display_id.
2465
    array_shift($args);
2466
  }
2467

    
2468
  $view = views_get_view($name);
2469
  if (!$view || !$view->access($display_id)) {
2470
    return;
2471
  }
2472

    
2473
  return $view->preview($display_id, $args);
2474
}
2475

    
2476
/**
2477
 * Get the result of a view.
2478
 *
2479
 * @param string $name
2480
 *   The name of the view to retrieve the data from.
2481
 * @param string $display_id
2482
 *   The display id. On the edit page for the view in question, you'll find
2483
 *   a list of displays at the left side of the control area. "Master"
2484
 *   will be at the top of that list. Hover your cursor over the name of the
2485
 *   display you want to use. An URL will appear in the status bar of your
2486
 *   browser. This is usually at the bottom of the window, in the chrome.
2487
 *   Everything after #views-tab- is the display ID, e.g. page_1.
2488
 * @param ...
2489
 *   Any additional parameters will be passed as arguments.
2490
 *
2491
 * @return array
2492
 *   An array containing an object for each view item.
2493
 */
2494
function views_get_view_result($name, $display_id = NULL) {
2495
  $args = func_get_args();
2496
  // Remove $name.
2497
  array_shift($args);
2498
  if (count($args)) {
2499
    // Remove $display_id.
2500
    array_shift($args);
2501
  }
2502

    
2503
  $view = views_get_view($name);
2504
  if (is_object($view)) {
2505
    if (is_array($args)) {
2506
      $view->set_arguments($args);
2507
    }
2508
    if (is_string($display_id)) {
2509
      $view->set_display($display_id);
2510
    }
2511
    else {
2512
      $view->init_display();
2513
    }
2514
    $view->pre_execute();
2515
    $view->execute();
2516
    return $view->result;
2517
  }
2518
  else {
2519
    return array();
2520
  }
2521
}
2522

    
2523
/**
2524
 * Export a field.
2525
 */
2526
function views_var_export($var, $prefix = '', $init = TRUE) {
2527
  if (is_array($var)) {
2528
    if (empty($var)) {
2529
      $output = 'array()';
2530
    }
2531
    else {
2532
      $output = "array(\n";
2533
      foreach ($var as $key => $value) {
2534
        $output .= "  " . views_var_export($key, '', FALSE) . " => " . views_var_export($value, '  ', FALSE) . ",\n";
2535
      }
2536
      $output .= ')';
2537
    }
2538
  }
2539
  elseif (is_bool($var)) {
2540
    $output = $var ? 'TRUE' : 'FALSE';
2541
  }
2542
  elseif (is_string($var) && strpos($var, "\n") !== FALSE) {
2543
    // Replace line breaks in strings with a token for replacement
2544
    // at the very end. This protects multi-line strings from
2545
    // unintentional indentation.
2546
    $var = str_replace("\n", "***BREAK***", $var);
2547
    $output = var_export($var, TRUE);
2548
  }
2549
  else {
2550
    $output = var_export($var, TRUE);
2551
  }
2552

    
2553
  if ($prefix) {
2554
    $output = str_replace("\n", "\n$prefix", $output);
2555
  }
2556

    
2557
  if ($init) {
2558
    $output = str_replace("***BREAK***", "\n", $output);
2559
  }
2560

    
2561
  return $output;
2562
}
2563

    
2564
/**
2565
 * Prepare a string for use as a valid CSS identifier.
2566
 *
2567
 * This function is similar to a core version but with more sane filter values.
2568
 * http://www.w3.org/TR/CSS21/syndata.html#characters shows the syntax for valid
2569
 * CSS identifiers (including element names, classes, and IDs in selectors).
2570
 *
2571
 * @param string $identifier
2572
 *   The identifier to clean.
2573
 * @param array $filter
2574
 *   An array of string replacements to use on the identifier.
2575
 *
2576
 * @return string
2577
 *   The cleaned identifier.
2578
 *
2579
 * @see drupal_clean_css_identifier()
2580
 */
2581
function views_clean_css_identifier($identifier, $filter = array(
2582
  ' ' => '-',
2583
  '/' => '-',
2584
  '[' => '-',
2585
  ']' => '',
2586
)) {
2587
  // By default, we filter using Drupal's coding standards.
2588
  $identifier = strtr($identifier, $filter);
2589

    
2590
  // Valid characters in a CSS identifier are:
2591
  // - the hyphen (U+002D)
2592
  // - a-z (U+0030 - U+0039)
2593
  // - A-Z (U+0041 - U+005A)
2594
  // - the underscore (U+005F)
2595
  // - 0-9 (U+0061 - U+007A)
2596
  // - ISO 10646 characters U+00A1 and higher
2597
  // We strip out any character not in the above list.
2598
  $identifier = preg_replace('/[^\x{002D}\x{0030}-\x{0039}\x{0041}-\x{005A}\x{005F}\x{0061}-\x{007A}\x{00A1}-\x{FFFF}]/u', '', $identifier);
2599

    
2600
  return $identifier;
2601
}
2602

    
2603
/**
2604
 * Implements hook_views_exportables().
2605
 */
2606
function views_views_exportables($op = 'list', $views = NULL, $name = 'foo') {
2607
  $all_views = views_get_all_views();
2608
  if ($op == 'list') {
2609

    
2610
    foreach ($all_views as $name => $view) {
2611
      // In list, $views is a list of tags.
2612
      if (empty($views) || in_array($view->tag, $views)) {
2613
        $return[$name] = array(
2614
          'name' => check_plain($name),
2615
          'desc' => check_plain($view->description),
2616
          'tag' => check_plain($view->tag),
2617
        );
2618
      }
2619
    }
2620
    return $return;
2621
  }
2622

    
2623
  if ($op == 'export') {
2624
    $code = "/**\n";
2625
    $code .= " * Implement hook_views_default_views().\n";
2626
    $code .= " */\n";
2627
    $code .= "function " . $name . "_views_default_views() {\n";
2628
    foreach ($views as $view => $truth) {
2629
      $code .= "  /*\n";
2630
      $code .= "   * View " . var_export($all_views[$view]->name, TRUE) . "\n";
2631
      $code .= "   */\n";
2632
      $code .= $all_views[$view]->export('  ');
2633
      $code .= '  $views[$view->name] = $view;' . "\n\n";
2634
    }
2635
    $code .= "  return \$views;\n";
2636
    $code .= "}\n";
2637

    
2638
    return $code;
2639
  }
2640
}
2641

    
2642
/**
2643
 * Process callback to see if we need to check_plain() the options.
2644
 *
2645
 * Since FAPI is inconsistent, the #options are sanitized for you in all cases
2646
 * _except_ checkboxes. We have form elements that are sometimes 'select' and
2647
 * sometimes 'checkboxes', so we need decide late in the form rendering cycle
2648
 * if the options need to be sanitized before they're rendered. This callback
2649
 * inspects the type, and if it's still 'checkboxes', does the sanitation.
2650
 */
2651
function views_process_check_options($element, &$form_state) {
2652
  if ($element['#type'] == 'checkboxes' || $element['#type'] == 'checkbox') {
2653
    $element['#options'] = array_map('check_plain', $element['#options']);
2654
  }
2655
  return $element;
2656
}
2657

    
2658
/**
2659
 * Trim the field down to the specified length.
2660
 *
2661
 * @param array $alter
2662
 *   - max_length: Maximum length of the string, the rest gets truncated.
2663
 *   - word_boundary: Trim only on a word boundary.
2664
 *   - ellipsis: Show an ellipsis (...) at the end of the trimmed string.
2665
 *   - html: Take sure that the html is correct.
2666
 * @param string $value
2667
 *   The string which should be trimmed.
2668
 */
2669
function views_trim_text($alter, $value) {
2670
  if (drupal_strlen($value) > $alter['max_length']) {
2671
    $value = drupal_substr($value, 0, $alter['max_length']);
2672
    // @todo Replace this with cleanstring of CTools.
2673
    if (!empty($alter['word_boundary'])) {
2674
      $regex = "(.*)\b.+";
2675
      if (function_exists('mb_ereg')) {
2676
        mb_regex_encoding('UTF-8');
2677
        $found = mb_ereg($regex, $value, $matches);
2678
      }
2679
      else {
2680
        $found = preg_match("/$regex/us", $value, $matches);
2681
      }
2682
      if ($found) {
2683
        $value = $matches[1];
2684
      }
2685
    }
2686
    // Remove scraps of HTML entities from the end of a strings.
2687
    $value = rtrim(preg_replace('/(?:<(?!.+>)|&(?!.+;)).*$/us', '', $value));
2688

    
2689
    if (!empty($alter['ellipsis'])) {
2690
      $value .= t('...');
2691
    }
2692
  }
2693
  if (!empty($alter['html'])) {
2694
    $value = _filter_htmlcorrector($value);
2695
  }
2696

    
2697
  return $value;
2698
}
2699

    
2700
/**
2701
 * Adds one to each key of the array.
2702
 *
2703
 * For example array(0 => 'foo') would be array(1 => 'foo').
2704
 */
2705
function views_array_key_plus($array) {
2706
  $keys = array_keys($array);
2707
  rsort($keys);
2708
  foreach ($keys as $key) {
2709
    $array[$key + 1] = $array[$key];
2710
    unset($array[$key]);
2711
  }
2712
  asort($array);
2713
  return $array;
2714
}
2715

    
2716
/**
2717
 * Implements hook_ctools_plugin_api_hook_name().
2718
 *
2719
 * Report to CTools that we use hook_views_api instead of
2720
 * hook_ctools_plugin_api().
2721
 */
2722
function views_ctools_plugin_api_hook_name() {
2723
  return 'views_api';
2724
}
2725

    
2726
/**
2727
 * Implements hook_views_api().
2728
 *
2729
 * This one is used as the base to reduce errors when updating.
2730
 */
2731
function views_views_api() {
2732
  return array(
2733
    // In your modules do *not* use views_api_version()!!!
2734
    'api' => views_api_version(),
2735
    'path' => drupal_get_path('module', 'views') . '/modules',
2736
  );
2737
}
2738

    
2739
/**
2740
 * Implements hook_admin_menu_cache_info().
2741
 */
2742
function views_admin_menu_cache_info() {
2743
  $caches['views'] = array(
2744
    'title' => t('Views'),
2745
    'callback' => 'views_invalidate_cache',
2746
  );
2747
  return $caches;
2748
}
2749

    
2750
if (!function_exists('aggregator_views_api')) {
2751
  /**
2752
   * Provide Views integration for the Aggregator module.
2753
   */
2754
  function aggregator_views_api() {
2755
    return views_views_api();
2756
  }
2757
}
2758

    
2759
if (!function_exists('book_views_api')) {
2760
  /**
2761
   * Provide Views integration for the Book module.
2762
   */
2763
  function book_views_api() {
2764
    return views_views_api();
2765
  }
2766
}
2767

    
2768
if (!function_exists('comment_views_api')) {
2769
  /**
2770
   * Provide Views integration for the Comment module.
2771
   */
2772
  function comment_views_api() {
2773
    return views_views_api();
2774
  }
2775
}
2776

    
2777
if (!function_exists('field_views_api')) {
2778
  /**
2779
   * Provide Views integration for the Field module.
2780
   */
2781
  function field_views_api() {
2782
    return views_views_api();
2783
  }
2784
}
2785

    
2786
if (!function_exists('file_views_api')) {
2787
  /**
2788
   * Provide Views integration for the File module.
2789
   */
2790
  function file_views_api() {
2791
    return views_views_api();
2792
  }
2793
}
2794

    
2795
if (!function_exists('filter_views_api')) {
2796
  /**
2797
   * Provide Views integration for the Filter module.
2798
   */
2799
  function filter_views_api() {
2800
    return views_views_api();
2801
  }
2802
}
2803

    
2804
if (!function_exists('image_views_api')) {
2805
  /**
2806
   * Provide Views integration for the Image module.
2807
   */
2808
  function image_views_api() {
2809
    return views_views_api();
2810
  }
2811
}
2812

    
2813
if (!function_exists('locale_views_api')) {
2814
  /**
2815
   * Provide Views integration for the Locale module.
2816
   */
2817
  function locale_views_api() {
2818
    return views_views_api();
2819
  }
2820
}
2821

    
2822
if (!function_exists('node_views_api')) {
2823
  /**
2824
   * Provide Views integration for the Node module.
2825
   */
2826
  function node_views_api() {
2827
    return views_views_api();
2828
  }
2829
}
2830

    
2831
if (!function_exists('poll_views_api')) {
2832
  /**
2833
   * Provide Views integration for the Poll module.
2834
   */
2835
  function poll_views_api() {
2836
    return views_views_api();
2837
  }
2838
}
2839

    
2840
if (!function_exists('profile_views_api')) {
2841
  /**
2842
   * Provide Views integration for the Profile module.
2843
   */
2844
  function profile_views_api() {
2845
    return views_views_api();
2846
  }
2847
}
2848

    
2849
if (!function_exists('search_views_api')) {
2850
  /**
2851
   * Provide Views integration for the Search module.
2852
   */
2853
  function search_views_api() {
2854
    return views_views_api();
2855
  }
2856
}
2857

    
2858
if (!function_exists('statistics_views_api')) {
2859
  /**
2860
   * Provide Views integration for the Statistics module.
2861
   */
2862
  function statistics_views_api() {
2863
    return views_views_api();
2864
  }
2865
}
2866

    
2867
if (!function_exists('system_views_api')) {
2868
  /**
2869
   * Provide Views integration for the System module.
2870
   */
2871
  function system_views_api() {
2872
    return views_views_api();
2873
  }
2874
}
2875

    
2876
if (!function_exists('tracker_views_api')) {
2877
  /**
2878
   * Provide Views integration for the Tracker module.
2879
   */
2880
  function tracker_views_api() {
2881
    return views_views_api();
2882
  }
2883
}
2884

    
2885
if (!function_exists('taxonomy_views_api')) {
2886
  /**
2887
   * Provide Views integration for the Taxonomy module.
2888
   */
2889
  function taxonomy_views_api() {
2890
    return views_views_api();
2891
  }
2892
}
2893

    
2894
if (!function_exists('translation_views_api')) {
2895
  /**
2896
   * Provide Views integration for the Translation module.
2897
   */
2898
  function translation_views_api() {
2899
    return views_views_api();
2900
  }
2901
}
2902

    
2903
if (!function_exists('user_views_api')) {
2904
  /**
2905
   * Provide Views integration for the User module.
2906
   */
2907
  function user_views_api() {
2908
    return views_views_api();
2909
  }
2910
}
2911

    
2912
if (!function_exists('contact_views_api')) {
2913
  /**
2914
   * Provide Views integration for the Contact module.
2915
   */
2916
  function contact_views_api() {
2917
    return views_views_api();
2918
  }
2919
}