diff --git a/includes/cache.inc b/includes/cache.inc index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..58b9f3a0f807a451cdd77cf1d365a6c82f860008 100644 --- a/includes/cache.inc +++ b/includes/cache.inc @@ -0,0 +1,170 @@ +<?php +// $Id$ +/** + * @file cache.inc + * + * Functions to load Views' data so that it knows what is available to + * build queries from. + */ + +/** + * Load views files on behalf of modules. + */ +function _views_include_handlers() { + $views_path = drupal_get_path('module', 'views') . '/modules'; + foreach (module_list() as $module) { + $module_path = drupal_get_path('module', $module); + if (file_exists("$module_path/$module.views.inc")) { + include_once "$module_path/$module.views.inc"; + } + else if (file_exists("$views_path/$module.views.inc")) { + include_once "$views_path/$module.views.inc"; + } + } +} + +/** + * Load default views files on behalf of modules. + */ +function _views_include_default_views() { + // Now include the views_default files specifically. + $views_path = drupal_get_path('module', 'views') . '/modules'; + foreach (module_list() as $module) { + $module_path = drupal_get_path('module', $module); + if (file_exists("$module_path/$module.views_default.inc")) { + include_once "$module_path/$module.views_default.inc"; + } + else if (file_exists("$views_path/$module.views_default.inc")) { + include_once "$views_path/$module.views_default.inc"; + } + } +} + +/** + * Fetch Views' data from the cache + */ +function _views_fetch_data($table = NULL) { + static $cache = NULL; + if (!isset($cache)) { + $start = microtime(); + // NOTE: This happens whether we retrieve them from cache or otherwise. + views_include_handlers(); + + // @todo: We can probably cache the views_data longer than just the current page run. + $cache = module_invoke_all('views_data'); + if (module_exists('devel')) { + dsm('Views data build time: ' . (microtime() - $start) * 1000 . ' ms'); + } + } + + if (!$table) { + return $cache; + } + if (isset($cache[$table])) { + return $cache[$table]; + } + // Return an empty array if there is no match. + return array(); +} + +/** + * Fetch the plugin data from cache. + */ +function _views_fetch_plugin_data($type = NULL, $plugin = NULL) { + static $cache = NULL; + if (!isset($cache)) { + $start = microtime(); + views_include('plugins'); + views_include_handlers(); + + $cache = views_discover_plugins(); + + if (module_exists('devel')) { + dsm('Views plugins build time: ' . (microtime() - $start) * 1000 . ' ms'); + } + } + + if (!$type && !$plugin) { + return $cache; + } + else if (!$plugin) { + // Not in the if above so the else below won't run + if (isset($cache[$type])) { + return $cache[$type]; + } + } + else if (isset($cache[$type][$plugin])) { + return $cache[$type][$plugin]; + } + + // Return an empty array if there is no match. + return array(); +} + +/** + * Scan all modules for default views and rebuild the default views cache. + * + * @return An associative array of all known default views. + */ +function _views_discover_default_views() { + global $language; + views_include_default_views(); + $defaults = module_invoke_all('views_default_views'); + + foreach ($defaults as $name => $view) { + if ($should_cache) { + views_cache_set('views_default_views:'. $name, $view, TRUE); + } + $cache[$name] = $view; + } + + return $cache; +} + +/** + * Set a cached item in the views cache. + * + * This is just a convenience wrapper around cache_set(). + * + * @param $cid + * The cache ID of the data to store. + * @param $data + * The data to store in the cache. Complex data types will be automatically serialized before insertion. + * Strings will be stored as plain text and not serialized. + * @param $use_language + * If TRUE, the data will be cached specific to the currently active language. + */ +function views_cache_set($cid, $data, $use_language = FALSE) { + global $language; + + if (variable_get('views_skip_cache', FALSE)) { + return; + } + if ($use_language) { + $cid .= ':'. $language->language; + } + cache_set($cid, $data, 'cache_views'); +} + +/** + * Return data from the persistent views cache. + * + * This is just a convenience wrapper around cache_get(). + * + * @param $cid + * The cache ID of the data to retrieve. + * @param $use_language + * If TRUE, the data will be requested specific to the currently active language. + */ +function views_cache_get($cid, $use_language = FALSE) { + global $language; + + if (variable_get('views_skip_cache', FALSE)) { + return 0; + } + if ($use_language) { + $cid .= ':'. $language->language; + } + + return cache_get($cid, 'cache_views'); +} diff --git a/includes/handlers.inc b/includes/handlers.inc index 7a27eca055a2ed41706bbb62158d1217e958e7b7..b560c7f7c7de6bd184aef2d058d753fa56f20ae9 100644 --- a/includes/handlers.inc +++ b/includes/handlers.inc @@ -5,6 +5,61 @@ * Defines the various handler objects to help build and display views. */ +/** + * Instantiate and construct a new handler + * + * @todo: move to handlers.inc + */ +function _views_create_handler($definition) { +// dpr('Instantiating handler ' . $definition['handler']); + $handler = new $definition['handler']; + if (isset($definition['arguments'])) { + call_user_func_array(array($handler, 'construct'), $definition['arguments']); + } + + $handler->set_definition($definition); + return $handler; +} + +/** + * Prepare a handler's data by checking defaults and such. + * + * @todo: move to handlers.inc + */ +function _views_prepare_handler($definition, $data, $field) { + foreach (array('group', 'title', 'help') as $key) { + // First check the field level + if (!isset($definition[$key]) && !empty($data[$field][$key])) { + $definition[$key] = $data[$field][$key]; + } + // Then if that doesn't work, check the table level + if (!isset($definition['table'][$key]) && !empty($data['table'][$key])) { + $definition[$key] = $data['table'][$key]; + } + } + + return _views_create_handler($definition); +} + +/** + * Fetch a handler to join one table to a primary table from the data cache + * + * @todo: move to cache.inc (maybe) + */ +function views_get_table_join($table, $primary_table) { + $data = views_fetch_data($table); + if (isset($data['table']['join'][$primary_table])) { + $h = $data['table']['join'][$primary_table]; + $handler = new $h['handler']; + if (isset($h['arguments'])) { + call_user_func_array(array($handler, 'construct'), $h['arguments']); + } + return $handler; + } + // DEBUG -- identify missing handlers + dsm("missing join: $table $primary_table"); +} + /** * @defgroup views_join_handlers Views' join handlers * @{ diff --git a/includes/plugins.inc b/includes/plugins.inc index e030bd0e77a6e63b91ee95fc728a9cf032f6b950..46a045b7df122cfe88f448bfe79398b134c67d2e 100644 --- a/includes/plugins.inc +++ b/includes/plugins.inc @@ -76,6 +76,48 @@ function views_views_plugins() { } +/** + * Builds and return a list of all plugins available in the system. + * + * @return Nested array of plugins, grouped by type and + */ +function views_discover_plugins() { + $cache = array('display' => array(), 'style' => array(), 'row' => array()); + // Get plugins from all mdoules. + foreach (module_implements('views_plugins') as $module) { + $function = $module . '_views_plugins'; + $result = $function(); + if (!is_array($result)) { + continue; + } + + $module_dir = isset($result['module']) ? $result['module'] : $module; + // Setup automatic path/file finding for theme registration + if ($module_dir == 'views') { + $path = drupal_get_path('module', $module_dir) . '/theme'; + $file = 'theme.inc'; + } + else { + $path = drupal_get_path('module', $module_dir); + $file = "$module.views.inc"; + } + foreach ($result as $type => $info) { + if ($type == 'module') { + continue; + } + foreach ($info as $plugin => $def) { + if (isset($def['theme']) && !isset($def['path'])) { + $def['path'] = $path; + $def['file'] = $file; + } + // merge the new data in + $cache[$type][$plugin] = $def; + } + } + } + return $cache; +} + /** * @defgroup views_display_plugins Views' display plugins * @{ @@ -159,7 +201,7 @@ class views_display_plugin extends views_object { /** * Render the view's title for display - * @todo: Necessary? Hm. + * @todo Necessary? Hm. */ function render_title() { } diff --git a/views.module b/views.module index 9dbc8048e2b0f870e04696deebdba268b79673b6..49406f489695d83102c0ca3d12c3978a0534269d 100644 --- a/views.module +++ b/views.module @@ -3,11 +3,12 @@ /** * @file views.module - * Query and view site content + * + * Primarily Drupal hooks and global API functions to manipulate views. */ /** - * Register views theming functions. + * Implementation of hook_theme(). Register views theming functions. */ function views_theme() { $path = drupal_get_path('module', 'views'); @@ -69,7 +70,7 @@ function views_theme() { } /** - * Implementation of hook_menu + * Implementation of hook_menu(). * * This probably needs to actually be hook_menu_alter or something. */ @@ -151,8 +152,24 @@ function views_block($op = 'list', $delta = 0, $edit = array()) { } } +/** + * Implementation of hook_devel_caches(). + * + * When the devel cache is cleared, clear cached views, too. That + * makes development a bit easier. + */ +function views_devel_caches() { + return array('cache_views'); +} + /** * Determine if the given user has access to the view + display. + * + * @param $view + * May be a view object, or an array with the view name and the display ID, + * or a string to use as the view name. + * @param $account + * An optional account to use; if left off, the current user will be used. */ function views_access($view, $account = NULL) { if (is_array($view)) { @@ -196,11 +213,38 @@ function &views_get_page_view() { return views_set_page_view(); } +/** + * Set the current 'current view' that is being built/rendered so that it is + * easy for other modules or items in drupal_eval to identify + */ +function &views_set_current_view($view = NULL) { + static $cache = NULL; + if (isset($view)) { + $cache = $view; + } + + return $cache; +} + +/** + * Find out what, if any, current view is currently in use. Please note that + * this returns a reference, so be careful! You can unintentionally modify the + * $view object. + */ +function &views_get_current_view() { + return views_set_current_view(); +} + /** * Include views .inc files as necessary. */ function views_include($file) { - require_once drupal_get_path('module', 'views') . "/includes/$file.inc"; + static $used = array(); + if (!isset($used[$file])) { + require_once drupal_get_path('module', 'views') . "/includes/$file.inc"; + } + + $used[$file] = TRUE; } /** @@ -214,16 +258,8 @@ function views_include_handlers() { } views_include('handlers'); - $views_path = drupal_get_path('module', 'views') . '/modules'; - foreach (module_list() as $module) { - $module_path = drupal_get_path('module', $module); - if (file_exists("$module_path/$module.views.inc")) { - include_once "$module_path/$module.views.inc"; - } - else if (file_exists("$views_path/$module.views.inc")) { - include_once "$views_path/$module.views.inc"; - } - } + views_include('cache'); + _views_include_handlers(); $finished = TRUE; } @@ -242,96 +278,10 @@ function views_include_default_views() { // the module author. views_include_handlers(); - // Now include the views_default files specifically. - $views_path = drupal_get_path('module', 'views') . '/modules'; - foreach (module_list() as $module) { - $module_path = drupal_get_path('module', $module); - if (file_exists("$module_path/$module.views_default.inc")) { - include_once "$module_path/$module.views_default.inc"; - } - else if (file_exists("$views_path/$module.views_default.inc")) { - include_once "$views_path/$module.views_default.inc"; - } - } + _views_include_default_views(); $finished = TRUE; } -/** - * Fetch Views' data from the cache - */ -function views_fetch_data($table = NULL) { - static $cache = NULL; - if (!isset($cache)) { - $start = microtime(); - // NOTE: This happens whether we retrieve them from cache or otherwise. - views_include_handlers(); - - // @todo: We can probably cache the views_data longer than just the current page run. - $cache = module_invoke_all('views_data'); - if (module_exists('devel')) { - dsm('Views data build time: ' . (microtime() - $start) * 1000 . ' ms'); - } - } - - if (!$table) { - return $cache; - } - if (isset($cache[$table])) { - return $cache[$table]; - } - // Return an empty array if there is no match. - return array(); -} - -/** - * Fetch a handler to join one table to a primary table from the data cache - */ -function views_get_table_join($table, $primary_table) { - $data = views_fetch_data($table); - if (isset($data['table']['join'][$primary_table])) { - $h = $data['table']['join'][$primary_table]; - $handler = new $h['handler']; - if (isset($h['arguments'])) { - call_user_func_array(array($handler, 'construct'), $h['arguments']); - } - return $handler; - } - // DEBUG -- identify missing handlers - dsm("join: $table $primary_table"); -} - -/** - * Instantiate and construct a new handler - */ -function _views_create_handler($definition) { -// dpr('Instantiating handler ' . $definition['handler']); - $handler = new $definition['handler']; - if (isset($definition['arguments'])) { - call_user_func_array(array($handler, 'construct'), $definition['arguments']); - } - - $handler->set_definition($definition); - return $handler; -} - -/** - * Prepare a handler's data by checking defaults and such. - */ -function _views_prepare_handler($definition, $data, $field) { - foreach (array('group', 'title', 'help') as $key) { - // First check the field level - if (!isset($definition[$key]) && !empty($data[$field][$key])) { - $definition[$key] = $data[$field][$key]; - } - // Then if that doesn't work, check the table level - if (!isset($definition['table'][$key]) && !empty($data['table'][$key])) { - $definition[$key] = $data['table'][$key]; - } - } - - return _views_create_handler($definition); -} - /** * Fetch a handler from the data cache. */ @@ -345,79 +295,19 @@ function views_get_handler($table, $field, $key) { } /** - * Builds and return a list of all plugins available in the system. - * - * @return Nested array of plugins, grouped by type and + * Fetch Views' data from the cache */ -function views_discover_plugins() { - $cache = array('display' => array(), 'style' => array(), 'row' => array()); - // Get plugins from all mdoules. - foreach (module_implements('views_plugins') as $module) { - $function = $module . '_views_plugins'; - $result = $function(); - if (!is_array($result)) { - continue; - } - - $module_dir = isset($result['module']) ? $result['module'] : $module; - // Setup automatic path/file finding for theme registration - if ($module_dir == 'views') { - $path = drupal_get_path('module', $module_dir) . '/theme'; - $file = 'theme.inc'; - } - else { - $path = drupal_get_path('module', $module_dir); - $file = "$module.views.inc"; - } - foreach ($result as $type => $info) { - if ($type == 'module') { - continue; - } - foreach ($info as $plugin => $def) { - if (isset($def['theme']) && !isset($def['path'])) { - $def['path'] = $path; - $def['file'] = $file; - } - // merge the new data in - $cache[$type][$plugin] = $def; - } - } - } - return $cache; +function views_fetch_data($table = NULL) { + views_include('cache'); + return _views_fetch_data($table); } /** * Fetch the plugin data from cache. */ function views_fetch_plugin_data($type = NULL, $plugin = NULL) { - static $cache = NULL; - if (!isset($cache)) { - $start = microtime(); - views_include('plugins'); - views_include_handlers(); - - $cache = views_discover_plugins(); - - if (module_exists('devel')) { - dsm('Views plugins build time: ' . (microtime() - $start) * 1000 . ' ms'); - } - } - - if (!$type && !$plugin) { - return $cache; - } - else if (!$plugin) { - // Not in the if above so the else below won't run - if (isset($cache[$type])) { - return $cache[$type]; - } - } - else if (isset($cache[$type][$plugin])) { - return $cache[$type][$plugin]; - } - - // Return an empty array if there is no match. - return array(); + views_include('cache'); + return _views_fetch_plugin_data($type, $plugin); } /** @@ -437,27 +327,6 @@ function views_css_safe($string) { return str_replace('_', '-', $string); } -/** - * Basic definition for many views objects - */ -class views_object { - /** - * Views handlers use a special construct function so that we can more - * easily construct them with variable arguments. - */ - function construct() { } - - /** - * Let the handler know what its full definition is. - */ - function set_definition($definition) { - $this->definition = $definition; - if (isset($definition['field'])) { - $this->real_field = $definition['field']; - } - } -} - /** * Get a view from the database or from default views. * @@ -488,30 +357,11 @@ function &views_get_view($name, $reset = FALSE) { * @return A view object or NULL if it is not available. */ function &views_get_default_view($view_name) { - static $cache = array(); - global $language; + $cache = views_discover_default_views(); - // We need to ensure that the view class is defined before we de-serialize the - // cached view(s), or else it gets cast to a stdClass object. - views_include('view'); - - if (empty($cache[$view_name])) { - // Try to get the view (for this language) from the cache. - $view = views_cache_get('views_default_views:'. $view_name, TRUE); - - // If we found the view in the cache, just use that. If not, try to find it from - // module hooks. - if ($view) { - $cache[$view_name] = $view->data; - } - else { - // We may as well rebuild the cache while we're at it, since we need to pull all - // of the data anyway. We pick out the one we want along the way. - $cache = views_discover_default_views(); - } + if (isset($cache[$view_name])) { + return $cache[$view_name]; } - - return $cache[$view_name]; } /** @@ -520,85 +370,52 @@ function &views_get_default_view($view_name) { * @return An associative array of all known default views. */ function views_discover_default_views() { - global $language; static $cache = array(); if (empty($cache)) { - views_include_default_views(); - $defaults = module_invoke_all('views_default_views'); - foreach ($defaults as $name => $view) { - if ($should_cache) { - views_cache_set('views_default_views:'. $name, $view, TRUE); - } - $cache[$name] = $view; - } + views_include('cache'); + $cache = _views_discover_default_views(); } return $cache; } /** - * Set a cached item in the views cache. - * - * This is just a convenience wrapper around cache_set(). - * - * @param $cid - * The cache ID of the data to store. - * @param $data - * The data to store in the cache. Complex data types will be automatically serialized before insertion. - * Strings will be stored as plain text and not serialized. - * @param $use_language - * If TRUE, the data will be cached specific to the currently active language. - */ -function views_cache_set($cid, $data, $use_language = FALSE) { - global $language; - - if (variable_get('views_skip_cache', FALSE)) { - return; - } - if ($use_language) { - $cid .= ':'. $language->language; - } - cache_set($cid, $data, 'cache_views'); -} - -/** - * Return data from the persistent views cache. - * - * This is just a convenience wrapper around cache_get(). + * Get a list of all views and the display plugins that provide + * page support to the Drupal menu system. Since views can appear + * in this list multiple times, the return of this function is an + * array of arrays. * - * @param $cid - * The cache ID of the data to retrieve. - * @param $use_language - * If TRUE, the data will be requested specific to the currently active language. + * @return + * @code + * array( + * array($view, $display_id), + * array($view, $display_id), + * ); */ -function views_cache_get($cid, $use_language = FALSE) { - global $language; - - if (variable_get('views_skip_cache', FALSE)) { - return 0; - } - if ($use_language) { - $cid .= ':'. $language->language; - } - - return cache_get($cid, 'cache_views'); +function views_get_page_views() { + return views_get_applicable_views('uses_hook_menu'); } /** - * Implementation of hook_devel_caches(). + * Get a list of all views and the display plugins that provide + * themselves to the Drupal block system. Since views can appear + * in this list multiple times, the return of this function is an + * array of arrays. * - * When the devel cache is cleared, clear cached views, too. That - * makes development a bit easier. + * @return + * @code + * array( + * array($view, $display_id), + * array($view, $display_id), + * ); */ -function views_devel_caches() { - return array('cache_views'); +function views_get_block_views() { + return views_get_applicable_views('uses_hook_block'); } /** - * Get a list of all views and the display plugins that provide - * page support to the Drupal menu system. Since views can appear - * in this list multiple times, the return of this function is an - * array of arrays. + * Return a list of all views and display IDs that have a particular + * setting in their display's plugin settings. * * @return * @code @@ -607,26 +424,19 @@ function views_devel_caches() { * array($view, $display_id), * ); */ -function views_get_page_views() { - // First, get all applicable views that are defined in the database. - views_include('view'); - $views = view::load_views("d.url IS NOT NULL AND d.url <> ''", "INNER JOIN {views_display} d ON v.vid = d.vid"); +function views_get_applicable_views($type) { + $result = array(); + $views = views_get_all_views(); - // Now get all applicable views that are defined in a default_views hook. - foreach (views_discover_default_views() as $name => $view) { - foreach ($view->display as $display) { - if ($display->display_plugin == 'page') { - $views[$view->name] = &$view; - break; - } + foreach ($views as $view) { + // Skip disabled views. + if (!empty($view->disabled)) { + continue; } - } - $result = array(); - foreach ($views as $view) { foreach ($view->display as $display) { $plugin = views_fetch_plugin_data('display', $display->display_plugin); - if (!empty($plugin['uses_hook_menu'])) { + if (!empty($plugin[$type])) { // This view uses hook menu. Clone it so that different handlers // don't trip over each other, and add it to the list. $v = drupal_clone($view); @@ -639,26 +449,40 @@ function views_get_page_views() { } /** - * Get a list of all views and the display plugins that provide - * page support to the Drupal menu system. Since views can appear - * in this list multiple times, the return of this function is an - * array of arrays. - * - * See @see views_get_page_views + * Return an array of all views as fully loaded $view objects. */ -function views_get_block_views() { - // First, get all applicable views. - views_include('view'); - $views = view::load_views("d.block = 1", "INNER JOIN {views_display} d ON v.vid = d.vid"); +function views_get_all_views() { + static $views = array(); - $result = array(); - foreach ($views as $view) { - $view->init_handlers(); - foreach ($view->display as $display) { - if ($display->handler && $display->handler->uses_hook_menu) { - $result[] = array($view, $display->id); - } + if (empty($views)) { + // First, get all applicable views. + views_include('view'); + $views = view::load_views(); + + // Merge in default views; this construct will not overwrite any keys that + // are already set. + $views += views_discover_default_views(); + } + return $views; +} + +/** + * Basic definition for many views objects + */ +class views_object { + /** + * Views handlers use a special construct function so that we can more + * easily construct them with variable arguments. + */ + function construct() { } + + /** + * Let the handler know what its full definition is. + */ + function set_definition($definition) { + $this->definition = $definition; + if (isset($definition['field'])) { + $this->real_field = $definition['field']; } } - return $result; }