? views_caching.patch Index: views.install =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/views/views.install,v retrieving revision 1.46 diff -u -p -r1.46 views.install --- views.install 7 Apr 2009 20:39:51 -0000 1.46 +++ views.install 21 May 2009 18:37:54 -0000 @@ -126,6 +126,11 @@ function views_schema_1() { ); $schema['cache_views'] = drupal_get_schema_unprocessed('system', 'cache'); + + $schema['cache_views_data'] = drupal_get_schema_unprocessed('system', 'cache'); + $schema['cache_views_data']['description'] = 'Cache table for views to store pre-rendered queries, results, and display output.'; + $schema['cache_views_data']['fields']['serialized']['default'] = 1; + $schema['views_object_cache'] = array( 'description' => 'A special cache used to store objects that are being edited; it serves to save state in an ordinarily stateless environment.', @@ -239,3 +244,18 @@ function views_update_6004() { return $ret; } + +/** + * Add the cache_views_data table to support standard caching. + */ +function views_update_6005() { + $ret = array(); + + $table = drupal_get_schema_unprocessed('system', 'cache'); + $table['description'] = 'Cache table for views to store pre-rendered queries, results, and display output.'; + $table['fields']['serialized']['default'] = 1; + + db_create_table($ret, 'cache_views_data', $table); + + return $ret; +} Index: includes/plugins.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/views/includes/plugins.inc,v retrieving revision 1.152.2.1 diff -u -p -r1.152.2.1 plugins.inc --- includes/plugins.inc 20 May 2009 03:03:38 -0000 1.152.2.1 +++ includes/plugins.inc 21 May 2009 18:37:54 -0000 @@ -247,6 +247,26 @@ function views_views_plugins() { 'handler' => 'views_plugin_query_default' ), ), + 'cache' => array( + 'parent' => array( + 'no ui' => TRUE, + 'handler' => 'views_plugin_cache', + 'parent' => '', + ), + 'none' => array( + 'title' => t('None'), + 'help' => t('No caching of Views data.'), + 'handler' => 'views_plugin_cache_none', + 'help topic' => 'cache-none', + ), + 'time' => array( + 'title' => t('Time-based'), + 'help' => t('Simple time-based caching of data.'), + 'handler' => 'views_plugin_cache_time', + 'uses options' => TRUE, + 'help topic' => 'cache-time', + ), + ), ); } @@ -256,7 +276,7 @@ function views_views_plugins() { * @return Nested array of plugins, grouped by type. */ function views_discover_plugins() { - $cache = array('display' => array(), 'style' => array(), 'row' => array(), 'argument default' => array(), 'argument validator' => array(), 'access' => array()); + $cache = array('display' => array(), 'style' => array(), 'row' => array(), 'argument default' => array(), 'argument validator' => array(), 'access' => array(), 'cache' => array()); // Get plugins from all mdoules. foreach (module_implements('views_plugins') as $module) { $function = $module . '_views_plugins'; Index: includes/view.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/views/includes/view.inc,v retrieving revision 1.151.2.3 diff -u -p -r1.151.2.3 view.inc --- includes/view.inc 20 May 2009 03:09:36 -0000 1.151.2.3 +++ includes/view.inc 21 May 2009 18:37:54 -0000 @@ -693,9 +693,6 @@ class view extends views_db_object { * Render this view for display. */ function render($display_id = NULL) { - // Check for cached output. - // @todo: Implement this - $this->execute($display_id); // Check to see if the build failed. @@ -707,34 +704,45 @@ class view extends views_db_object { if (!empty($this->live_preview) && variable_get('views_show_additional_queries', FALSE)) { $this->start_query_capture(); } - - // Initialize the style plugin. - $this->init_style(); - - $this->style_plugin->pre_render($this->result); - - // Let modules modify the view just prior to executing it. - foreach (module_implements('views_pre_render') as $module) { - $function = $module . '_views_pre_render'; - $function($this); + + // Check for already-cached output. + $cache = $this->display_handler->get_cache_plugin(); + if ($cache && $cache->cache_get('output')) { + vpr('Used cached output'); } - - // Give field handlers the opportunity to perform additional queries - // using the entire resultset prior to rendering. - if ($this->style_plugin->uses_fields()) { - foreach ($this->field as $id => $handler) { - if (!empty($this->field[$id])) { - $this->field[$id]->pre_render($this->result); + else { + // Initialize the style plugin. + $this->init_style(); + + $this->style_plugin->pre_render($this->result); + + // Let modules modify the view just prior to executing it. + foreach (module_implements('views_pre_render') as $module) { + $function = $module . '_views_pre_render'; + $function($this); + } + + // Give field handlers the opportunity to perform additional queries + // using the entire resultset prior to rendering. + if ($this->style_plugin->uses_fields()) { + foreach ($this->field as $id => $handler) { + if (!empty($this->field[$id])) { + $this->field[$id]->pre_render($this->result); + } } } + $this->display_handler->output = $this->display_handler->render(); + if ($cache) { + $cache->cache_set('output'); + } } - $output = $this->display_handler->render(); if (!empty($this->live_preview) && variable_get('views_show_additional_queries', FALSE)) { $this->end_query_capture(); } $this->render_time = views_microtime() - $start; - return $output; + + return $this->display_handler->output; } /** Index: plugins/views_plugin_cache.inc =================================================================== RCS file: plugins/views_plugin_cache.inc diff -N plugins/views_plugin_cache.inc --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ plugins/views_plugin_cache.inc 21 May 2009 18:37:54 -0000 @@ -0,0 +1,70 @@ +view = &$view; + $this->display = &$display; + $this->options = array(); + + if (is_object($display->handler)) { + // Note: The below is read only. + $this->options = $display->handler->get_option('cache'); + } + } + + /** + * Retrieve the default options when this is a new access + * control plugin + */ + function option_defaults(&$options) { } + + /** + * Provide the default form for setting options. + */ + function options_form(&$form, &$form_state) { } + + /** + * Provide the default form form for validating options + */ + function options_validate(&$form, &$form_state) { } + + /** + * Provide the default form form for submitting options + */ + function options_submit(&$form, &$form_state) { } + + /** + * Return a string to display as the clickable title for the + * access control. + */ + function summary_title() { + return t('Unknown'); + } + + /** + * Save data to the cache. + */ + function cache_set($type, $data = NULL) { } + + + /** + * Retrieve data from the cache. + */ + function cache_get($type) { + return FALSE; + } +} Index: plugins/views_plugin_cache_none.inc =================================================================== RCS file: plugins/views_plugin_cache_none.inc diff -N plugins/views_plugin_cache_none.inc --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ plugins/views_plugin_cache_none.inc 21 May 2009 18:37:54 -0000 @@ -0,0 +1,11 @@ +view = &$view; + $this->display = &$display; + $this->options = array(); + + if (is_object($display->handler)) { + // Note: The below is read only. + $this->options = $display->handler->get_option('cache'); + } + } + + /** + * Retrieve the default options when this is a new access + * control plugin + */ + function option_defaults(&$options) { + $options['lifespan'] = 3600; + } + + /** + * Provide the default form for setting options. + */ + function options_form(&$form, &$form_state) { + $options = array(60, 300, 1800, 3600, 21600, 518400); + $form['lifespan'] = array( + '#type' => 'select', + '#title' => t('Cache lifespan'), + '#description' => t('The length of time cached data should be kept.'), + '#options' => drupal_map_assoc($options, 'format_interval'), + '#default_value' => $this->options['expiration'], + ); + } + + /** + * Provide the default form form for validating options + */ + function options_validate(&$form, &$form_state) { } + + /** + * Provide the default form form for submitting options + */ + function options_submit(&$form, &$form_state) { } + + /** + * Return a string to display as the clickable title for the + * access control. + */ + function summary_title() { + return format_interval($this->options['lifespan'], 1); + } + + /** + * Save data to the cache. + */ + function cache_set($type) { + switch ($type) { + case 'query': + break; + case 'results': + break; + case 'output': + cache_set($this->_output_key(), $this->view->display_handler->output, 'cache_views_data'); + break; + } + } + + /** + * Retrieve data from the cache. + */ + function cache_get($type) { + $cutoff = time() - $this->options['lifespan']; + switch ($type) { + case 'query': + return FALSE; + case 'results': + return FALSE; + case 'output': + if ($cache = cache_get($this->_output_key(), 'cache_views_data')) { + if ($cache->created > $cutoff) { + $this->view->display_handler->output = $cache->data; + return TRUE; + } + } + return FALSE; + } + } + + function _query_key() { + $key_data = array(); + return $this->view->name .':'. $this->display->id .':query:'. md5(serialize($key_data)); + } + + function _results_key() { + $key_data = array(); + return $this->view->name .':'. $this->display->id .':results:'. md5(serialize($key_data)); + } + + function _output_key() { + $key_data = array( + 'build_info' => $this->view->build_info, + 'pager' => $this->view->pager, + ); + return $this->view->name .':'. $this->display->id .':output:'. md5(serialize($key_data)); + } +} Index: plugins/views_plugin_display.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/views/plugins/views_plugin_display.inc,v retrieving revision 1.20.2.2 diff -u -p -r1.20.2.2 views_plugin_display.inc --- plugins/views_plugin_display.inc 20 May 2009 03:09:37 -0000 1.20.2.2 +++ plugins/views_plugin_display.inc 21 May 2009 18:37:55 -0000 @@ -148,6 +148,7 @@ class views_plugin_display extends views function defaultable_sections($section = NULL) { $sections = array( 'access' => array('access'), + 'cache' => array('cache'), 'title' => array('title'), 'header' => array('header', 'header_format', 'header_empty'), 'footer' => array('footer', 'footer_format', 'footer_empty'), @@ -212,6 +213,7 @@ class views_plugin_display extends views 'defaults' => array( 'default' => array( 'access' => TRUE, + 'cache' => TRUE, 'title' => TRUE, 'header' => TRUE, 'header_format' => TRUE, @@ -270,6 +272,11 @@ class views_plugin_display extends views 'type' => array('default' => 'none'), ), ), + 'cache' => array( + 'contains' => array( + 'type' => array('default' => 'none'), + ), + ), 'title' => array( 'default' => '', 'translatable' => TRUE, @@ -482,6 +489,22 @@ class views_plugin_display extends views } /** + * Get the cache plugin + */ + function get_cache_plugin($name = NULL) { + if (!$name) { + $cache = $this->get_option('cache'); + $name = $cache['type']; + } + + $plugin = views_get_plugin('cache', $name); + if ($plugin) { + $plugin->init($this->view, $this->display); + return $plugin; + } + } + + /** * Get the handler object for a single handler. */ function &get_handler($type, $id) { @@ -687,6 +710,25 @@ class views_plugin_display extends views $options['access']['links']['access_options'] = t('Change settings for this access type.'); } + $cache_plugin = $this->get_cache_plugin(); + if (!$cache_plugin) { + // default to the no cache control plugin. + $cache_plugin = views_get_plugin('cache', 'none'); + } + + $cache_str = $cache_plugin->summary_title(); + + $options['cache'] = array( + 'category' => 'basic', + 'title' => t('Caching'), + 'value' => $cache_str, + 'desc' => t('Specify caching type for this display.'), + ); + + if (!empty($cache_plugin->definition['uses options'])) { + $options['cache']['links']['cache_options'] = t('Change settings for this caching type.'); + } + if ($this->uses_link_display()) { // Only show the 'link display' if there is more than one option. $count = 0; @@ -893,6 +935,47 @@ class views_plugin_display extends views $plugin->options_form($form['access_options'], $form_state); } break; + case 'cache': + $form['#title'] .= t('Caching'); + $form['cache'] = array( + '#prefix' => '