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