Index: views.module =================================================================== RCS file: /cvs/drupal/contributions/modules/views/views.module,v retrieving revision 1.235 diff -u -p -r1.235 views.module --- views.module 24 Mar 2008 20:26:23 -0000 1.235 +++ views.module 25 Mar 2008 19:12:31 -0000 @@ -107,6 +107,14 @@ function views_menu() { list($view, $display_id) = $data; $items += $view->execute_hook_menu($display_id); } + $items['views/ajax'] = array( + 'title' => t('Views'), + 'page callback' => 'views_ajax', + 'access callback' => 'user_access', + 'access arguments' => array('access content'), + 'description' => t('Ajax callback for view loading.'), + 'type' => MENU_CALLBACK, + ); return $items; } @@ -243,6 +251,55 @@ function views_access($view, $account = } /** + * Menu callback to load a view via AJAX. + */ +function views_ajax() { + if (isset($_REQUEST['view_name']) && isset($_REQUEST['view_display_id'])) { + $name = $_REQUEST['view_name']; + $display_id = $_REQUEST['view_display_id']; + $args = isset($_REQUEST['view_args']) ? explode('/', $_REQUEST['view_args']) : array(); + $path = isset($_REQUEST['view_path']) ? $_REQUEST['view_path'] : NULL; + views_include('ajax'); + $object = new stdClass(); + + $object->status = FALSE; + $object->display = ''; + + // Load the view. + if ($v = views_get_view($name)) { + $view = drupal_clone($v); + if ($view->access($display_id)) { + + // Fix 'q' for paging. + if (!empty($path)) { + $_GET['q'] = $path; + } + + $errors = $view->validate(); + if ($errors === TRUE) { + $object->status = TRUE; + $object->title = $view->get_title(); + $object->display .= $view->preview($display_id, $args); + } + else { + foreach ($errors as $error) { + drupal_set_message($error, 'error'); + } + } + // Register the standard JavaScript callback. + $object->__callbacks = array('Drupal.Views.Ajax.ajaxViewResponse'); + // Allow other modules to extend the data returned. + drupal_alter('ajax_data', $object, 'views', $view); + } + } + $messages = theme('status_messages'); + $object->messages = $messages ? '
' : ''; + + views_ajax_render($object); + } +} + +/** * Set the current 'page view' that is being displayed so that it is easy * for other modules or the theme to identify. */ @@ -908,6 +965,10 @@ function views_exposed_form(&$form_state $form['#theme'] = views_theme_functions('views_exposed_form', $view, $display); + // If using AJAX, we need the form plugin. + if ($view->use_ajax) { + drupal_add_js('misc/jquery.form.js'); + } views_add_js('dependent'); return $form; } Index: includes/plugins.inc =================================================================== RCS file: /cvs/drupal/contributions/modules/views/includes/plugins.inc,v retrieving revision 1.40 diff -u -p -r1.40 plugins.inc --- includes/plugins.inc 21 Mar 2008 09:24:12 -0000 1.40 +++ includes/plugins.inc 25 Mar 2008 19:12:36 -0000 @@ -21,6 +21,7 @@ function views_views_plugins() { 'no ui' => TRUE, 'no remove' => TRUE, 'js' => array('misc/collapse.js', 'misc/textarea.js', 'misc/tabledrag.js', "$path/dependent.js"), + 'use ajax' => TRUE, 'use pager' => TRUE, ), 'page' => array( @@ -28,6 +29,7 @@ function views_views_plugins() { 'help' => t('Display the view as a page, with a URL and menu links.'), 'handler' => 'views_plugin_display_page', 'uses hook menu' => TRUE, + 'use ajax' => TRUE, 'use pager' => TRUE, ), 'block' => array( @@ -35,6 +37,7 @@ function views_views_plugins() { 'help' => t('Display the view as a block.'), 'handler' => 'views_plugin_display_block', 'uses hook block' => TRUE, + 'use ajax' => TRUE, ), ), 'style' => array( @@ -178,6 +181,16 @@ class views_plugin_display extends views } /** + * Does the display use AJAX? + */ + function use_ajax() { + if (!empty($this->definition['use ajax'])) { + return $this->get_option('use_ajax'); + } + return FALSE; + } + + /** * Does the display have a pager enabled? */ function use_pager() { @@ -198,6 +211,7 @@ class views_plugin_display extends views 'header' => array('header', 'header_format', 'header_empty'), 'footer' => array('footer', 'footer_format', 'footer_empty'), 'empty' => array('empty', 'empty_format'), + 'use_ajax' => array('use_ajax'), 'items_per_page' => array('items_per_page', 'offset', 'use_pager', 'pager_element'), 'use_pager' => array('items_per_page', 'offset', 'use_pager', 'pager_element'), 'link_display' => array('link_display'), @@ -249,6 +263,7 @@ class views_plugin_display extends views 'empty' => TRUE, 'empty_format' => TRUE, + 'use_ajax' => TRUE, 'items_per_page' => TRUE, 'offset' => TRUE, 'use_pager' => TRUE, @@ -442,6 +457,13 @@ class views_plugin_display extends views $options['row_plugin']['links']['row_options'] = t('Settings'); } } + if (!empty($this->definition['use ajax'])) { + $options['use_ajax'] = array( + 'category' => 'basic', + 'title' => t('Use AJAX'), + 'value' => $this->get_option('use_ajax') ? t('Yes') : t('No'), + ); + } if (!empty($this->definition['use pager'])) { $options['use_pager'] = array( @@ -575,6 +597,14 @@ class views_plugin_display extends views '#default_value' => $this->get_option('title'), ); break; + case 'use_ajax': + $form['#title'] .= t('Use AJAX when available to load this view'); + $form['use_ajax'] = array( + '#type' => 'radios', + '#options' => array(1 => t('Yes'), 0 => t('No')), + '#default_value' => $this->get_option('use_ajax'), + ); + break; case 'use_pager': $form['#title'] .= t('Use a pager for this view'); $form['use_pager'] = array( @@ -793,6 +823,7 @@ class views_plugin_display extends views case 'access': case 'link_display': case 'php_arg_code': + case 'use_ajax': $this->set_option($section, $form_state['values'][$section]); break; case 'use_pager': @@ -997,6 +1028,9 @@ class views_plugin_display extends views * overridden on an individual display. */ function pre_execute() { + $this->view->set_use_ajax($this->use_ajax()); + // TODO: remove this. + $this->view->set_use_ajax(1); // Copy pager information from the display. $this->view->set_use_pager($this->use_pager()); $this->view->set_pager_element($this->get_option('pager_element')); Index: includes/view.inc =================================================================== RCS file: /cvs/drupal/contributions/modules/views/includes/view.inc,v retrieving revision 1.56 diff -u -p -r1.56 view.inc --- includes/view.inc 24 Mar 2008 20:26:24 -0000 1.56 +++ includes/view.inc 25 Mar 2008 19:12:38 -0000 @@ -27,6 +27,8 @@ class view extends views_db_object { var $args = array(); var $build_info = array(); + var $use_ajax = FALSE; + // pager variables var $pager = array( 'use_pager' => FALSE, @@ -85,6 +87,13 @@ class view extends views_db_object { } /** + * Whether or not AJAX should be used. + */ + function set_use_ajax($use_ajax) { + $this->use_ajax = $use_ajax; + } + + /** * Whether or not the pager should be used. */ function set_use_pager($use_pager) { Index: js/ajax.js =================================================================== RCS file: /cvs/drupal/contributions/modules/views/js/ajax.js,v retrieving revision 1.10 diff -u -p -r1.10 ajax.js --- js/ajax.js 21 Mar 2008 23:20:06 -0000 1.10 +++ js/ajax.js 25 Mar 2008 19:12:39 -0000 @@ -1,11 +1,11 @@ // $Id: ajax.js,v 1.10 2008/03/21 23:20:06 merlinofchaos Exp $ /** -* @file ajax.inc -* -* Handles AJAX submission and response in Views UI. -*/ + * @file ajax_admin.js + * + * Handles AJAX submission and response in Views UI. + */ -Drupal.Views.Ajax = {}; +Drupal.Views.Ajax = Drupal.Views.Ajax || {}; /** * Handles the simple process of setting the ajax form area with new data. Index: js/ajax_view.js =================================================================== RCS file: js/ajax_view.js diff -N js/ajax_view.js --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ js/ajax_view.js 25 Mar 2008 19:12:39 -0000 @@ -0,0 +1,103 @@ +// $Id: $ + +/** + * @file ajaxView.js + * + * Handles AJAX fetching of views, including filter submission and response. + */ + +Drupal.Views.Ajax = Drupal.Views.Ajax || {}; + +/** + * An ajax responder that accepts a packet of JSON data and acts appropriately. + * + * The following fields control behavior. + * - 'display': Display the associated data in the view area. + */ +Drupal.Views.Ajax.ajaxViewResponse = function(target, response) { + + if (response.debug) { + alert(response.debug); + } + + // Check the 'display' for data. + if (response.status && response.display) { + var view = $(target).replaceWith(response.display).get(0); + Drupal.attachBehaviors(view); + } +}; + +/** + * Ajax behavior for views. + */ +Drupal.behaviors.ViewsAjaxView = function() { + + if (Drupal.settings && Drupal.settings.views && Drupal.settings.views.ajaxViews) { + $.each(Drupal.settings.views.ajaxViews, function(i, settings) { + var target; + $('.view-id-'+ settings.view_name +'.view-display-id-'+ settings.view_display_id +':not(.views-processed)') + .addClass('views-processed') + .each(function () { + target = $(this).get(0); + }) + // Process exposed filter forms. + .find('form#views-exposed-form') + .each(function () { + var form = this; + // ajaxSubmit doesn't accept a data argument, so we have to + // pass additional fields this way. + $.each(settings, function(key, setting) { + $(form).append(''); + }); + }) + .submit(function () { + $(this).ajaxSubmit({ + url: Drupal.settings.views.ajax_path, + type: 'GET', + success: function(response) { + // Call all callbacks. + if (response.__callbacks) { + $.each(response.__callbacks, function(i, callback) { + eval(callback)(target, response); + }); + } + }, + error: function() { alert("An error occurred"); }, + dataType: 'json' + }); + + return false; + }) + .end() + // Process pager links. + .find('ul.pager > li > a') + .each(function () { + var viewData = Drupal.Views.parseQueryString($(this).attr('href')); + $.each(settings, function (key, setting) { + viewData[key] = setting; + }); + + $(this) + .click(function () { + $.ajax({ + url: Drupal.settings.views.ajax_path, + type: 'GET', + data: viewData, + success: function(response) { + // Call all callbacks. + if (response.__callbacks) { + $.each(response.__callbacks, function(i, callback) { + eval(callback)(target, response); + }); + } + }, + error: function() { alert("An error occurred"); }, + dataType: 'json' + }); + + return false; + }); + }); + }); + } +}; \ No newline at end of file Index: js/base.js =================================================================== RCS file: /cvs/drupal/contributions/modules/views/js/base.js,v retrieving revision 1.4 diff -u -p -r1.4 base.js --- js/base.js 21 Mar 2008 23:20:06 -0000 1.4 +++ js/base.js 25 Mar 2008 19:12:39 -0000 @@ -3,7 +3,6 @@ Drupal.Views = {}; /** * jQuery UI tabs, Views integration component */ - Drupal.behaviors.viewsTabs = function (context) { if ($.ui && $.ui.tabs) { $('#views-tabset:not(.views-processed)').addClass('views-processed').tabs({ @@ -20,3 +19,36 @@ Drupal.behaviors.viewsTabs = function (c return false; }); } + +/** + * Helper function to parse a querystring. + */ +Drupal.Views.parseQueryString = function (query) { + var args = {}; + var pos = query.indexOf('?'); + if (pos != -1) { + query = query.substring(pos + 1); + } + var pairs = query.split('&'); + for(var i in pairs) { + var pair = pairs[i].split('='); + // Ignore the 'q' path argument, if present. + if (pair[0] != 'q') { + args[pair[0]] = unescape(pair[1].replace(/\+/g, ' ')); + } + } + return args; +}; + +/** + * Helper function to return the last portion of a class. + */ +Drupal.Views.getClassSuffix = function (elt, prefix) { + var className = elt.className.split(' '); + for (var i in className) { + if (className[i].indexOf(prefix) == 0) { + return className[i].substring(prefix.length); + } + } + return false; +}; \ No newline at end of file Index: theme/theme.inc =================================================================== RCS file: /cvs/drupal/contributions/modules/views/theme/theme.inc,v retrieving revision 1.19 diff -u -p -r1.19 theme.inc --- theme/theme.inc 21 Mar 2008 23:20:06 -0000 1.19 +++ theme/theme.inc 25 Mar 2008 19:12:40 -0000 @@ -26,6 +26,8 @@ function template_preprocess_views_view( $vars['rows'] = $view->style_handler->render($view->result); $vars['css_name'] = views_css_safe($view->name); + $vars['name'] = $view->name; + $vars['display_id'] = $view->current_display; if (!$vars['rows']) { $vars['empty'] = $view->display_handler->render_empty(); @@ -56,6 +58,25 @@ function template_preprocess_views_view( $vars['pager'] = theme('pager', $view->exposed_input, $view->pager['items_per_page'], $view->pager['element']); } + // If using AJAX, send identifying data about this view. + if ($view->use_ajax) { + $settings = array( + 'views' => array( + 'ajax_path' => url('views/ajax'), + 'ajaxViews' => array( + array( + 'view_name' => $view->name, + 'view_display_id' => $view->current_display, + 'view_args' => implode('/', $view->args), + 'view_path' => $_GET['q'], + ), + ), + ), + ); + + drupal_add_js($settings, 'setting'); + views_add_js('ajax_view'); + } } /** Index: theme/views-view.tpl.php =================================================================== RCS file: /cvs/drupal/contributions/modules/views/theme/views-view.tpl.php,v retrieving revision 1.5 diff -u -p -r1.5 views-view.tpl.php --- theme/views-view.tpl.php 20 Feb 2008 01:08:25 -0000 1.5 +++ theme/views-view.tpl.php 25 Mar 2008 19:12:40 -0000 @@ -16,7 +16,7 @@ * @ingroup views_templates */ ?> -