#873238 follow up by dereine: Remove adding cookie.js all the time. Ajax loading...
[project/views.git] / views.module
CommitLineData
31dd0540
TGGM
1<?php
2// $Id$
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 * Advertise the current views api version
14 */
15function views_api_version() {
a8f0683e
DW
16 return '3.0-alpha1';
17}
18
19/**
20 * Views will not load plugins advertising a version older than this.
21 */
22function views_api_minimum_version() {
23 return '2';
31dd0540
TGGM
24}
25
26/**
80e25196
DW
27 * Implement hook_init().
28 */
29function views_init() {
30 drupal_add_css(drupal_get_path('module', 'views') .'/css/views.css');
31}
32
33/**
2dd76fe0 34 * Implement hook_theme(). Register views theming functions.
31dd0540 35 */
9cab79ea 36function views_theme($existing, $type, $theme, $path) {
31dd0540 37 $path = drupal_get_path('module', 'views');
2dd76fe0 38 include_once $path . '/theme/theme.inc';
31dd0540
TGGM
39
40 // Some quasi clever array merging here.
41 $base = array(
42 'file' => 'theme.inc',
2dd76fe0 43 'path' => $path . '/theme',
31dd0540
TGGM
44 );
45
46 // Our extra version of pager from pager.inc
47 $hooks['views_mini_pager'] = $base + array(
ac9dea05 48 'variables' => array('tags' => array(), 'quantity' => 10, 'element' => 0, 'parameters' => array()),
31dd0540
TGGM
49 'pattern' => 'views_mini_pager__',
50 );
51
52 $arguments = array(
53 'display' => array('view' => NULL),
54 'style' => array('view' => NULL, 'options' => NULL, 'rows' => NULL, 'title' => NULL),
55 'row' => array('view' => NULL, 'options' => NULL, 'row' => NULL, 'field_alias' => NULL),
56 );
57
58 // Default view themes
59 $hooks['views_view_field'] = $base + array(
60 'pattern' => 'views_view_field__',
3c45ad76 61 'variables' => array('view' => NULL, 'field' => NULL, 'row' => NULL),
31dd0540
TGGM
62 );
63
64 $plugins = views_fetch_plugin_data();
65
66 // Register theme functions for all style plugins
67 foreach ($plugins as $type => $info) {
68 foreach ($info as $plugin => $def) {
69 if (isset($def['theme'])) {
70 $hooks[$def['theme']] = array(
71 'pattern' => $def['theme'] . '__',
72 'file' => $def['theme file'],
73 'path' => $def['theme path'],
3c45ad76 74 'variables' => $arguments[$type],
31dd0540
TGGM
75 );
76
2dd76fe0 77 $include = DRUPAL_ROOT . '/' . $def['theme path'] . '/' . $def['theme file'];
31dd0540
TGGM
78 if (file_exists($include)) {
79 require_once $include;
80 }
81
82 if (!function_exists('theme_' . $def['theme'])) {
5be494e5 83 $hooks[$def['theme']]['template'] = drupal_clean_css_identifier($def['theme']);
31dd0540
TGGM
84 }
85 }
86 if (isset($def['additional themes'])) {
87 foreach ($def['additional themes'] as $theme => $theme_type) {
88 if (empty($theme_type)) {
89 $theme = $theme_type;
90 $theme_type = $type;
91 }
92
93 $hooks[$theme] = array(
94 'pattern' => $theme . '__',
95 'file' => $def['theme file'],
96 'path' => $def['theme path'],
3c45ad76 97 'variables' => $arguments[$theme_type],
31dd0540
TGGM
98 );
99
100 if (!function_exists('theme_' . $theme)) {
5be494e5 101 $hooks[$theme]['template'] = drupal_clean_css_identifier($theme);
31dd0540
TGGM
102 }
103 }
104 }
105 }
106 }
107
108 $hooks['views_exposed_form'] = $base + array(
109 'template' => 'views-exposed-form',
110 'pattern' => 'views_exposed_form__',
3c45ad76 111 'render element' => 'form',
31dd0540
TGGM
112 );
113
114 $hooks['views_more'] = $base + array(
115 'template' => 'views-more',
116 'pattern' => 'views_more__',
3c45ad76 117 'variables' => array('more_url' => NULL, 'link_text' => 'more'),
31dd0540 118 );
9cab79ea
DW
119
120 // Add theme suggestions which are part of modules.
121 foreach (views_get_module_apis() as $info) {
122 if (isset($info['template path'])) {
123 $hooks += _views_find_module_templates($hooks, $info['template path']);
124 }
125 }
31dd0540
TGGM
126 return $hooks;
127}
128
129/**
9cab79ea
DW
130 * Scans a directory of a module for template files.
131 *
132 * @param $cache
133 * The existing cache of theme hooks to test against.
134 * @param $path
135 * The path to search.
136 *
137 * @see drupal_find_theme_templates
138 */
139function _views_find_module_templates($cache, $path) {
ce243f57 140 $regex = '/' . '\.tpl\.php' . '$' . '/';
9cab79ea
DW
141
142 // Because drupal_system_listing works the way it does, we check for real
143 // templates separately from checking for patterns.
144 $files = drupal_system_listing($regex, $path, 'name', 0);
9cab79ea
DW
145 foreach ($files as $template => $file) {
146 // Chop off the remaining extensions if there are any. $template already
147 // has the rightmost extension removed, but there might still be more,
148 // such as with .tpl.php, which still has .tpl in $template at this point.
149 if (($pos = strpos($template, '.')) !== FALSE) {
150 $template = substr($template, 0, $pos);
151 }
152 // Transform - in filenames to _ to match function naming scheme
153 // for the purposes of searching.
154 $hook = strtr($template, '-', '_');
155 if (isset($cache[$hook])) {
156 $templates[$hook] = array(
157 'template' => $template,
158 'path' => dirname($file->filename),
ce243f57 159 'includes' => isset($cache[$hook]['includes']) ? $cache[$hook]['includes'] : NULL,
9cab79ea
DW
160 );
161 }
162 // Ensure that the pattern is maintained from base themes to its sub-themes.
163 // Each sub-theme will have their templates scanned so the pattern must be
164 // held for subsequent runs.
165 if (isset($cache[$hook]['pattern'])) {
166 $templates[$hook]['pattern'] = $cache[$hook]['pattern'];
167 }
168 }
169
170 $patterns = array_keys($files);
171
9cab79ea
DW
172 foreach ($cache as $hook => $info) {
173 if (!empty($info['pattern'])) {
174 // Transform _ in pattern to - to match file naming scheme
175 // for the purposes of searching.
176 $pattern = strtr($info['pattern'], '_', '-');
177
178 $matches = preg_grep('/^'. $pattern .'/', $patterns);
179 if ($matches) {
180 foreach ($matches as $match) {
181 $file = substr($match, 0, strpos($match, '.'));
182 // Put the underscores back in for the hook name and register this pattern.
9cab79ea
DW
183 $templates[strtr($file, '-', '_')] = array(
184 'template' => $file,
185 'path' => dirname($files[$match]->filename),
ce243f57 186 'variables' => $info['variables'],
d0fdb348 187 'base hook' => $hook,
ce243f57 188 'includes' => isset($info['includes']) ? $info['includes'] : NULL,
9cab79ea
DW
189 );
190 }
191 }
192 }
193 }
194
195 return $templates;
196}
197
198/**
31dd0540
TGGM
199 * A theme preprocess function to automatically allow view-based node
200 * templates if called from a view.
201 *
202 * The 'modules/node.views.inc' file is a better place for this, but
203 * we haven't got a chance to load that file before Drupal builds the
204 * node portion of the theme registry.
205 */
206function views_preprocess_node(&$vars) {
207 // The 'view' attribute of the node is added in template_preprocess_views_view_row_node()
208 if (!empty($vars['node']->view) && !empty($vars['node']->view->name)) {
209 $vars['view'] = &$vars['node']->view;
742335fc 210 $vars['theme_hook_suggestions'][] = 'node__view__' . $vars['node']->view->name;
2dd76fe0 211 if (!empty($vars['node']->view->current_display)) {
742335fc 212 $vars['theme_hook_suggestions'][] = 'node__view__' . $vars['node']->view->name . '__' . $vars['node']->view->current_display;
31dd0540
TGGM
213 }
214 }
215}
216
217/**
218 * A theme preprocess function to automatically allow view-based node
219 * templates if called from a view.
220 */
221function views_preprocess_comment(&$vars) {
222 // The 'view' attribute of the node is added in template_preprocess_views_view_row_comment()
223 if (!empty($vars['node']->view) && !empty($vars['node']->view->name)) {
224 $vars['view'] = &$vars['node']->view;
742335fc 225 $vars['theme_hook_suggestions'][] = 'comment__view__' . $vars['node']->view->name;
2dd76fe0 226 if (!empty($vars['node']->view->current_display)) {
742335fc 227 $vars['theme_hook_suggestions'][] = 'comment__view__' . $vars['node']->view->name . '__' . $vars['node']->view->current_display;
31dd0540
TGGM
228 }
229 }
230}
231
232/*
2dd76fe0 233 * Implement hook_permission().
31dd0540 234 */
2dd76fe0
EM
235function views_permission() {
236 return array(
237 'administer views' => array(
238 'title' => t('Administer views'),
239 'description' => t('Access the views administration pages.'),
240 ),
241 'access all views' => array(
242 'title' => t('Access all views'),
243 'description' => t('Bypass access control when accessing views.'),
244 ),
245 );
31dd0540
TGGM
246}
247
248/**
2dd76fe0 249 * Implement hook_menu().
31dd0540
TGGM
250 */
251function views_menu() {
252 // Any event which causes a menu_rebuild could potentially mean that the
253 // Views data is updated -- module changes, profile changes, etc.
254 views_invalidate_cache();
255 $items = array();
256 $items['views/ajax'] = array(
257 'title' => 'Views',
258 'page callback' => 'views_ajax',
259 'access callback' => TRUE,
260 'description' => 'Ajax callback for view loading.',
31dd0540
TGGM
261 'type' => MENU_CALLBACK,
262 );
2dd76fe0 263 // Path is not admin/structure/views due to menu complications with the wildcards from
31dd0540
TGGM
264 // the generic ajax callback.
265 $items['admin/views/ajax/autocomplete/user'] = array(
266 'page callback' => 'views_ajax_autocomplete_user',
267 'access callback' => 'user_access',
268 'access arguments' => array('access content'),
31dd0540 269 'type' => MENU_CALLBACK,
2dd76fe0 270 'file' => 'includes/ajax.inc',
31dd0540 271 );
bd3ad3ed
DW
272 // Define another taxonomy autocomplete because the default one of drupal
273 // does not support a vid a argument anymore
274 $items['admin/views/ajax/autocomplete/taxonomy'] = array(
275 'page callback' => 'views_ajax_autocomplete_taxonomy',
276 'access callback' => 'user_access',
277 'access arguments' => array('access content'),
278 'type' => MENU_CALLBACK,
279 'file' => 'includes/ajax.inc',
280 );
31dd0540
TGGM
281 return $items;
282}
283
284/**
2dd76fe0 285 * Implement hook_menu_alter().
31dd0540
TGGM
286 */
287function views_menu_alter(&$callbacks) {
288 $our_paths = array();
289 $views = views_get_applicable_views('uses hook menu');
290 foreach ($views as $data) {
291 list($view, $display_id) = $data;
58f55929 292 $result = $view->execute_hook_menu($display_id, $callbacks);
31dd0540
TGGM
293 if (is_array($result)) {
294 // The menu system doesn't support having two otherwise
295 // identical paths with different placeholders. So we
296 // want to remove the existing items from the menu whose
297 // paths would conflict with ours.
298
299 // First, we must find any existing menu items that may
300 // conflict. We use a regular expression because we don't
301 // know what placeholders they might use. Note that we
302 // first construct the regex itself by replacing %views_arg
303 // in the display path, then we use this constructed regex
304 // (which will be something like '#^(foo/%[^/]*/bar)$#') to
305 // search through the existing paths.
306 $regex = '#^(' . preg_replace('#%views_arg#', '%[^/]*', implode('|', array_keys($result))) . ')$#';
307 $matches = preg_grep($regex, array_keys($callbacks));
308
309 // Remove any conflicting items that were found.
310 foreach ($matches as $path) {
311 // Don't remove the paths we just added!
312 if (!isset($our_paths[$path])) {
313 unset($callbacks[$path]);
314 }
315 }
316 foreach ($result as $path => $item) {
317 if (!isset($callbacks[$path])) {
318 // Add a new item, possibly replacing (and thus effectively
319 // overriding) one that we removed above.
320 $callbacks[$path] = $item;
321 }
322 else {
323 // This item already exists, so it must be one that we added.
324 // We change the various callback arguments to pass an array
325 // of possible display IDs instead of a single ID.
326 $callbacks[$path]['page arguments'][1] = (array)$callbacks[$path]['page arguments'][1];
327 $callbacks[$path]['page arguments'][1][] = $display_id;
328 $callbacks[$path]['access arguments'][] = $item['access arguments'][0];
329 $callbacks[$path]['load arguments'][1] = (array)$callbacks[$path]['load arguments'][1];
330 $callbacks[$path]['load arguments'][1][] = $display_id;
331 }
332 $our_paths[$path] = TRUE;
333 }
334 }
335 }
336
337 // Save memory: Destroy those views.
338 foreach ($views as $data) {
339 list($view, $display_id) = $data;
340 $view->destroy();
341 }
342}
343
344/**
345 * Helper function for menu loading. This will automatically be
346 * called in order to 'load' a views argument; primarily it
347 * will be used to perform validation.
348 *
349 * @param $value
350 * The actual value passed.
351 * @param $name
352 * The name of the view. This needs to be specified in the 'load function'
353 * of the menu entry.
f9c4cee0
EM
354 * @param $display_id
355 * The display id that will be loaded for this menu item.
31dd0540
TGGM
356 * @param $index
357 * The menu argument index. This counts from 1.
358 */
359function views_arg_load($value, $name, $display_id, $index) {
f9c4cee0
EM
360 static $views = array();
361
362 // Make sure we haven't already loaded this views argument for a similar menu
363 // item elsewhere.
364 $key = $name . ':' . $display_id . ':' . $value . ':' . $index;
365 if (isset($views[$key])) {
366 return $views[$key];
367 }
368
31dd0540
TGGM
369 if ($view = views_get_view($name)) {
370 $view->set_display($display_id);
371 $view->init_handlers();
372
373 $ids = array_keys($view->argument);
374
375 $indexes = array();
376 $path = explode('/', $view->get_path());
377
378 foreach ($path as $id => $piece) {
379 if ($piece == '%' && !empty($ids)) {
380 $indexes[$id] = array_shift($ids);
381 }
382 }
383
384 if (isset($indexes[$index])) {
385 if (isset($view->argument[$indexes[$index]])) {
386 $arg = $view->argument[$indexes[$index]]->validate_argument($value) ? $value : FALSE;
387 $view->destroy();
f9c4cee0
EM
388
389 // Store the output in case we load this same menu item again.
390 $views[$key] = $arg;
31dd0540
TGGM
391 return $arg;
392 }
393 }
394 $view->destroy();
395 }
396}
397
398/**
399 * Page callback entry point; requires a view and a display id, then
400 * passes control to the display handler.
401 */
402function views_page() {
403 $args = func_get_args();
404 $name = array_shift($args);
405 $display_id = array_shift($args);
406
407 // Load the view
408 if ($view = views_get_view($name)) {
409 return $view->execute_display($display_id, $args);
410 }
411
412 // Fallback; if we get here no view was found or handler was not valid.
413 return drupal_not_found();
414}
415
416/**
ea9088df 417 * Implement hook_block_info().
31dd0540 418 */
ea9088df 419function views_block_info() {
9f1018b7
EM
420 // Try to avoid instantiating all the views just to get the blocks info.
421 views_include('cache');
422 $cache = views_cache_get('views_block_items', TRUE);
423 if ($cache && is_array($cache->data)) {
424 return $cache->data;
425 }
426
2dd76fe0
EM
427 $items = array();
428 $views = views_get_all_views();
429 foreach ($views as $view) {
430 // disabled views get nothing.
431 if (!empty($view->disabled)) {
432 continue;
433 }
31dd0540 434
2dd76fe0
EM
435 $view->init_display();
436 foreach ($view->display as $display_id => $display) {
31dd0540 437
2dd76fe0
EM
438 if (isset($display->handler) && !empty($display->handler->definition['uses hook block'])) {
439 $result = $display->handler->execute_hook_block_list();
440 if (is_array($result)) {
441 $items = array_merge($items, $result);
31dd0540
TGGM
442 }
443 }
444
2dd76fe0
EM
445 if (isset($display->handler) && $display->handler->get_option('exposed_block')) {
446 $result = $display->handler->get_special_blocks();
447 if (is_array($result)) {
448 $items = array_merge($items, $result);
31dd0540
TGGM
449 }
450 }
2dd76fe0
EM
451 }
452 }
31dd0540 453
2dd76fe0
EM
454 // block.module has a delta length limit of 32, but our deltas can
455 // unfortunately be longer because view names can be 32 and display IDs
456 // can also be 32. So for very long deltas, change to md5 hashes.
457 $hashes = array();
458
459 // get the keys because we're modifying the array and we don't want to
460 // confuse PHP too much.
461 $keys = array_keys($items);
462 foreach ($keys as $delta) {
463 if (strlen($delta) >= 32) {
464 $hash = md5($delta);
465 $hashes[$hash] = $delta;
466 $items[$hash] = $items[$delta];
467 unset($items[$delta]);
468 }
469 }
31dd0540 470
593e6ae6
DW
471 // Only save hashes if they have changed.
472 $old_hashes = variable_get('views_block_hashes', array());
473 if ($hashes != $old_hashes) {
474 variable_set('views_block_hashes', $hashes);
475 }
2dd76fe0
EM
476 // Save memory: Destroy those views.
477 foreach ($views as $view) {
478 $view->destroy();
479 }
31dd0540 480
9f1018b7
EM
481 views_cache_set('views_block_items', $items, TRUE);
482
2dd76fe0
EM
483 return $items;
484}
485
486/**
487 * Implement hook_block_view().
488 */
489function views_block_view($delta) {
490 $start = microtime(TRUE);
491 // if this is 32, this should be an md5 hash.
492 if (strlen($delta) == 32) {
493 $hashes = variable_get('views_block_hashes', array());
494 if (!empty($hashes[$delta])) {
495 $delta = $hashes[$delta];
496 }
497 }
31dd0540 498
2dd76fe0
EM
499 // This indicates it's a special one.
500 if (substr($delta, 0, 1) == '-') {
501 list($nothing, $type, $name, $display_id) = explode('-', $delta);
502 // Put the - back on.
503 $type = '-' . $type;
504 if ($view = views_get_view($name)) {
505 if ($view->access($display_id)) {
506 $view->set_display($display_id);
507 if (isset($view->display_handler)) {
508 $output = $view->display_handler->view_special_blocks($type);
31dd0540
TGGM
509 $view->destroy();
510 return $output;
511 }
31dd0540 512 }
2dd76fe0
EM
513 $view->destroy();
514 }
515 }
516
517 list($name, $display_id) = explode('-', $delta);
518 // Load the view
519 if ($view = views_get_view($name)) {
520 if ($view->access($display_id)) {
521 $output = $view->execute_display($display_id);
2dd76fe0
EM
522 $view->destroy();
523 return $output;
524 }
525 $view->destroy();
31dd0540
TGGM
526 }
527}
528
529/**
50de0ded 530 * Implements hook_flush_caches().
31dd0540
TGGM
531 */
532function views_flush_caches() {
533 return array('cache_views', 'cache_views_data');
534}
535
536/**
50de0ded
DW
537 * Implements hook_field_create_instance.
538 */
539function views_field_create_instance($instance) {
540 cache_clear_all('*', 'cache_views', TRUE);
541 cache_clear_all('*', 'cache_views_data', TRUE);
542}
543
544/**
545 * Implements hook_field_update_instance.
546 */
547function views_field_update_instance($instance, $prior_instance) {
548 cache_clear_all('*', 'cache_views', TRUE);
549 cache_clear_all('*', 'cache_views_data', TRUE);
550}
551
552/**
553 * Implements hook_field_delete_instance.
554 */
555function views_field_delete_instance($instance) {
556 cache_clear_all('*', 'cache_views', TRUE);
557 cache_clear_all('*', 'cache_views_data', TRUE);
558}
559
560/**
31dd0540
TGGM
561 * Invalidate the views cache, forcing a rebuild on the next grab of table data.
562 */
563function views_invalidate_cache() {
2dd76fe0 564 cache_clear_all('*', 'cache_views', TRUE);
31dd0540
TGGM
565}
566
567/**
2b31a2d7
EM
568 * Access callback to determine if the user can import Views.
569 *
570 * View imports require an additional access check because they are PHP
571 * code and PHP is more locked down than administer views.
572 */
573function views_import_access() {
0ef49a5b 574 return user_access('administer views') && user_access('use PHP for settings');
2b31a2d7
EM
575}
576
577/**
31dd0540
TGGM
578 * Determine if the logged in user has access to a view.
579 *
580 * This function should only be called from a menu hook or some other
581 * embedded source. Each argument is the result of a call to
582 * views_plugin_access::get_access_callback() which is then used
583 * to determine if that display is accessible. If *any* argument
584 * is accessible, then the view is accessible.
585 */
586function views_access() {
31dd0540
TGGM
587 $args = func_get_args();
588 foreach ($args as $arg) {
589 if ($arg === TRUE) {
590 return TRUE;
591 }
592
593 if (!is_array($arg)) {
594 continue;
595 }
596
597 list($callback, $arguments) = $arg;
5e2c1962 598 $arguments = $arguments ? $arguments : array();
31dd0540
TGGM
599 if (function_exists($callback) && call_user_func_array($callback, $arguments)) {
600 return TRUE;
601 }
602 }
603
604 return FALSE;
605}
606
607/**
5eb98d56 608 * Access callback for the views_plugin_access_perm access plugin.
31dd0540 609 *
5eb98d56
EM
610 * Determine if the specified user has access to a view on the basis of
611 * permissions. If the $account argument is omitted, the current user
612 * is used.
613 */
614function views_check_perm($perm, $account = NULL) {
615 return user_access($perm, $account) || user_access('access all views', $account);
616}
617
618/**
619 * Access callback for the views_plugin_access_role access plugin.
620
621 * Determine if the specified user has access to a view on the basis of any of
622 * the requested roles. If the $account argument is omitted, the current user
623 * is used.
31dd0540 624 */
5eb98d56 625function views_check_roles($rids, $account = NULL) {
31dd0540 626 global $user;
5eb98d56
EM
627 $account = isset($account) ? $account : $user;
628 $roles = array_keys($account->roles);
629 $roles[] = $account->uid ? DRUPAL_AUTHENTICATED_RID : DRUPAL_ANONYMOUS_RID;
630 return user_access('access all views', $account) || array_intersect(array_filter($rids), $roles);
31dd0540
TGGM
631}
632// ------------------------------------------------------------------
633// Functions to help identify views that are running or ran
634
635/**
636 * Set the current 'page view' that is being displayed so that it is easy
637 * for other modules or the theme to identify.
638 */
639function &views_set_page_view($view = NULL) {
640 static $cache = NULL;
641 if (isset($view)) {
642 $cache = $view;
643 }
644
645 return $cache;
646}
647
648/**
649 * Find out what, if any, page view is currently in use. Please note that
650 * this returns a reference, so be careful! You can unintentionally modify the
651 * $view object.
652 */
653function &views_get_page_view() {
654 return views_set_page_view();
655}
656
657/**
658 * Set the current 'current view' that is being built/rendered so that it is
659 * easy for other modules or items in drupal_eval to identify
660 */
661function &views_set_current_view($view = NULL) {
662 static $cache = NULL;
663 if (isset($view)) {
664 $cache = $view;
665 }
666
667 return $cache;
668}
669
670/**
671 * Find out what, if any, current view is currently in use. Please note that
672 * this returns a reference, so be careful! You can unintentionally modify the
673 * $view object.
674 */
675function &views_get_current_view() {
676 return views_set_current_view();
677}
678
679// ------------------------------------------------------------------
680// Include file helpers
681
682/**
683 * Include views .inc files as necessary.
684 */
685function views_include($file) {
2dd76fe0 686 module_load_include('inc', 'views', "includes/$file");
31dd0540
TGGM
687}
688
689/**
690 * Load views files on behalf of modules.
691 */
8e218614
DW
692function views_module_include($file, $reset = FALSE) {
693 foreach (views_get_module_apis($reset) as $module => $info) {
2dd76fe0
EM
694 if (file_exists(DRUPAL_ROOT . "/$info[path]/$module.$file")) {
695 require_once DRUPAL_ROOT . "/$info[path]/$module.$file";
31dd0540
TGGM
696 }
697 }
698}
699
700/**
701 * Get a list of modules that support the current views API.
702 */
8e218614 703function views_get_module_apis($reset = FALSE) {
31dd0540 704 static $cache = NULL;
8e218614 705 if (!isset($cache) || $reset) {
31dd0540
TGGM
706 $cache = array();
707 foreach (module_implements('views_api') as $module) {
2dd76fe0 708 $info = module_invoke($module, 'views_api');
a8f0683e
DW
709 if (version_compare($info['api'], views_api_minimum_version(), '>=') &&
710 version_compare($info['api'], views_api_version(), '<=')) {
a89254f5
DW
711 if (!isset($info['path'])) {
712 $info['path'] = drupal_get_path('module', $module);
713 }
a8f0683e 714 $cache[$module] = $info;
31dd0540
TGGM
715 }
716 }
717 }
718
719 return $cache;
720}
721
722/**
723 * Include views .css files.
724 */
725function views_add_css($file) {
726 // We set preprocess to FALSE because we are adding the files conditionally,
727 // and we don't want to generate duplicate cache files.
728 // TODO: at some point investigate adding some files unconditionally and
729 // allowing preprocess.
2dd76fe0 730 drupal_add_css(drupal_get_path('module', 'views') . "/css/$file.css", array('preprocess' => FALSE));
31dd0540
TGGM
731}
732
733/**
734 * Include views .js files.
735 */
736function views_add_js($file) {
737 // If javascript has been disabled by the user, never add js files.
738 if (variable_get('views_no_javascript', FALSE)) {
739 return;
740 }
741
2dd76fe0 742 static $base = TRUE, $ajax = TRUE;
31dd0540
TGGM
743 if ($base) {
744 drupal_add_js(drupal_get_path('module', 'views') . "/js/base.js");
745 $base = FALSE;
746 }
2dd76fe0 747 if ($ajax && $file == 'ajax') {
1c7d6fba 748 drupal_add_js('misc/ajax.js', array('group' => JS_LIBRARY));
2dd76fe0
EM
749 $ajax = TRUE;
750 }
31dd0540
TGGM
751 drupal_add_js(drupal_get_path('module', 'views') . "/js/$file.js");
752}
753
754/**
755 * Load views files on behalf of modules.
756 */
8e218614 757function views_include_handlers($reset = FALSE) {
31dd0540
TGGM
758 static $finished = FALSE;
759 // Ensure this only gets run once.
8e218614 760 if ($finished && !$reset) {
31dd0540
TGGM
761 return;
762 }
763
764 views_include('base');
765 views_include('handlers');
766 views_include('cache');
767 views_include('plugins');
768 _views_include_handlers();
769 $finished = TRUE;
770}
771
772/**
773 * Load default views files on behalf of modules.
774 */
8e218614 775function views_include_default_views($reset = FALSE) {
31dd0540
TGGM
776 static $finished = FALSE;
777 // Ensure this only gets run once.
8e218614 778 if ($finished && !$reset) {
31dd0540
TGGM
779 return;
780 }
781
782 // Default views hooks may be in the normal handler file,
783 // or in a separate views_default file at the discretion of
784 // the module author.
8e218614 785 views_include_handlers($reset);
31dd0540 786
8e218614 787 _views_include_default_views($reset);
31dd0540
TGGM
788 $finished = TRUE;
789}
790
791// -----------------------------------------------------------------------
792// Views handler functions
793
794/**
795 * Fetch a handler from the data cache.
796 *
797 * @param $table
798 * The name of the table this handler is from.
799 * @param $field
800 * The name of the field this handler is from.
801 * @param $key
802 * The type of handler. i.e, sort, field, argument, filter, relationship
a8f0683e
DW
803 * @param $override
804 * Override the actual handler object with this class. Used for
805 * aggregation when the handler is redirected to the aggregation
806 * handler.
31dd0540
TGGM
807 *
808 * @return
809 * An instance of a handler object. May be views_handler_broken.
810 */
a8f0683e 811function views_get_handler($table, $field, $key, $override = NULL) {
31dd0540 812 $data = views_fetch_data($table);
a8f0683e
DW
813 $handler = NULL;
814
31dd0540
TGGM
815 if (isset($data[$field][$key])) {
816 // Set up a default handler:
817 if (empty($data[$field][$key]['handler'])) {
818 $data[$field][$key]['handler'] = 'views_handler_' . $key;
819 }
a8f0683e
DW
820
821 if ($override) {
822 $data[$field][$key]['override handler'] = $override;
823 }
824
825 $handler = _views_prepare_handler($data[$field][$key], $data, $field);
31dd0540 826 }
a8f0683e
DW
827
828 if ($handler) {
829 return $handler;
830 }
831
31dd0540 832 // DEBUG -- identify missing handlers
2dd76fe0 833 debug("Missing handler: $table $field $key");
31dd0540
TGGM
834 $broken = array(
835 'title' => t('Broken handler @table.@field', array('@table' => $table, '@field' => $field)),
836 'handler' => 'views_handler_' . $key . '_broken',
837 'table' => $table,
838 'field' => $field,
839 );
840 return _views_create_handler($broken);
841}
842
843/**
844 * Fetch Views' data from the cache
845 */
8e218614 846function views_fetch_data($table = NULL, $reset = FALSE) {
31dd0540 847 views_include('cache');
8e218614 848 return _views_fetch_data($table, $reset);
31dd0540
TGGM
849}
850
851// -----------------------------------------------------------------------
852// Views plugin functions
853
854/**
855 * Fetch the plugin data from cache.
856 */
8e218614 857function views_fetch_plugin_data($type = NULL, $plugin = NULL, $reset = FALSE) {
31dd0540 858 views_include('cache');
8e218614 859 return _views_fetch_plugin_data($type, $plugin, $reset);
31dd0540
TGGM
860}
861
862/**
863 * Get a handler for a plugin
864 */
8e218614 865function views_get_plugin($type, $plugin, $reset = FALSE) {
2dd76fe0 866 views_include('handlers');
8e218614 867 $definition = views_fetch_plugin_data($type, $plugin, $reset);
31dd0540
TGGM
868 if (!empty($definition)) {
869 return _views_create_handler($definition, $type);
870 }
871}
872
873// -----------------------------------------------------------------------
874// Views database functions
875
876/**
877 * Get a view from the default views defined by modules.
878 *
879 * Default views are cached per-language. This function will rescan the
880 * default_views hook if necessary.
881 *
882 * @param $view_name
883 * The name of the view to load.
884 * @return
885 * A view object or NULL if it is not available.
886 */
0dc1ea54 887function &views_get_default_view($view_name, $reset = FALSE) {
31dd0540 888 $null = NULL;
31dd0540 889
7ba86678
EM
890 // Attempt to load individually cached view from cache.
891 views_include('cache');
0dc1ea54
EM
892 if (!$reset) {
893 $data = views_cache_get("views_default:{$view_name}", TRUE);
894 if (isset($data->data) && is_object($data->data)) {
895 return $data->data;
896 }
7ba86678
EM
897 }
898
899 // Otherwise, allow entire cache to be rebuilt.
0dc1ea54 900 $cache = views_discover_default_views($reset);
31dd0540
TGGM
901 if (isset($cache[$view_name])) {
902 return $cache[$view_name];
903 }
904 return $null;
905}
906
907/**
908 * Create an empty view to work with.
909 *
910 * @return
911 * A fully formed, empty $view object. This object must be populated before
912 * it can be successfully saved.
913 */
914function views_new_view() {
915 views_include('view');
916 $view = new view();
917 $view->vid = 'new';
918 $view->add_display('default');
919
920 return $view;
921}
922
923/**
924 * Scan all modules for default views and rebuild the default views cache.
925 *
926 * @return An associative array of all known default views.
927 */
0dc1ea54 928function views_discover_default_views($reset = FALSE) {
31dd0540
TGGM
929 static $cache = array();
930
0dc1ea54 931 if (empty($cache) || $reset) {
31dd0540 932 views_include('cache');
0dc1ea54 933 $cache = _views_discover_default_views($reset);
31dd0540
TGGM
934 }
935 return $cache;
936}
937
938/**
939 * Return a list of all views and display IDs that have a particular
940 * setting in their display's plugin settings.
941 *
942 * @return
943 * @code
944 * array(
945 * array($view, $display_id),
946 * array($view, $display_id),
947 * );
948 * @endcode
949 */
950function views_get_applicable_views($type) {
951 // @todo: Use a smarter flagging system so that we don't have to
952 // load every view for this.
953 $result = array();
954 $views = views_get_all_views();
955
956 foreach ($views as $view) {
957 // Skip disabled views.
958 if (!empty($view->disabled)) {
959 continue;
960 }
961
962 if (empty($view->display)) {
963 // Skip this view as it is broken.
964 vsm(t("Skipping broken view @view", array('@view' => $view->name)));
965 continue;
966 }
967
968 // Loop on array keys because something seems to muck with $view->display
969 // a bit in PHP4.
970 foreach (array_keys($view->display) as $id) {
971 $plugin = views_fetch_plugin_data('display', $view->display[$id]->display_plugin);
972 if (!empty($plugin[$type])) {
973 // This view uses hook menu. Clone it so that different handlers
974 // don't trip over each other, and add it to the list.
975 $v = $view->clone_view();
976 if ($v->set_display($id)) {
977 $result[] = array($v, $id);
978 }
979 // In PHP 4.4.7 and presumably earlier, if we do not unset $v
980 // here, we will find that it actually overwrites references
981 // possibly due to shallow copying issues.
982 unset($v);
983 }
984 }
985 }
986 return $result;
987}
988
989/**
990 * Return an array of all views as fully loaded $view objects.
991 *
992 * @param $reset
993 * If TRUE, reset the static cache forcing views to be reloaded.
994 */
995function views_get_all_views($reset = FALSE) {
996 static $views = array();
997
998 if (empty($views) || $reset) {
999 $views = array();
1000
1001 // First, get all applicable views.
1002 views_include('view');
1003 $views = view::load_views();
1004
1005 // Get all default views.
1006 $status = variable_get('views_defaults', array());
1007
0dc1ea54 1008 foreach (views_discover_default_views($reset) as $view) {
31dd0540
TGGM
1009 // Determine if default view is enabled or disabled.
1010 if (isset($status[$view->name])) {
1011 $view->disabled = $status[$view->name];
1012 }
1013
1014 // If overridden, also say so.
1015 if (!empty($views[$view->name])) {
1016 $views[$view->name]->type = t('Overridden');
1017 }
1018 else {
1019 $view->type = t('Default');
1020 $views[$view->name] = $view;
1021 }
1022 }
1023
1024 }
1025 return $views;
1026}
1027
1028/**
1029 * Get a view from the database or from default views.
1030 *
1031 * This function is just a static wrapper around views::load(). This function
1032 * isn't called 'views_load()' primarily because it might get a view
1033 * from the default views which aren't technically loaded from the database.
1034 *
1035 * @param $name
1036 * The name of the view.
1037 * @param $reset
1038 * If TRUE, reset this entry in the load cache.
1039 * @return $view
1040 * A reference to the $view object. Use $reset if you're sure you want
1041 * a fresh one.
1042 */
1043function views_get_view($name, $reset = FALSE) {
1044 views_include('view');
1045 $view = view::load($name, $reset);
0dc1ea54 1046 $default_view = views_get_default_view($name, $reset);
31dd0540 1047
318fda5f 1048 // The view does not exist.
31dd0540
TGGM
1049 if (empty($view) && empty($default_view)) {
1050 return;
1051 }
318fda5f 1052 // The view is defined in code.
31dd0540 1053 elseif (empty($view) && !empty($default_view)) {
aaef8fac
EM
1054 $status = variable_get('views_defaults', array());
1055 if (isset($status[$default_view->name])) {
1056 $default_view->disabled = $status[$default_view->name];
1057 }
31dd0540
TGGM
1058 $default_view->type = t('Default');
1059 return $default_view->clone_view();
1060 }
318fda5f 1061 // The view is overriden/defined in the database.
31dd0540
TGGM
1062 elseif (!empty($view) && !empty($default_view)) {
1063 $view->type = t('Overridden');
1064 }
1065
1066 return $view->clone_view();
1067}
1068
31dd0540
TGGM
1069// ------------------------------------------------------------------
1070// Views debug helper functions
1071
1072/**
31dd0540
TGGM
1073 * Debug messages
1074 */
1075function vsm($message) {
1076 if (module_exists('devel')) {
1077 dsm($message);
1078 }
1079}
1080
1081function views_trace() {
1082 $message = '';
1083 foreach (debug_backtrace() as $item) {
1084 if (!empty($item['file']) && !in_array($item['function'], array('vsm_trace', 'vpr_trace', 'views_trace'))) {
1085 $message .= basename($item['file']) . ": " . (empty($item['class']) ? '' : ($item['class'] . '->')) . "$item[function] line $item[line]" . "\n";
1086 }
1087 }
1088 return $message;
1089}
1090
1091function vsm_trace() {
1092 vsm(views_trace());
1093}
1094
1095function vpr_trace() {
1096 dpr(views_trace());
1097}
1098
1099// ------------------------------------------------------------------
1100// Exposed widgets form
1101
1102/**
1103 * Form builder for the exposed widgets form.
1104 *
1105 * Be sure that $view and $display are references.
1106 */
2dd76fe0 1107function views_exposed_form($form, &$form_state) {
31dd0540 1108 // Don't show the form when batch operations are in progress.
4f09d3fd 1109 if ($batch = batch_get() && isset($batch['current_set'])) {
31dd0540
TGGM
1110 return array(
1111 // Set the theme callback to be nothing to avoid errors in template_preprocess_views_exposed_form().
1112 '#theme' => '',
1113 );
1114 }
1115
1116 // Make sure that we validate because this form might be submitted
1117 // multiple times per page.
1118 $form_state['must_validate'] = TRUE;
1119 $view = &$form_state['view'];
1120 $display = &$form_state['display'];
1121
1122 $form_state['input'] = $view->get_exposed_input();
1123
1124 // Let form plugins know this is for exposed widgets.
1125 $form_state['exposed'] = TRUE;
1126 // Check if the form was already created
1127 if ($cache = views_exposed_form_cache($view->name, $view->current_display)) {
1128 return $cache;
1129 }
1130
1131 $form['#info'] = array();
1132
1133 if (!variable_get('clean_url', FALSE)) {
1134 $form['q'] = array(
1135 '#type' => 'hidden',
1136 '#value' => $view->get_url(),
1137 );
1138 }
1139
2dd76fe0
EM
1140 // Go through each handler and let it generate its exposed widget.
1141 foreach ($view->display_handler->handlers as $type => $value) {
1142 foreach ($view->$type as $id => $handler) {
1143 if ($handler->can_expose() && $handler->is_exposed()) {
1144 $handler->exposed_form($form, $form_state);
1145 if ($info = $handler->exposed_info()) {
1146 $form['#info']["$type-$id"] = $info;
1147 }
1148 }
31dd0540
TGGM
1149 }
1150 }
1151
31dd0540
TGGM
1152 $form['submit'] = array(
1153 '#name' => '', // prevent from showing up in $_GET.
1154 '#type' => 'submit',
9d4cb3d4 1155 '#value' => t('Apply'),
2dd76fe0 1156 '#id' => drupal_html_id('edit-submit-' . $view->name),
31dd0540
TGGM
1157 );
1158
1159 $form['#action'] = url($view->get_url());
1160 $form['#theme'] = views_theme_functions('views_exposed_form', $view, $display);
5be494e5 1161 $form['#id'] = drupal_clean_css_identifier('views_exposed_form-' . check_plain($view->name) . '-' . check_plain($display->id));
31dd0540
TGGM
1162// $form['#attributes']['class'] = array('views-exposed-form');
1163
1164 // If using AJAX, we need the form plugin.
1165 if ($view->use_ajax) {
1c7d6fba 1166 drupal_add_js('misc/jquery.form.js', array('group' => JS_LIBRARY));
31dd0540 1167 }
3981ce08 1168 ctools_include('dependent');
31dd0540 1169
652ea18e
DW
1170 $exposed_form_plugin = $form_state['exposed_form_plugin'];
1171 $exposed_form_plugin->exposed_form_alter($form, $form_state);
1172
31dd0540
TGGM
1173 // Save the form
1174 views_exposed_form_cache($view->name, $view->current_display, $form);
1175
1176 return $form;
1177}
1178
1179/**
1180 * Validate handler for exposed filters
1181 */
1182function views_exposed_form_validate(&$form, &$form_state) {
1183 foreach (array('field', 'filter') as $type) {
1184 $handlers = &$form_state['view']->$type;
1185 foreach ($handlers as $key => $handler) {
1186 $handlers[$key]->exposed_validate($form, $form_state);
1187 }
1188 }
652ea18e
DW
1189 $exposed_form_plugin = $form_state['exposed_form_plugin'];
1190 $exposed_form_plugin->exposed_form_validate($form, $form_state);
31dd0540
TGGM
1191}
1192
1193/**
1194 * Submit handler for exposed filters
1195 */
1196function views_exposed_form_submit(&$form, &$form_state) {
1197 foreach (array('field', 'filter') as $type) {
1198 $handlers = &$form_state['view']->$type;
1199 foreach ($handlers as $key => $info) {
1200 $handlers[$key]->exposed_submit($form, $form_state);
1201 }
1202 }
1203 $form_state['view']->exposed_data = $form_state['values'];
1204 $form_state['view']->exposed_raw_input = array();
1205
652ea18e 1206
7df3edc3 1207 $exclude = array('q', 'submit', 'form_build_id', 'form_id', 'form_token', 'exposed_form_plugin', '');
652ea18e
DW
1208 $exposed_form_plugin = $form_state['exposed_form_plugin'];
1209 $exposed_form_plugin->exposed_form_submit($form, $form_state, $exclude);
1210
31dd0540 1211 foreach ($form_state['values'] as $key => $value) {
652ea18e 1212 if (!in_array($key, $exclude)) {
31dd0540
TGGM
1213 $form_state['view']->exposed_raw_input[$key] = $value;
1214 }
1215 }
1216}
1217
1218/**
1219 * Save the Views exposed form for later use.
1220 *
1221 * @param $views_name
1222 * String. The views name.
1223 * @param $display_name
1224 * String. The current view display name.
1225 * @param $form_output
1226 * Array (optional). The form structure. Only needed when inserting the value.
1227 * @return
1228 * Array. The form structure, if any. Otherwise, return FALSE.
1229 */
1230function views_exposed_form_cache($views_name, $display_name, $form_output = NULL) {
1231 static $views_exposed;
1232
1233 // Save the form output
1234 if (!empty($form_output)) {
1235 $views_exposed[$views_name][$display_name] = $form_output;
1236 return;
1237 }
1238
1239 // Return the form output, if any
1240 return empty($views_exposed[$views_name][$display_name]) ? FALSE : $views_exposed[$views_name][$display_name];
1241}
1242
1243// ------------------------------------------------------------------
1244// Misc helpers
1245
1246/**
1247 * Build a list of theme function names for use most everywhere.
1248 */
1249function views_theme_functions($hook, $view, $display = NULL) {
2dd76fe0 1250 require_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'views') . "/theme/theme.inc";
31dd0540
TGGM
1251 return _views_theme_functions($hook, $view, $display);
1252}
1253
1254/**
31dd0540
TGGM
1255 * Substitute current time; this works with cached queries.
1256 */
1257function views_views_query_substitutions($view) {
1258 global $language;
1259 return array(
dfb4b281
DW
1260 '***CURRENT_VERSION***' => VERSION,
1261 '***CURRENT_TIME***' => REQUEST_TIME,
1262 '***CURRENT_LANGUAGE***' => $language->language,
1263 '***DEFAULT_LANGUAGE***' => language_default('language'),
1264 '***NO_LANGUAGE***' => '',
31dd0540
TGGM
1265 );
1266}
1267
1268/**
dfb4b281
DW
1269 * Implements hook_query_TAG_alter().
1270 *
1271 * This is the hook_query_alter() for queries tagged by Views and is used to
1272 * add in substitutions from hook_views_query_substitutions().
1273 */
1274function views_query_views_alter(QueryAlterableInterface $query) {
1275 $substitutions = $query->getMetaData('views_substitutions');
1276 $tables =& $query->getTables();
1277 $where =& $query->conditions();
1278
1279 // Replaces substitions in tables.
1280 foreach ($tables as $table_name => $table_metadata) {
1281 foreach ($table_metadata['arguments'] as $replacement_key => $value) {
3eb76e0c 1282 if (isset($substitutions[$value])) {
dfb4b281
DW
1283 $tables[$table_name]['arguments'][$replacement_key] = $substitutions[$value];
1284 }
1285 }
1286 }
1287
1288 // Replaces substitions in filter criterias.
35175e08 1289 _views_query_tag_alter_condition($query, $where, $substitutions);
dfb4b281
DW
1290}
1291
1292/**
1293 * Replaces the substitutions recursive foreach condition.
1294 */
35175e08 1295function _views_query_tag_alter_condition(QueryAlterableInterface $query, &$conditions, $substitutions) {
dfb4b281
DW
1296 foreach ($conditions as $condition_id => &$condition) {
1297 if (is_numeric($condition_id)) {
1298 if (is_string($condition['field'])) {
1299 $condition['field'] = str_replace(array_keys($substitutions), array_values($substitutions), $condition['field']);
1300 }
35175e08 1301 elseif (is_object($condition['field'])) {
dfb4b281 1302 $sub_conditions =& $condition['field']->conditions();
35175e08
DW
1303 _views_query_tag_alter_condition($query, $sub_conditions, $substitutions);
1304 }
1305 // $condition['value'] is a subquery so alter the subquery recursive.
1306 // Therefore take sure to get the metadata of the main query.
1307 if (is_object($condition['value'])) {
1308 $subquery = $condition['value'];
1309 $subquery->addMetaData('views_substitutions', $query->getMetaData('views_substitutions'));
1310 views_query_views_alter($condition['value']);
1311 }
1312 elseif (isset($condition['value'])) {
1313 $condition['value'] = str_replace(array_keys($substitutions), array_values($substitutions), $condition['value']);
dfb4b281
DW
1314 }
1315 }
1316 }
1317}
1318
1319/**
31dd0540
TGGM
1320 * Embed a view using a PHP snippet.
1321 *
1322 * This function is meant to be called from PHP snippets, should one wish to
1323 * embed a view in a node or something. It's meant to provide the simplest
1324 * solution and doesn't really offer a lot of options, but breaking the function
1325 * apart is pretty easy, and this provides a worthwhile guide to doing so.
1326 *
1327 * Note that this function does NOT display the title of the view. If you want
1328 * to do that, you will need to do what this function does manually, by
1329 * loading the view, getting the preview and then getting $view->get_title().
1330 *
1331 * @param $name
1332 * The name of the view to embed.
1333 * @param $display_id
1334 * The display id to embed. If unsure, use 'default', as it will always be
1335 * valid. But things like 'page' or 'block' should work here.
1336 * @param ...
1337 * Any additional parameters will be passed as arguments.
1338 */
1339function views_embed_view($name, $display_id = 'default') {
1340 $args = func_get_args();
1341 array_shift($args); // remove $name
1342 if (count($args)) {
1343 array_shift($args); // remove $display_id
1344 }
1345
1346 $view = views_get_view($name);
1347 if (!$view || !$view->access($display_id)) {
1348 return;
1349 }
1350
1351 return $view->preview($display_id, $args);
1352}
1353
1354/**
5ee9b0c7
EM
1355* Get the result of a view.
1356*
1357* @param string $name
1358* The name of the view to retrieve the data from.
1359* @param string $display_id
1360* The display id. On the edit page for the view in question, you'll find
1361* a list of displays at the left side of the control area. "Defaults"
1362* will be at the top of that list. Hover your cursor over the name of the
1363* display you want to use. An URL will appear in the status bar of your
1364* browser. This is usually at the bottom of the window, in the chrome.
1365* Everything after #views-tab- is the display ID, e.g. page_1.
1366 * @param ...
1367 * Any additional parameters will be passed as arguments.
1368* @return
1369* array
1370* An array containing an object for each view item.
1371*/
1372function views_get_view_result($name, $display_id = NULL) {
1373 $args = func_get_args();
1374 array_shift($args); // remove $name
1375 if (count($args)) {
1376 array_shift($args); // remove $display_id
1377 }
1378
1379 $view = views_get_view($name);
1380 if (is_object($view)) {
1381 if (is_array($args)) {
1382 $view->set_arguments($args);
1383 }
1384 if (is_string($display_id)) {
1385 $view->set_display($display_id);
1386 }
1387 else {
1388 $view->init_display();
1389 }
1390 $view->pre_execute();
1391 $view->execute();
1392 return $view->result;
1393 }
1394 else {
1395 return array();
1396 }
1397}
1398
1399/**
31dd0540
TGGM
1400 * Export a field.
1401 */
1402function views_var_export($var, $prefix = '', $init = TRUE) {
1403 if (is_array($var)) {
1404 if (empty($var)) {
1405 $output = 'array()';
1406 }
1407 else {
1408 $output = "array(\n";
1409 foreach ($var as $key => $value) {
638dc155 1410 $output .= " " . views_var_export($key, '', FALSE) . " => " . views_var_export($value, ' ', FALSE) . ",\n";
31dd0540
TGGM
1411 }
1412 $output .= ')';
1413 }
1414 }
2dd76fe0 1415 elseif (is_bool($var)) {
31dd0540
TGGM
1416 $output = $var ? 'TRUE' : 'FALSE';
1417 }
2dd76fe0 1418 elseif (is_string($var) && strpos($var, "\n") !== FALSE) {
31dd0540
TGGM
1419 // Replace line breaks in strings with a token for replacement
1420 // at the very end. This protects multi-line strings from
1421 // unintentional indentation.
1422 $var = str_replace("\n", "***BREAK***", $var);
1423 $output = var_export($var, TRUE);
1424 }
1425 else {
1426 $output = var_export($var, TRUE);
1427 }
1428
1429 if ($prefix) {
1430 $output = str_replace("\n", "\n$prefix", $output);
1431 }
1432
1433 if ($init) {
1434 $output = str_replace("***BREAK***", "\n", $output);
1435 }
1436
1437 return $output;
1438}
1439
1440/**
2dd76fe0 1441 * Implement hook_views_exportables().
31dd0540
TGGM
1442 */
1443function views_views_exportables($op = 'list', $views = NULL, $name = 'foo') {
1444 $all_views = views_get_all_views();
1445 if ($op == 'list') {
1446
1447 foreach ($all_views as $name => $view) {
1448 // in list, $views is a list of tags.
1449 if (empty($views) || in_array($view->tag, $views)) {
1450 $return[$name] = array(
1451 'name' => check_plain($name),
1452 'desc' => check_plain($view->description),
1453 'tag' => check_plain($view->tag)
1454 );
1455 }
1456 }
1457 return $return;
1458 }
1459
1460 if ($op == 'export') {
1461 $code = "/**\n";
2dd76fe0 1462 $code .= " * Implement hook_views_default_views().\n";
31dd0540
TGGM
1463 $code .= " */\n";
1464 $code .= "function " . $name . "_views_default_views() {\n";
1465 foreach ($views as $view => $truth) {
1466 $code .= " /*\n";
2dd76fe0 1467 $code .= " * View " . var_export($all_views[$view]->name, TRUE) . "\n";
31dd0540
TGGM
1468 $code .= " */\n";
1469 $code .= $all_views[$view]->export(' ');
1470 $code .= ' $views[$view->name] = $view;' . "\n\n";
1471 }
1472 $code .= " return \$views;\n";
1473 $code .= "}\n";
1474
1475 return $code;
1476 }
1477}
1478
1479/**
28244380
DW
1480 * #process callback to see if we need to check_plain() the options.
1481 *
1482 * Since FAPI is inconsistent, the #options are sanitized for you in all cases
1483 * _except_ checkboxes. We have form elements that are sometimes 'select' and
1484 * sometimes 'checkboxes', so we need decide late in the form rendering cycle
1485 * if the options need to be sanitized before they're rendered. This callback
1486 * inspects the type, and if it's still 'checkboxes', does the sanitation.
1487 */
0f2586f9 1488function views_process_check_options($element, &$form_state) {
28244380
DW
1489 if ($element['#type'] == 'checkboxes' || $element['#type'] == 'checkbox') {
1490 $element['#options'] = array_map('check_plain', $element['#options']);
1491 }
1492 return $element;
1493}
2f5e3034
EM
1494
1495/**
1496 * Trim the field down to the specified length.
1497 *
1498 * @param $alter
1499 * - max_length: Maximum lenght of the string, the rest gets truncated.
1500 * - word_boundary: Trim only on a word boundary.
1501 * - ellipsis: Trim only on a word boundary.
1502 * - html: Take sure that the html is correct.
1503 *
1504 * @param $value
1505 * The string which should be trimmed.
1506 */
3cf55a10 1507function views_trim_text($alter, $value) {
2f5e3034
EM
1508 if (drupal_strlen($value) > $alter['max_length']) {
1509 $value = drupal_substr($value, 0, $alter['max_length']);
1f3724e1 1510 // TODO: replace this with cleanstring of ctools
2f5e3034 1511 if (!empty($alter['word_boundary'])) {
1f3724e1
EM
1512 $regex = "(.*)\b.+";
1513 if (function_exists('mb_ereg')) {
1514 mb_regex_encoding('UTF-8');
1515 $found = mb_ereg($regex, $value, $matches);
1516 }
1517 else {
1518 $found = preg_match("/$regex/us", $value, $matches);
1519 }
1520 if ($found) {
2f5e3034
EM
1521 $value = $matches[1];
1522 }
1523 }
1524 // Remove scraps of HTML entities from the end of a strings
1525 $value = rtrim(preg_replace('/(?:<(?!.+>)|&(?!.+;)).*$/us', '', $value));
1526
1527 if (!empty($alter['ellipsis'])) {
1528 $value .= '...';
1529 }
1530 }
1531 if (!empty($alter['html'])) {
1532 $value = _filter_htmlcorrector($value);
1533 }
1534
1535 return $value;
1536}