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