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