Removing translation directories
[project/views.git] / views.module
index 6899831..833cd0c 100644 (file)
 <?php
-// $Id$
-
-function views_init() {
-  // hook init is called even on cached pages, but we don't want to
-  // actually do anything in that case.
-  if (!function_exists('drupal_get_path')) {
-    return;
-  }
-  // Load all our module 'on behalfs'.
-  $path = drupal_get_path('module', 'views') . '/modules';
-  $files = system_listing('views_.*\.inc$', $path, 'name', 0);
-
-  foreach($files as $file) {
-    // The filename format is very specific. It must be views_MODULENAME.inc
-    $module = substr_replace($file->name, '', 0, 6);
-    if (module_exist($module)) {
-      require_once("./$file->filename");
-    }
-  }
-}
 
 // ---------------------------------------------------------------------------
-// Acquire Views Data
+// Drupal Hooks
 
 /**
- * Return the arguments array; construct one if we haven't already. The
- * array is cached in a global, safely named variable so that arguments
- * are only constructed once per run.
+ * Implementation of hook_help()
  */
-function _views_get_arguments($titles = false) {
-  static $views_arguments;
-  global $locale;
-
-  if (!$views_arguments) {
-    $data = cache_get("views_arguments:$locale");
-    $cache = unserialize($data->data);
-    if (is_array($cache)) {
-      $views_arguments = $cache;
-    }
-    else {
-      $arguments = module_invoke_all('views_arguments');
-      foreach ($arguments as $name => $arg) {
-        if ($arg['option'] && !is_array($arg['option'])) {
-          if ($arg['option'] == 'string' || $arg['option'] == 'integer') {
-            $arg['option'] = array('#type' => 'textfield', '#size' => 10, '#maxlength' => 255);
-          }
-          else {
-            $arg['option'] = array('#type' => 'select', '#options' => $arg['option']);
-          }
-        }
-        $views_arguments['base'][$name] = $arg['name'];
-        $views_arguments['title'][$name] = $arg;
-      }
-      $cache = $views_arguments;
-      cache_set("views_arguments:$locale", serialize($cache));
-    }
+function views_help($section) {
+  switch ($section) {
+    case 'admin/help#views':
+      return t('The views module creates customized views of node lists. You may need to activate the Views UI module to get to the user administration pages.');
   }
-  return ($titles ? $views_arguments['base'] : $views_arguments['title']);
 }
 
 /**
- * Constructs the full table information array. Caches it into a global array
- * so that it will only be called once per run.
+ * Implementation of hook_menu()
  */
-function _views_get_tables($full = false) {
-  static $views_tables;
-  global $locale;
-
-  if (!$views_tables) {
-    $data = cache_get("views_tables:$locale");
-    $cache = unserialize($data->data);
+function views_menu($may_cache) {
+  $items = array();
 
-    if (is_array($cache)) {
-      $views_tables = $cache;
-    }
-    else {
-      $table_data = module_invoke_all('views_tables');
-      $views_tables['tables'] = $table_data;
-
-      foreach ($table_data as $name => $table) {
-        if (is_array($table['filters'])) {
-          foreach ($table['filters'] as $filter => $data) {
-            $data['table'] = $name;
-            // translate for deprecated APIs...
-            if ($data['option'] && !is_array($data['option'])) {
-              if ($data['option'] == 'string' || $data['option'] == 'integer') {
-                $data['option'] = array('#type' => 'textfield', '#size' => 10, '#maxlength' => 255);
-              }
-              else {
-                $data['option'] = array('#type' => 'select', '#options' => $data['option']);
-              }
-            }
-            if ($data['list']) {
-              $data['value'] = array('#type' => 'select', '#options' => $data['list']);
-              if ($data['list-type'] != 'select') {
-                $data['value']['#multiple'] = TRUE;
-              }
-            }
-            else if (!$data['value']) {
-              $data['value'] = array('#type' => 'textfield', '#size' => 10, '#maxlength' => 255);
-            }
-            $views_tables['filters']['titles']["$name.$filter"] = $data['name'];
-            $views_tables['filters']['base']["$name.$filter"] = $data;
-          }
-        }
-        if (is_array($table['fields'])) {
-          foreach ($table['fields'] as $field => $data) {
-            if ($data['option'] && !is_array($data['option'])) {
-              if ($data['option'] == 'string' || $data['option'] == 'integer') {
-                $data['option'] = array('#type' => 'textfield', '#size' => 10, '#maxlength' => 255);
-              }
-              else {
-                $data['option'] = array('#type' => 'select', '#options' => $data['option']);
-              }
-            }
-            $data['table'] = $name;
-            $views_tables['fields']['titles']["$name.$field"] = $data['name'];
-            $views_tables['fields']['base']["$name.$field"] = $data;
-          }
-        }
-        if (is_array($table['sorts'])) {
-          foreach ($table['sorts'] as $field => $data) {
-            $data['table'] = $name;
-            if ($data['option'] && !is_array($data['option'])) {
-              if ($data['option'] == 'string' || $data['option'] == 'integer') {
-                $data['option'] = array('#type' => 'textfield', '#size' => 10, '#maxlength' => 255);
-              }
-              else {
-                $data['option'] = array('#type' => 'select', '#options' => $data['option']);
-              }
-            }
-            $views_tables['sorts']['titles']["$name.$field"] = $data['name'];
-            $views_tables['sorts']['base']["$name.$field"] = $data;
-          }
-        }
-      }
-      cache_set("views_tables:$locale", serialize($views_tables));
+  if ($may_cache) {
+    views_load_cache();
+    // Invalidate the views cache to ensure that views data gets rebuilt.
+    // This is the best way to tell that module configuration has changed.
+    if (arg(0) == 'admin' && arg(2) == 'modules') {
+      views_invalidate_cache();
     }
-  }
-  return ($full ? $views_tables : $views_tables['tables']);
-}
 
-/**
- * Gets the filter information; if it doesn't exist, call the function
- * that constructs all that.
- */
-function _views_get_filters($titles = false) {
-  $table_data = _views_get_tables(true);
-  return ($titles ? $table_data['filters']['titles'] : $table_data['filters']['base']);
+    views_menu_standard_items($items);
+  }
+  else {
+    views_menu_inline_items($items);
+  }
+  return $items;
 }
 
 /**
- * Gets the field information; if it doesn't exist, call the function
- * that constructs all that.
+ * Add the menu items for all non-inline views to the menu
  */
-function _views_get_fields($titles = false) {
-  $table_data = _views_get_tables(true);
-  return ($titles ? $table_data['fields']['titles'] : $table_data['fields']['base']);
-}
+function views_menu_standard_items(&$items) {
+  $result = db_query("SELECT * FROM {view_view} WHERE page = 1");
 
-/**
- * Gets the sort information; if it doesn't exist, call the function
- * that constructs all that.
- */
-function _views_get_sorts($titles = false) {
-  $table_data = _views_get_tables(true);
-  return ($titles ? $table_data['sorts']['titles'] : $table_data['sorts']['base']);
-}
+  while ($view = db_fetch_object($result)) {
+    // This happens before the next check; even if it's put off for later
+    // it is still used.
+    $used[$view->name] = true;
 
-/**
- * Invalidate the views cache, forcing a rebuild on the next grab of table data.
- */
-function views_invalidate_cache() {
-  cache_clear_all('views_', true);
-}
+    // Skip views with inline arguments.
+    if ($view->url{0} == '$' || strpos($view->url, '/$') !== FALSE) {
+      continue;
+    }
 
-/**
- * Ensures that views have legitimate information; a bit more is stored on
- * the $view object than is in the database, and this isn't necessarily
- * set when a view is constructed externally.
- */
-function _views_sanitize_view(&$view) {
-  _views_check_arrays($view); // so reference works.
-  foreach ($view->field as $i => $field) {
-    $view->field[$i]['id'] = $view->field[$i]['fullname'] = "$field[tablename].$field[field]";
-    $view->field[$i]['queryname'] = "$field[tablename]_$field[field]";
-  }
+    // unpack the array
+    $view->access = ($view->access ? explode(', ', $view->access) : array());
 
-  foreach ($view->filter as $i => $filter) {
-    $view->filter[$i]['id'] = $view->filter[$i]['field'] = "$filter[tablename].$filter[field]";
+    _views_create_menu_item($items, $view, $view->url);
   }
+  $default_views = _views_get_default_views();
+  $views_status = variable_get('views_defaults', array());
 
-  foreach ($view->exposed_filter as $i => $exposed_filter) {
-    $view->exposed_filter[$i]['id'] = $view->exposed_filter[$i]['field'] = "$exposed_filter[tablename].$exposed_filter[field]";
-  }
+  // Process default views
+  foreach ($default_views as $name => $view) {
+    if ($view->page && !$used[$name] &&
+     ($views_status[$name] == 'enabled' || (!$view->disabled && $views_status[$name] != 'disabled'))) {
 
-  foreach ($view->sort as $i => $sort) {
-    $view->sort[$i]['id'] = $view->sort[$i]['field'] = "$sort[tablename].$sort[field]";
-  }
+      // skip views with inline args
+      if ($view->url{0} == '$' || strpos($view->url, '/$') !== FALSE) {
+        continue;
+      }
 
-  foreach ($view->argument as $i => $argument) {
-    $view->argument[$i]['id'] = $view->argument[$i]['type'];
+      _views_create_menu_item($items, $view, $view->url);
+    }
   }
 }
 
-/**
- * Build default view information from all modules and cache it.
- */
-function _views_get_default_views() {
-  static $views_default_views;
-  global $locale;
+function views_menu_inline_items(&$items) {
+  // I don't think we gain anything by caching these, there should never
+  // be all that many, and caching == a database hit.
+  $tokens = module_invoke_all('views_url_tokens');
 
-  if (!$views_default_views) {
-    $data = cache_get("views_default_views:$locale");
-    $cache = unserialize($data->data);
+  $args = explode('/', $_GET['q']);
+  $urls = views_get_all_urls();
+  foreach ($urls as $view_name => $url) {
+    if ($url{0} != '$' && strpos($url, '/$') === FALSE) {
+      if (module_exists('views_ui') && user_access('administer views')) {
+        $view_args = $args;
 
-    if (is_array($cache)) {
-      $views_default_views = $cache;
+        foreach (explode('/', $url) as $num => $element) {
+          if ($element != $args[$num]) {
+            continue 2;
+          }
+          unset($view_args[$num]);
+        }
+        views_menu_admin_items($items, $view_name, $view_args, $args);
+      }
     }
     else {
-      // We have to make sure table data is built in order to be sure about providers.
-      $tables = array_keys(_views_get_tables());
-
-      $views = module_invoke_all('views_default_views');
-      $views_default_views = array();
-      foreach ($views as $i => $view) {
-        if (!is_array($view->requires) || !array_diff($view->requires, $tables)) {
-          _views_sanitize_view($view);
-          $views_default_views[$i] = $view;
+      // Do substitution on args.
+      $use_view = $use_menu = FALSE;
+      $menu_args = $view_args = $menu_path = array();
+
+      foreach (explode('/', $url) as $num => $element) {
+        if ($element{0} == '$') {
+          // If we pass the token check, this view is definitely being used.
+          list($token, $argument) = explode('-', $element);
+          if ($tokens[$token] && function_exists($tokens[$token])) {
+            if (!($use_view = $use_menu = $tokens[$token]($element, $argument, arg($num)))) {
+              break;
+            }
+          }
+          $menu_path[] = $view_args[] = arg($num);
+        }
+        else {
+          unset($menu_args[$num]);
+          $menu_path[] = $element;
+          if ($element != arg($num)) {
+            $use_menu = FALSE;
+          }
+        }
+        // we are only using views that match our URL, up to the
+        // point where we hit an inline arg.
+        if (!$use_view && $element != arg($num)) {
+          break;
         }
       }
-      cache_set("views_default_views:$locale", serialize($views_default_views));
+      if ($use_view) {
+        $path = implode('/', $menu_path);
+        $view = views_get_view($view_name);
+        $view->args = $view_args;
+        _views_create_menu_item($items, $view, $path, MENU_CALLBACK, $view_args);
+      }
+
+      if (module_exists('views_ui') && user_access('administer views') && $use_menu) {
+        views_menu_admin_items($items, $view_name, $menu_args, $args);
+      }
     }
   }
-  return $views_default_views;
 }
 
 /**
- * Return the style plugins; construct one if we haven't already. The
- * array is cached in a static variable so that arguments
- * are only constructed once per run.
+ * Implementation of hook_views_tabs().
  */
-function _views_get_style_plugins($titles = false) {
-  static $views_style_plugins;
-  global $locale;
-
-  if (!$views_style_plugins) {
-    $data = cache_get("views_style_plugins:$locale");
-    $cache = unserialize($data->data);
-    if (is_array($cache)) {
-      $views_style_plugins = $cache;
-    }
-    else {
-      $arguments = module_invoke_all('views_style_plugins');
-      foreach ($arguments as $name => $arg) {
-        $views_style_plugins['title'][$name] = $arg['name'];
-        $views_style_plugins['base'][$name] = $arg;
-      }
-      $cache = $views_style_plugins;
-      cache_set("views_style_plugins:$locale", serialize($cache));
-    }
+function views_views_tabs($op) {
+  switch ($op) {
+    case 'names':
+      return array('edit', 'view', 'clone', 'export', 'add');
+      break;
   }
-  return ($titles ? $views_style_plugins['title'] : $views_style_plugins['base']);
 }
 
-// ---------------------------------------------------------------------------
-// Drupal Hooks
-
 /**
- * Implementation of hook_help()
+ * Add the adminstrative items to a view.
  */
-function views_help($section) {
-  switch ($section) {
-    case 'admin/help#views':
-    case 'admin/modules#description':
-      return t('The views module creates customized views of node lists.');
+function views_menu_admin_items(&$items, $view_name, $view_args, $args) {
+  // Remove args that are tabs from $args.
+  $tabs = array();
+  foreach (module_implements('views_tabs') as $module) {
+    $function = $module .'_views_tabs';
+    $tabs = array_merge($tabs, (array) $function('names'));
+  }
+  // See what the last arg is.
+  $last_arg = array_pop($args);
+  if (in_array($last_arg, $tabs)) {
+    array_pop($view_args);
+  }
+  else {
+    $args[] = $last_arg;
   }
+  $view = views_get_view($view_name);
+  $path = implode('/', $args);
+  views_ui_add_menu_items($items, $view, $path, $path != $_GET['q'] && !empty($view_args), $view_args);
 }
 
 /**
- * Implementation of hook_menu()
+ * Load all of the URLs we use; this is cached in a special manner
+ * in an attempt to make the menu system both flexible and yet not
+ * overly intensive.
  */
-function views_menu($may_cache) {
-  $items = array();
-  global $locale;
-
-  if ($may_cache) {
-    // Invalidate the views cache to ensure that views data gets rebuilt.
-    // This is the best way to tell that module configuration has changed.
-    if (arg(0) == 'admin' && arg(1) == 'modules') {
-      views_invalidate_cache();
-    }
-
-    $result = db_query("SELECT * FROM {view_view} WHERE page = 1");
-    $views_with_inline_args = array();
+function views_get_all_urls() {
+  $cache = cache_get("views_urls", 'cache_views');
+  if ($cache == 0) {
+    $views = array();
+    $used = array();
+    // avoid creating empty path items by requiring an URL to be set
+    $result = db_query("SELECT name, url FROM {view_view} WHERE page = 1 AND LENGTH(url) > 0");
 
     while ($view = db_fetch_object($result)) {
-      // unpack the array
-      $view->access = ($view->access ? explode(', ', $view->access) : array());
-
-      // This happens before the next check; even if it's put off for later
-      // it is still used.
-      $used[$view->name] = true;
-
-      if (strrpos($view->url, '$arg')) {
-        $arg_result = db_query("SELECT * FROM {view_argument} WHERE vid = %d", $view->vid);
-        while ($view->argument[] = db_fetch_array($arg_result));
-        array_pop($view->argument); // get rid of the NULL at the end.
-        $views_with_inline_args[$view->name] = $view;
-        continue;
-      }
-      _views_create_menu_item($items, $view, $view->url);
+      $used[$view->name] = TRUE;
+      $views[$view->name] = $view->url;
     }
+
+    views_load_cache();
     $default_views = _views_get_default_views();
     $views_status = variable_get('views_defaults', array());
 
     foreach ($default_views as $name => $view) {
-      if ($view->page && !$used[$name] &&
-       ($views_status[$name] == 'enabled' || (!$view->disabled && $views_status[$name] != 'disabled'))) {
-
-        if (strrpos($view->url, '$arg')) {
-          $views_with_inline_args[$view->name] = $view;
-          continue;
-        }
-
-        _views_create_menu_item($items, $view, $view->url);
+      if ($view->page && !$used[$name] && ($views_status[$name] == 'enabled' || (!$view->disabled && $views_status[$name] != 'disabled'))) {
+          $views[$view->name] = $view->url;
       }
     }
-    cache_set("views_with_inline_args:$locale", serialize($views_with_inline_args));
+    cache_set("views_urls", 'cache_views', serialize($views));
   }
   else {
-    $data = cache_get("views_with_inline_args:$locale");
-    $views = unserialize($data->data);
-    if (is_array($views)) {
-      foreach ($views as $view) {
-        // Do substitution on args.
-        $view_args = array();
-        $menu_path = array();
-        foreach (explode('/', $view->url) as $num => $element) {
-          if ($element == '$arg') {
-            $menu_path[] = arg($num);
-            $view_args[] = arg($num);
-            $view->args[] = arg($num);
-          }
-          else {
-            $menu_path[] = $element;
-          }
-        }
-        $path = implode('/', $menu_path);
-        _views_create_menu_item($items, $view, $path, MENU_CALLBACK, $view_args);
-      }
-    }
+    $views = unserialize($cache->data);
   }
-  return $items;
+
+  return $views;
 }
 
 /**
  * Helper function to add a menu item for a view.
  */
 function _views_create_menu_item(&$items, $view, $path, $local_task_type = MENU_NORMAL_ITEM, $args = array()) {
-  static $roles = NULL;
-  if ($roles == NULL) {
-    global $user;
-    $roles = array_keys($user->roles);
-  }
-  $title = views_get_title($view, 'menu');
+  $title = filter_xss_admin(views_get_title($view, 'menu'));
   $type = _views_menu_type($view);
   if ($type == MENU_LOCAL_TASK || $type == MENU_DEFAULT_LOCAL_TASK) {
     $weight = $view->menu_tab_weight;
   }
-  $access = !$view->access || array_intersect($view->access, $roles);
+  $access = views_access($view);
   $items[] = _views_menu_item($path, $title, $view, $args, $access, $type, $weight);
 
   if ($type == MENU_DEFAULT_LOCAL_TASK) {
-    $items[] = _views_menu_item(dirname($path), $title, $view, $args, $access, $local_task_type, $weight);
+    switch ($view->menu_tab_default_parent_type) {
+      case 'tab':
+        $parent_type = MENU_LOCAL_TASK;
+        break;
+      case 'normal':
+        $parent_type = MENU_NORMAL_ITEM;
+        break;
+      case 'existing':
+        $parent_type = 0;
+        break;
+    }
+    if ($parent_type) {
+      $title = filter_xss_admin(views_get_title($view, 'menu-parent'));
+      $weight = $view->menu_parent_tab_weight;
+      $items[] = _views_menu_item(dirname($path), $title, $view, $args, $access, $parent_type, $weight);
+    }
   }
 }
 
@@ -417,15 +280,52 @@ function _views_menu_type($view) {
 }
 
 /**
+ * Implementation of hook_perm
+ */
+function views_perm() {
+  return array('access all views');
+}
+
+/**
+ * Determine if the specified user has access to a view.
+ */
+function views_access($view, $account = NULL) {
+  if (!$account) {
+    global $user;
+    $account = $user;
+  }
+
+  // Administrator privileges
+  if (user_access('access all views', $account)) {
+    return TRUE;
+  }
+
+  // All views with an empty access setting are available to all roles.
+  if (!$view->access) {
+    return TRUE;
+  }
+
+  // Otherwise, check roles
+  static $roles = array();
+  if (!isset($roles[$account->uid])) {
+    $roles[$account->uid] = array_keys($account->roles);
+    $roles[$account->uid][] = $account->uid ? DRUPAL_AUTHENTICATED_RID : DRUPAL_ANONYMOUS_RID;
+  }
+
+  return array_intersect($view->access, $roles[$account->uid]);
+}
+
+/**
  * Implementation of hook_block()
  */
 function views_block($op = 'list', $delta = 0) {
   $block = array();
   if ($op == 'list') {
+    views_load_cache();
     // Grab views from the database and provide them as blocks.
     $result = db_query("SELECT vid, block_title, page_title, name FROM {view_view} WHERE block = 1");
     while ($view = db_fetch_object($result)) {
-      $block[$view->name]['info'] = views_get_title($view, 'block-info');
+      $block[$view->name]['info'] = filter_xss_admin(views_get_title($view, 'block-info'));
     }
 
     $default_views = _views_get_default_views();
@@ -434,7 +334,8 @@ function views_block($op = 'list', $delta = 0) {
     foreach ($default_views as $name => $view) {
       if (!isset($block[$name]) && $view->block &&
         ($views_status[$name] == 'enabled' || (!$view->disabled && $views_status[$name] != 'disabled'))) {
-        $block[$name]['info'] = views_get_title($view, 'block');
+        $title = filter_xss_admin(views_get_title($view, 'block'));
+        $block[$name]['info'] = empty($title) ? $name : $title;
       }
     }
     return $block;
@@ -467,6 +368,7 @@ function _views_check_arrays(&$view) {
  * for a default view by that name.
  */
 function views_get_view($view_name) {
+  views_load_cache();
   $view = _views_load_view($view_name);
   if ($view) {
     return $view;
@@ -479,7 +381,9 @@ function views_get_view($view_name) {
   $default_views = _views_get_default_views();
 
   if (isset($default_views[$view_name])) {
-    return $default_views[$view_name];
+    $view = $default_views[$view_name];
+    $view->is_cacheable = _views_is_cacheable($view);
+    return $view;
   }
 }
 
@@ -517,6 +421,7 @@ function views_view_page() {
  * This views a view by block. Can be used as a callback or programmatically.
  */
 function views_view_block($vid) {
+  views_load_cache();
   $view = views_get_view($vid);
 
   if (!$view || !$view->block) {
@@ -529,14 +434,14 @@ function views_view_block($vid) {
   }
 
   $roles = array_keys($user->roles);
-  if ($view->access && !array_intersect($roles, $view->access)) {
+  if (!views_access($view)) {
     return NULL;
   }
 
   $content = views_build_view('block', $view, array(), false, $view->nodes_per_block);
   if ($content) {
     $block['content'] = $content;
-    $block['subject'] = views_get_title($view, 'block');
+    $block['subject'] = filter_xss_admin(views_get_title($view, 'block'));
     return $block;
   }
   else {
@@ -544,6 +449,22 @@ function views_view_block($vid) {
   }
 }
 
+function &views_set_current_view(&$view) {
+  static $current_view = NULL;
+  if ($view !== NULL) {
+    unset($current_view);
+    $current_view = &$view;
+    unset($GLOBALS['current_view']);
+    $GLOBALS['current_view'] = &$view;
+  }
+  return $current_view;
+}
+
+function &views_get_current_view() {
+  $dummy = NULL;
+  return views_set_current_view($dummy);
+}
+
 /**
  * This builds the basic view.
  * @param $type
@@ -561,6 +482,8 @@ function views_view_block($vid) {
  *      result: Database object you can use db_fetch_object on.
  *    'items' -- return info array as above, except instead of result,
  *      items: An array of objects containing the results of the query.
+ *    'queries' -- returns an array, summarizing the queries, but does not
+ *      run them.
  * @param $view
  *   The actual view object. Use views_get_view() if you only have the name or
  *   vid.
@@ -581,15 +504,31 @@ function views_view_block($vid) {
  * @param $page
  *   $use_pager is false, and $limit is !0, $page tells it what page to start
  *   on, in case for some reason a particular section of view is needed,
+ * @param $offset
+ *   If $use_pager == false, skip the first $offset results. Does not work
+ *   with pager.
  *   without paging on.
+ * @param $filters
+ *   An array of exposed filter ops and values to use with the exposed filter system
+ *   Array has the form:
+ *     [0] => array('op' => 'foo', 'value' => 'bar'),
+ *     [1] => array('value' => 'zoo'), // for a locked operator, e.g.
+ *   If no array is passed in, views will look in the $_GET array for potential filters
 */
-function views_build_view($type, &$view, $args = array(), $use_pager = false, $limit = 0, $page = 0) {
+function views_build_view($type, &$view, $args = array(), $use_pager = false, $limit = 0, $page = 0, $offset = 0, $filters = NULL) {
+  views_load_cache();
+
   // Fix a number of annoying whines when NULL is passed in..
   if ($args == NULL) {
     $args = array();
   }
 
-  $GLOBALS['current_view'] = &$view;
+  // if no filter values are passed in, get them from the $_GET array
+  if ($filters == NULL) {
+    $filters = views_get_filter_values();
+  }
+
+  views_set_current_view($view);
 
   $view->build_type = $type;
   $view->type = ($type == 'block' ? $view->block_type : $view->page_type);
@@ -603,57 +542,48 @@ function views_build_view($type, &$view, $args = array(), $use_pager = false, $l
     ob_end_clean();
   }
 
-  $plugins = _views_get_style_plugins();
-  if ($view->query) {
-    $info['query'] = $view->query;
-    $info['countquery'] = $view->countquery;
-
-    if ($plugins[$view->type]['needs_table_header']) {
-      $view->table_header = _views_construct_header($view, $fields);
-    }
-  }
-  else {
-    $path = drupal_get_path('module', 'views');
-    require_once("./$path/views_query.inc");
+  $view->use_pager = $use_pager;
+  $view->pager_limit = $limit;
+  $view->current_page = $page;
+  $view->offset = $offset;
 
-    $info = _views_build_query($view, $args);
-    if ($info['fail']) {
-      return FALSE;
-    }
+  // Call a hook that'll let modules modify the view query before it is created
+  foreach (module_implements('views_pre_query') as $module) {
+    $function = $module .'_views_pre_query';
+    $output .= $function($view);
   }
 
-  // Run-time replacement so we can do cacheing
-  $replacements = module_invoke_all('views_query_substitutions', $view);
-  foreach ($replacements as $src => $dest) {
-    $info['query'] = str_replace($src, $dest, $info['query']);
-    $info['countquery'] = str_replace($src, $dest, $info['countquery']);
+  $info = _views_get_query($view, $args, $filters);
 
-    if (is_array($info['args'])) {
-      foreach ($info['args'] as $id => $arg) {
-        $info['args'][$id] = str_replace($src, $dest, $arg);
-      }
-    }
+  if ($info['fail']) {
+    return FALSE;
   }
 
-  $query = db_rewrite_sql($info['query'], 'node');
-
-  if ($use_pager) {
-    $cquery = db_rewrite_sql($info['countquery'], 'node', 'nid', $info['rewrite_args']);
-    $result = pager_query($query, $limit, $use_pager - 1, $cquery, $info['args']);
-    $view->total_rows = $GLOBALS['pager_total_items'][$use_pager - 1];
-  }
-  else {
-    $result = ($limit ? db_query_range($query, $info['args'], $page * $limit, $limit) : db_query($query, $info['args']));
-  }
-  $view->num_rows = db_num_rows($result);
-  if ($type == 'result') {
-    $info['result'] = $result;
+  if ($type == 'queries') {
     return $info;
   }
 
   $items = array();
-  while ($item = db_fetch_object($result)) {
-    $items[] = $item;
+  if ($info['query']) {
+    $query = db_rewrite_sql($info['query'], 'node');
+
+    if ($view->use_pager) {
+      $cquery = db_rewrite_sql($info['countquery'], 'node', 'nid', $info['rewrite_args']);
+      $result = pager_query($query, $view->pager_limit, $view->use_pager - 1, $cquery, $info['args']);
+      $view->total_rows = $GLOBALS['pager_total_items'][$view->use_pager - 1];
+    }
+    else {
+      $result = ($view->pager_limit ? db_query_range($query, $info['args'], $view->current_page * $view->pager_limit + $view->offset, $view->pager_limit) : db_query($query, $info['args']));
+    }
+    $view->num_rows = db_num_rows($result);
+    if ($type == 'result') {
+      $info['result'] = $result;
+      return $info;
+    }
+
+    while ($item = db_fetch_object($result)) {
+      $items[] = $item;
+    }
   }
 
   if ($type == 'items') {
@@ -669,8 +599,6 @@ function views_build_view($type, &$view, $args = array(), $use_pager = false, $l
 
   $view->real_url = views_get_url($view, $args);
 
-  $view->use_pager = $use_pager;
-  $view->pager_limit = $limit;
   $output .= views_theme('views_view', $view, $type, $items, $info['level'], $args);
 
   // Call a hook that'll let modules modify the view just after it is displayed.
@@ -686,6 +614,22 @@ function views_build_view($type, &$view, $args = array(), $use_pager = false, $l
 // Utility
 
 /**
+ * Load the cache sub-module
+ */
+function views_load_cache() {
+  $path = drupal_get_path('module', 'views');
+  require_once("./$path/views_cache.inc");
+}
+
+/**
+ * Load the query sub-module
+ */
+function views_load_query() {
+  $path = drupal_get_path('module', 'views');
+  require_once("./$path/views_query.inc");
+}
+
+/**
  * Easily theme any item to a view.
  * @param $function
  *   The name of the function to call.
@@ -751,7 +695,7 @@ function _views_get_timezone() {
     if (!$already_set) {
       if ($GLOBALS['db_type'] == 'mysqli' || version_compare(mysql_get_server_info(), '4.1.3', '>=')) {
         db_query("SET @@session.time_zone = '+00:00'");
-      } 
+      }
       $already_set = true;
     }
   }
@@ -786,8 +730,12 @@ function views_get_url($view, $args) {
  * Figure out what the title of a view should be.
  */
 function views_get_title($view, $context = 'menu', $args = NULL) {
-  if ($context == 'menu' && $view->menu_title)
+  if ($context == 'menu-parent' && $view->menu_parent_title) {
+    return $view->menu_parent_title;
+  }
+  if (($context == 'menu' || $context == 'menu-parent' || $context == 'admin') && $view->menu_title) {
     return $view->menu_title;
+  }
 
   if ($context == 'block-info') {
     return $view->description ? $view->description : $view->name;
@@ -808,11 +756,11 @@ function views_get_title($view, $context = 'menu', $args = NULL) {
     }
   }
 
-  if (!$title && ($context == 'menu' || $context == 'page')) {
+  if (!$title && ($context == 'menu' || $context == 'menu-parent' || $context == 'page' || $context == 'admin')) {
     $title = $view->page_title;
   }
 
-  if (!$title) {
+  if (!$title && ($context == 'block' || $context == 'admin')) {
     $title = $view->block_title;
   }
 
@@ -820,6 +768,7 @@ function views_get_title($view, $context = 'menu', $args = NULL) {
     return $title;
   }
 
+  views_load_cache();
   $arginfo = _views_get_arguments();
   foreach ($view->argument as $i => $arg) {
     if (!isset($args[$i])) {
@@ -846,10 +795,11 @@ function views_get_title($view, $context = 'menu', $args = NULL) {
  */
 function _views_is_cacheable(&$view) {
   // views with arguments are immediately not cacheable.
-  if (!empty($view->argument) || !empty($view->exposed_filter)) {
+  if (!empty($view->argument) || !empty($view->exposed_filter) || !empty($view->no_cache)) {
     return false;
   }
 
+  views_load_cache();
   $filters = _views_get_filters();
 
   foreach ($view->filter as $i => $filter) {
@@ -866,6 +816,13 @@ function _views_is_cacheable(&$view) {
   return true;
 }
 
+/**
+ * Invalidate the views cache, forcing a rebuild on the next grab of table data.
+ */
+function views_invalidate_cache() {
+  cache_clear_all('*', 'cache_views', true);
+}
+
 // ---------------------------------------------------------------------------
 // Database functions
 
@@ -873,7 +830,50 @@ function _views_is_cacheable(&$view) {
  * Provide all the fields in a view.
  */
 function _views_view_fields() {
-  return array('vid', 'name', 'description', 'access', 'page', 'page_title', 'page_header', 'page_header_format', 'page_footer', 'page_footer_format', 'page_empty', 'page_empty_format', 'page_type', 'use_pager', 'nodes_per_page', 'url', 'menu', 'menu_tab', 'menu_tab_default', 'menu_tab_weight', 'menu_title', 'block', 'block_title', 'block_use_page_header', 'block_header', 'block_header_format', 'block_use_page_footer', 'block_footer', 'block_footer_format', 'block_use_page_empty', 'block_empty', 'block_empty_format', 'block_type', 'nodes_per_block', 'block_more', 'url', 'breadcrumb_no_home', 'changed', 'query', 'countquery', 'view_args_php');
+  return array(
+    'vid' => '%d',
+    'name' => "'%s'",
+    'description' => "'%s'",
+    'access' => "'%s'",
+    'page' => '%d',
+    'page_title' => "'%s'",
+    'page_header' => "'%s'",
+    'page_header_format' => '%d',
+    'page_footer' => "'%s'",
+    'page_footer_format' => '%d',
+    'page_empty' => "'%s'",
+    'page_empty_format' => '%d',
+    'page_type' => "'%s'",
+    'use_pager' => '%d',
+    'nodes_per_page' => '%d',
+    'url' => "'%s'",
+    'menu' => '%d',
+    'menu_tab' => '%d',
+    'menu_tab_default' => '%d',
+    'menu_tab_weight' => '%d',
+    'menu_title' => "'%s'",
+    'menu_tab_default_parent_type' => "'%s'",
+    'menu_parent_title' => "'%s'",
+    'menu_parent_tab_weight' => '%d',
+    'block' => '%d',
+    'block_title' => "'%s'",
+    'block_use_page_header' => '%d',
+    'block_header' => "'%s'",
+    'block_header_format' => '%d',
+    'block_use_page_footer' => '%d',
+    'block_footer' => "'%s'",
+    'block_footer_format' => '%d',
+    'block_use_page_empty' => '%d',
+    'block_empty' => "'%s'",
+    'block_empty_format' => '%d',
+    'block_type' => "'%s'",
+    'nodes_per_block' => '%d',
+    'block_more' => '%d',
+    'breadcrumb_no_home' => '%d',
+    'changed' => '%d',
+    'view_args_php' => "'%s'",
+    'is_cacheable' => '%d',
+  );
 }
 
 /**
@@ -889,6 +889,11 @@ function _views_delete_view($view) {
   db_query("DELETE FROM {view_sort} where vid=%d", $view->vid);
   db_query("DELETE FROM {view_argument} where vid=%d", $view->vid);
   db_query("DELETE FROM {view_tablefield} where vid=%d", $view->vid);
+  db_query("DELETE FROM {view_filter} where vid=%d", $view->vid);
+  db_query("DELETE FROM {view_exposed_filter} where vid=%d", $view->vid);
+
+  cache_clear_all('views_query:' . $view->name, 'cache_views');
+  cache_clear_all(); // In Drupal 5.0 and later this clears the page cache only.
 }
 
 /**
@@ -952,6 +957,7 @@ function _views_load_view($arg) {
 
   $result = db_query("SELECT * FROM {view_filter} WHERE vid = $view->vid ORDER BY position ASC");
 
+  views_load_cache();
   $filters = _views_get_filters();
   $view->filter = array();
   while ($filter = db_fetch_array($result)) {
@@ -994,19 +1000,7 @@ function _views_load_view($arg) {
 function _views_save_view($view) {
   _views_check_arrays($view);
 
-  // cache the query
-  if (_views_is_cacheable($view)) {
-    $path = drupal_get_path('module', 'views');
-    require_once("./$path/views_query.inc");
-
-    $info = _views_build_query($view);
-    $view->query = _views_replace_args($info['query'], $info['args']);
-    $view->countquery = _views_replace_args($info['countquery'], $info['args']);
-  }
-  else {
-    $view->query = NULL;
-    $view->countquery = NULL;
-  }
+  $view->is_cacheable = _views_is_cacheable($view);
 
   $view->access = implode(', ', $view->access);
 
@@ -1016,19 +1010,21 @@ function _views_save_view($view) {
     // update
     // Prepare the query:
     foreach ($view as $key => $value) {
-      if (in_array($key, $fields)) {
-        $q[] = db_escape_string($key) ." = '%s'";
+      if (array_key_exists($key, $fields)) {
+        $q[] = db_escape_string($key) ." = $fields[$key]";
         $v[] = $value;
       }
     }
 
     // Update the view in the database:
-    db_query("UPDATE {view_view} SET ". implode(', ', $q) ." WHERE vid = '$view->vid'", $v);
+    db_query("UPDATE {view_view} SET " . implode(', ', $q) . " WHERE vid = '$view->vid'", $v);
     db_query("DELETE from {view_sort} WHERE vid='$view->vid'");
     db_query("DELETE from {view_argument} WHERE vid='$view->vid'");
     db_query("DELETE from {view_tablefield} WHERE vid='$view->vid'");
     db_query("DELETE from {view_filter} WHERE vid='$view->vid'");
     db_query("DELETE from {view_exposed_filter} WHERE vid='$view->vid'");
+
+    cache_clear_all('views_query:' . $view->name, 'cache_views');
   }
   else {
     // insert
@@ -1039,14 +1035,14 @@ function _views_save_view($view) {
 
     // Prepare the query:
     foreach ($view as $key => $value) {
-      if (in_array((string) $key, $fields)) {
+      if (array_key_exists((string) $key, $fields)) {
         $k[] = db_escape_string($key);
         $v[] = $value;
-        $s[] = is_numeric($value) ? '%d' : "'%s'";
+        $s[] = $fields[$key];
       }
     }
 
-    db_query("INSERT INTO {view_view} (". implode(", ", $k) .") VALUES (". implode(", ", $s) .")", $v);
+    db_query("INSERT INTO {view_view} (" . implode(", ", $k) . ") VALUES (" . implode(", ", $s) . ")", $v);
   }
 
   foreach ($view->sort as $i => $sort) {
@@ -1071,6 +1067,9 @@ function _views_save_view($view) {
   foreach ($view->exposed_filter as $i => $arg) {
     db_query("INSERT INTO {view_exposed_filter} (vid, field, label, optional, is_default, single, operator, position) VALUES (%d, '%s', '%s', %d, %d, %d, %d, %d)", $view->vid, $arg['field'], $arg['label'], $arg['optional'], $arg['is_default'], $arg['single'], $arg['operator'], $i);
   }
+  cache_clear_all('views_urls', 'cache_views');
+  cache_clear_all(); // clear the page cache as well.
+  return $view->vid;
 }
 
 // ---------------------------------------------------------------------------
@@ -1085,7 +1084,7 @@ function views_new_table($table_name, $provider, $left_table, $left_field, $righ
   $table['provider'] = $provider;
   $table['join']['left']['table'] = $left_table;
   $table['join']['left']['field'] = $left_field;
-  $table['join']['right']['field'] = $left_field;
+  $table['join']['right']['field'] = $right_field;
   if ($extra) {
     $table['join']['extra'] = $extra;
   }
@@ -1278,34 +1277,61 @@ function _views_construct_header($view, $fields) {
     $header = array();
     $info = $fields[$field['fullname']];
 
-    $header['data'] = ($field['label'] ? $field['label'] : $info['name']);
-
     if ($field['sortable']) {
+      $header['data'] = ($field['label'] ? $field['label'] : $info['name']);
       if (function_exists($info['sort_handler']))  {
         $header['field'] = $info['sort_handler']($field, $info);
       }
       else {
-        $header['field'] = $field['fullname'];
+        $header['field'] = $field['queryname'];
       }
     }
+    else if ($field['label']) {
+      $header['data'] = $field['label'];
+    }
+
     if ($field['defaultsort']) {
       $header['sort'] = strtolower($field['defaultsort']);
     }
 
     // Add CSS id to table cell header cell.
-    $header['id'] = "view-field-$field[queryname]";
-    $header['class'] = "view-cell-header";
+    $header['class'] = "view-cell-header" . views_css_safe(' view-field-'. $field['queryname']);
     $headers[] = $header;
   }
   return $headers;
 }
 
 function theme_views_display_filters($view) {
-  $form = views_filters_form($view);
-  return drupal_get_form("views_filters_$view->name", $form, 'views_filters');
+  return drupal_get_form("views_filters", $view);
 }
 
-function views_filters_form($view) {
+function views_filters($view) {
+  $form = _views_build_filters_form($view);
+  $form['#view_name'] = $view->name;
+  $form['#method'] = 'get';
+  $form['#process'] = array('views_filters_process' => array());
+  $form['#action'] = url($view->real_url ? $view->real_url : $view->url, NULL, NULL, true);
+  $form['view'] = array('#type' => 'value', '#value' => $view);
+  $form['submit'] = array('#type' => 'button', '#name' => '', '#value' => t('Submit'));
+
+  // clean URL get forms breaks if we don't give it a 'q'.
+  if (!(bool)variable_get('clean_url', '0')) {
+    $form['q'] = array(
+      '#type' => 'hidden',
+      '#value' => $view->real_url ? $view->real_url : $view->url,
+      '#name' => 'q',
+    );
+  }
+
+
+  return $form;
+}
+
+function _views_build_filters_form($view) {
+  // When the form is retrieved through an AJAX callback, the cache hasn't
+  // been loaded yet. The cache is necesssary for _views_get_filters().
+  views_load_cache();
+
   $filters = _views_get_filters();
   foreach ($view->exposed_filter as $count => $expose) {
     $id = $expose['id'];
@@ -1325,7 +1351,6 @@ function views_filters_form($view) {
       }
 
       $form["op$count"] = array(
-        '#name' => "op$count", // get rid of edit[] array.
         '#type' => 'select',
         '#default_value' => $filter['operator'],
         '#options' => $operator,
@@ -1338,7 +1363,6 @@ function views_filters_form($view) {
 
     // set up the filter widget.
     $item = $filterinfo['value'];
-    $item['#name'] = "filter$count";
 
     if (!is_array($item['#options']) && function_exists($item['#options'])) {
       $item['#options'] = $item['#options']('value', $filterinfo);
@@ -1349,6 +1373,8 @@ function views_filters_form($view) {
 
     if ($expose['single']) {
       unset($item['#multiple']);
+      // On multi-select categories the #size element is in the form by default.  We remove it to allow the single-select drop-down filter to work.
+      unset($item['#size']);
     }
     if ($expose['optional'] && is_array($item['#options'])) {
       $item['#options'] = array('**ALL**' => t('<All>')) + $item['#options'];
@@ -1363,39 +1389,27 @@ function views_filters_form($view) {
     }
     $form["filter$count"] = $item;
   }
-  $form['#method'] = 'get';
-  $form['#process'] = array('views_filters_process' => array());
-  $form['view'] = array('#type' => 'value', '#value' => $view);
-  $form['submit'] = array('#type' => 'button', '#name' => '', '#value' => t('Submit'));
-    // clean URL get forms breaks if we don't give it a 'q'.
-    if (!(bool)variable_get('clean_url', '0')) {
-      $form['q'] = array(
-        '#type' => 'hidden',
-        '#value' => $_GET['q'],
-        '#name' => 'q',
-      );
-    }
-
 
   return $form;
 }
 
 function views_filters_process($form) {
   unset($form['form_id']);
+  unset($form['form_token']);
   return $form;
 }
 function theme_views_filters($form) {
   $view = $form['view']['#value'];
 
   foreach ($view->exposed_filter as $count => $expose) {
-    $row[] = form_render($form["op$count"]) . form_render($form["filter$count"]);
+    $row[] = drupal_render($form["op$count"]) . drupal_render($form["filter$count"]);
     $label[] = $expose['label'];
   }
-  $row[] = form_render($form['submit']);
+  $row[] = drupal_render($form['submit']);
   $label[] = ''; // so the column count is the same.
 
   // make the 'q' come first
-  return form_render($form['q']) . theme('table', $label, array($row)) . form_render($form);
+  return drupal_render($form['q']) . theme('table', $label, array($row)) . drupal_render($form);
 }
 
 /**
@@ -1407,14 +1421,14 @@ function theme_views_view_list($view, $nodes, $type) {
   foreach ($nodes as $node) {
     $item = '';
     foreach ($view->field as $field) {
-      if ($fields[$field['id']]['visible'] !== FALSE) {
+      if (!isset($fields[$field['id']]['visible']) && $fields[$field['id']]['visible'] !== FALSE) {
         if ($field['label']) {
-          $item .= "<div class='view-label view-label-$field[queryname]'>" . $field['label'] . "</div>";
+          $item .= "<div class='view-label ". views_css_safe('view-label-'. $field['queryname']) ."'>" . $field['label'] . "</div>";
         }
-        $item .= "<div class='view-field view-data-$field[queryname]'>" . views_theme_field('views_handle_field', $field['queryname'], $fields, $field, $node, $view) . "</div>";
+        $item .= "<div class='view-field ". views_css_safe('view-data-'. $field['queryname']) ."'>" . views_theme_field('views_handle_field', $field['queryname'], $fields, $field, $node, $view) . "</div>";
       }
     }
-    $items[] = "<div class='view-item view-item-$view->name'>$item</div>\n"; // l($node->title, "node/$node->nid");
+    $items[] = "<div class='view-item ". views_css_safe('view-item-'. $view->name) ."'>$item</div>\n"; // l($node->title, "node/$node->nid");
   }
   if ($items) {
     return theme('item_list', $items);
@@ -1432,7 +1446,7 @@ function theme_views_view_table($view, $nodes, $type) {
     foreach ($view->field as $field) {
       if ($fields[$field['id']]['visible'] !== FALSE) {
         $cell['data'] = views_theme_field('views_handle_field', $field['queryname'], $fields, $field, $node, $view);
-        $cell['class'] = "view-field view-field-$field[queryname]";
+        $cell['class'] = "view-field ". views_css_safe('view-field-'. $field['queryname']);
         $row[] = $cell;
       }
     }
@@ -1472,11 +1486,11 @@ function views_set_breadcrumb($view) {
     $where = 1;
     foreach ($view->args as $level => $arg) {
       if ($view->argument[$level]['argdefault'] != 1) {
-        $breadcrumb[] = l(views_get_title($view, 'page', $args), $url);
+        $breadcrumb[] = l(filter_xss_admin(views_get_title($view, 'page', $args)), $url, NULL, NULL, NULL, NULL, TRUE);
         // For next round.
       }
       $args[] = $arg;
-      if ($where && $where = strpos('$arg', $url)) {
+      if ($where && $where = strpos($url, '$arg')) {
         $url = substr_replace($url, $arg, $where, 4);
       }
       else {
@@ -1494,19 +1508,26 @@ function views_get_textarea($view, $type, $textarea) {
   $format = $var . '_format';
 
   if ($view->$var) {
-    return "<div class='view-$textarea view-$textarea-$view->name'>"
+    return "<div class='". views_css_safe('view-'. $textarea .' view-'. $textarea .'-'. $view->name) ."'>"
       . check_markup($view->$var, $view->$format, false) . "</div>\n";
   }
 }
 
 /**
+ * Prepare the specified string for use as a CSS identifier.
+ */
+function views_css_safe($string) {
+  return str_replace('_', '-', $string);
+}
+
+/**
  * Display a view.
  */
 function theme_views_view($view, $type, $nodes, $level = NULL, $args = NULL) {
   $num_nodes = count($nodes);
 
   if ($type == 'page') {
-    drupal_set_title(views_get_title($view, 'page'));
+    drupal_set_title(filter_xss_admin(views_get_title($view, 'page')));
     views_set_breadcrumb($view);
   }
 
@@ -1518,15 +1539,14 @@ function theme_views_view($view, $type, $nodes, $level = NULL, $args = NULL) {
     $output .= views_theme('views_display_filters', $view);
   }
 
-  if ($num_nodes) {
-
+  $plugins = _views_get_style_plugins();
+  $view_type = ($type == 'block') ? $view->block_type : $view->page_type;
+  if ($num_nodes || $plugins[$view_type]['even_empty']) {
     if ($level !== NULL) {
-      $output .= views_theme('views_summary', $view, $type, $level, $nodes, $args);
+      $output .= "<div class='view-summary ". views_css_safe('view-summary-'. $view->name) ."'>". views_theme($plugins[$view_type]['summary_theme'], $view, $type, $level, $nodes, $args) . '</div>';
     }
     else {
-      $plugins = _views_get_style_plugins();
-      $view_type = ($type == 'block') ? $view->block_type : $view->page_type;
-      $output .= "<div class='view-content view-content-".$view->name."'>".views_theme($plugins[$view_type]['theme'], $view, $nodes, $type).'</div>';
+      $output .= "<div class='view-content ". views_css_safe('view-content-'. $view->name) ."'>". views_theme($plugins[$view_type]['theme'], $view, $nodes, $type) . '</div>';
     }
     $output .= views_get_textarea($view, $type, 'footer');
 
@@ -1543,7 +1563,7 @@ function theme_views_view($view, $type, $nodes, $level = NULL, $args = NULL) {
   }
 
   if ($output) {
-    $output = "<div class='view view-$view->name'>$output</div>\n";
+    $output = "<div class='view ". views_css_safe('view-'. $view->name) ."'>$output</div>\n";
   }
   return $output;
 }
@@ -1565,25 +1585,6 @@ function views_get_summary_link($argtype, $item, $base) {
 }
 
 /**
- * In a summary view, each entry links to a more specific entry
- * in that view. Construct the base of that link.
- */
-/*
-function views_get_summary_link_base($argtype, $url, $level, $args) {
-  $arginfo = _views_get_arguments();
-  if (!function_exists($arginfo[$argtype]['handler'])) {
-    return NULL;
-  }
-
-  $arg = $url;
-  for ($i = 0; $i < $level; $i++) {
-    $arg .= "/$args[$i]";
-  }
-
-  return $arg;
-}
-*/
-/**
  * Display a summary version of a view.
  */
 function theme_views_summary($view, $type, $level, $nodes, $args) {
@@ -1617,35 +1618,35 @@ function theme_views_summary($view, $type, $level, $nodes, $args) {
  * Format a date.
  */
 function views_handler_field_date($fieldinfo, $fielddata, $value, $data) {
-  return $value ? format_date($value) : theme_views_nodate();
+  return $value ? format_date($value) : theme('views_nodate');
 }
 
 /**
  * Format a date using small representation.
  */
 function views_handler_field_date_small($fieldinfo, $fielddata, $value, $data) {
-  return $value ? format_date($value, 'small') : theme_views_nodate();
+  return $value ? format_date($value, 'small') : theme('views_nodate');
 }
 
 /**
  * Format a date using large representation.
  */
 function views_handler_field_date_large($fieldinfo, $fielddata, $value, $data) {
-  return $value ? format_date($value, 'large') : theme_views_nodate();
+  return $value ? format_date($value, 'large') : theme('views_nodate');
 }
 
 /**
  * Format a date using custom representation.
  */
 function views_handler_field_date_custom($fieldinfo, $fielddata, $value, $data) {
-  return $value ? format_date($value, 'custom', $fielddata['options']) : theme_views_nodate();
+  return $value ? format_date($value, 'custom', $fielddata['options']) : theme('views_nodate');
 }
 
 /**
  * Format a date as "X time ago".
  */
 function views_handler_field_since($fieldinfo, $fielddata, $value, $data) {
-  return $value ? t('%time ago', array('%time' => format_interval(time() - $value))) : theme_views_nodate();
+  return $value ? t('%time ago', array('%time' => format_interval(time() - $value, is_numeric($fielddata['options']) ? $fielddata['options'] : 2))) : theme('views_nodate');
 }
 
 function theme_views_nodate() {
@@ -1656,7 +1657,7 @@ function theme_views_nodate() {
  * Provide a list of all standard supproted date output handlers.
  */
 function views_handler_field_dates() {
-return array(
+  return array(
     'views_handler_field_date_small'  => t('As Short Date'),
     'views_handler_field_date'        => t('As Medium Date'),
     'views_handler_field_date_large'  => t('As Long Date'),
@@ -1665,6 +1666,60 @@ return array(
   );
 }
 
+function views_handler_sort_date_options() {
+  return array(
+    '#type' => 'select',
+    '#options' => array(
+      'normal' => t('Normal'),
+      'minute' => t('Granularity: minute'),
+      'hour'   => t('Granularity: hour'),
+      'day'    => t('Granularity: day'),
+      'month'  => t('Granularity: month'),
+      'year'   => t('Granularity: year'),
+    ),
+  );
+}
+
+function views_handler_sort_date($op, &$query, $sortinfo, $sort) {
+  $timezone = _views_get_timezone();
+  switch($sort['options']) {
+    case 'normal':
+    default:
+      $table = $sortinfo['table'];
+      $field = $sortinfo['field'];
+      break;
+    case 'minute':
+      $field = "DATE_FORMAT(FROM_UNIXTIME($sortinfo[table].$sortinfo[field]), '%Y%m%%d%H%i')";
+      break;
+    case 'hour':
+      $field = "DATE_FORMAT(FROM_UNIXTIME($sortinfo[table].$sortinfo[field]), '%Y%m%%d%H')";
+      break;
+    case 'day':
+      $field = "DATE_FORMAT(FROM_UNIXTIME($sortinfo[table].$sortinfo[field]+$timezone), '%Y%m%%d')";
+      break;
+    case 'month':
+      $field = "DATE_FORMAT(FROM_UNIXTIME($sortinfo[table].$sortinfo[field]+$timezone), '%Y%m')";
+      break;
+    case 'year':
+      $field = "DATE_FORMAT(FROM_UNIXTIME($sortinfo[table].$sortinfo[field]+$timezone), '%Y')";
+      break;
+  }
+  $alias = $as = $sortinfo['table'] . '_' . $sortinfo['field'];
+  if (!$table) {
+     $as .= '_orderby';
+     $alias = $field;
+  }
+
+//  $query->add_field($field, $table, $as);
+//  $query->orderby[] = "$alias $sort[sortorder]";
+  $query->add_orderby($table, $field, $sort['sortorder'], $as);
+}
+
+function views_handler_sort_date_minute($op, &$query, $sortinfo, $sort) {
+  $field = "DATE_FORMAT(FROM_UNIXTIME($table.$sortinfo[field]), '%Y%m%%d%H%m')";
+  $query->add_orderby(NULL, $field, $sort['sortorder']);
+}
+
 /**
  * Format a field as an integer.
  */
@@ -1757,6 +1812,22 @@ function views_handler_operator_yesno() {
   return array('1' => t('Yes'), '0' => t('No'));
 }
 
+/*
+ * Break x,y,z and x+y+z into an array. Numeric only.
+ */
+function _views_break_phrase($str) {
+  if (preg_match('/^([0-9]+[+ ])+[0-9]+$/', $str)) {
+    // The '+' character in a query string may be parsed as ' '.
+    return array('or', preg_split('/[+ ]/', $str));
+  }
+  else if (preg_match('/^([0-9]+,)*[0-9]+$/', $str)) {
+    return array('and', explode(',', $str));
+  }
+  else {
+    return NULL;
+  }
+}
+
 /**
  * Default Views style plugins. Implementation of hook_views_style_plugins()
  */
@@ -1767,6 +1838,7 @@ function views_views_style_plugins() {
       'theme' => 'views_view_list',
       'validate' => 'views_ui_plugin_validate_list',
       'needs_fields' => true,
+      'weight' => -10,
     ),
     'table' => array(
       'name' => t('Table View'),
@@ -1774,23 +1846,77 @@ function views_views_style_plugins() {
       'validate' => 'views_ui_plugin_validate_table',
       'needs_fields' => true,
       'needs_table_header' => true,
+      'weight' => -9,
     ),
     'teaser' => array(
       'name' => t('Teaser List'),
       'theme' => 'views_view_teasers',
+      'weight' => -8,
     ),
     'node' => array(
       'name' => t('Full Nodes'),
       'theme' => 'views_view_nodes',
+      'weight' => -7,
     ),
   );
 }
 
 /**
+ * Default Views URL tokens
+ */
+function views_views_url_tokens() {
+  return array(
+    '$arg' => 'views_url_arg',
+    '$node' => 'views_url_node',
+    '$user' => 'views_url_user',
+  );
+}
+
+/**
+ * Handle '$arg' in a URL. Any non-empty value is true.
+ */
+function views_url_arg($token, $argument, $arg) {
+  return $arg !== '' && $arg !== NULL;
+}
+
+/**
+ * Handle '$node' in a URL. Any valid node wil ldo.
+ */
+function views_url_node($token, $argument, $arg) {
+  if (!is_numeric($arg)) {
+    return FALSE;
+  }
+
+  $node = node_load($arg);
+  if (!$node) {
+    return FALSE;
+  }
+
+  if ($argument && $node->type != $argument) {
+    return FALSE;
+  }
+
+  // if a node loads, return true.
+  return TRUE;
+}
+
+/**
+ * Handle '$user' in a URL. Any valid user wil ldo.
+ */
+function views_url_user($token, $argument, $arg) {
+  if (!is_numeric($arg)) {
+    return FALSE;
+  }
+
+  // if a user loads, return true.
+  return (bool) user_load($arg);
+}
+
+/**
  * A list of options to be used in LIKE queries
  */
 function views_handler_operator_like() {
-  return array('=' => t('Is Equal To'), 'contains' => t('Contains'), 'starts' => t('Starts With'), 'ends' => t('Ends With'), 'not' => t('Does Not Contain'));
+  return array('=' => t('Is Equal To'), 'contains' => t('Contains'), 'word' => t('Contains Any Word'), 'allwords' => t('Contains All Words'), 'starts' => t('Starts With'), 'ends' => t('Ends With'), 'not' => t('Does Not Contain'));
 }
 
 /**
@@ -1806,10 +1932,6 @@ function views_handler_filter_like($op, $filter, $filterinfo, &$query) {
     case 'handler':
       $table = $filterinfo['table'];
       $column = $filterinfo['field'];
-      if (empty($column)) {
-        $fieldbits = explode('.', $filter['field']);
-        $column = $fieldbits[1];
-      }
       $field = "$table.$column";
       $query->ensure_table($table);
 
@@ -1818,6 +1940,34 @@ function views_handler_filter_like($op, $filter, $filterinfo, &$query) {
           $query->add_where("UPPER(%s) LIKE UPPER('%%%s%%')",
             $field, $filter['value']);
           break;
+        case 'word':
+        case 'allwords':
+          preg_match_all('/ (-?)("[^"]+"|[^" ]+)/i', ' '. $filter['value'], $matches, PREG_SET_ORDER);
+          foreach ($matches as $match) {
+            $phrase = false;
+            // Strip off phrase quotes
+            if ($match[2]{0} == '"') {
+              $match[2] = substr($match[2], 1, -1);
+              $phrase = true;
+            }
+            $words = trim($match[2], ',?!();:-');
+            $words = $phrase ? array($words) : preg_split('/ /', $words, -1, PREG_SPLIT_NO_EMPTY);
+            foreach ($words as $word) {
+              $where[] = "UPPER(%s) LIKE UPPER('%%%s%%')";
+              $values[] = $field;
+              $values[] = trim($word, " ,!?");
+            }
+          }
+          if ($filter['operator'] == 'word') {
+            $where = '('. implode(' OR ', $where) .')';
+          }
+          else {
+            $where = implode(' AND ', $where);
+          }
+          // previously this was a call_user_func_array but that's unnecessary
+          // as views will unpack an array that is a single arg.
+          $query->add_where($where, $values);
+          break;
         case 'starts':
           $query->add_where("UPPER(%s) LIKE UPPER('%s%%')",
             $field, $filter['value']);
@@ -1840,6 +1990,23 @@ function views_handler_filter_like($op, $filter, $filterinfo, &$query) {
 }
 
 /**
+ * Custom filter for IS NULL and IS NOT NULL operations
+ * Operator must be 'IS' or 'IS NOT'
+ */
+function views_handler_filter_null($op, $filter, $filterinfo, &$query) {
+  switch($op) {
+    case 'handler':
+      $table = $filterinfo['table'];
+      $column = $filterinfo['field'];
+      $field = "$table.$column";
+      $query->ensure_table($table);
+      $operator = $filter['operator'];
+      $query->add_where("$field $operator NULL");
+      break;
+  }
+}
+
+/**
  * Format a field as file size.
  */
 function views_handler_field_filesize($fieldinfo, $fielddata, $value, $data) {
@@ -1850,14 +2017,13 @@ function views_handler_field_filesize($fieldinfo, $fielddata, $value, $data) {
  * Handle a timestamp filter.
  */
 function views_handler_filter_timestamp($op, $filter, $filterinfo, &$query) {
-  $value = $filter['value'] == 'now' ? "***CURRENT_TIME***" : strtotime($filter['value']);
+  $value = 0;
+  if ($filter['value']) {
+    $value = $filter['value'] == 'now' ? "***CURRENT_TIME***" : strtotime($filter['value']);
+  }
 
   $table = $filterinfo['table'];
   $column = $filterinfo['field'];
-  if (empty($column)) {
-    $fieldbits = explode('.', $filter['field']);
-    $column = $fieldbits[1];
-  }
   $field = "$table.$column";
   if ($filterinfo['from_unixtime']) {
     $field = "from_UNIXTIME($field)";
@@ -1867,6 +2033,22 @@ function views_handler_filter_timestamp($op, $filter, $filterinfo, &$query) {
 }
 
 /**
+ * Provide validation for filters with array values. The $name
+ * must be provided by whatever added the validator to the
+ * form gadget.
+ */
+function views_filter_validate_array($form, $name) {
+  $op = $_POST['op'];
+
+  if ($op != t('Save') && $op != t('Save and edit')) {
+    return; // only validate on saving!
+  }
+  if (!isset($form['#value']) || $form['#value'] == array()) {
+    form_error($form, t('@filter must have a value!', array('@filter' => $name)));
+  }
+}
+
+/**
  * Provide a form gadget for dates.
  */
 function views_handler_filter_date_value_form() {
@@ -1888,14 +2070,15 @@ function views_views_query_substitutions($view) {
  * @param $view_name
  *    The name of the view.
  * @param $limit
- *   Required if $use_pager is set; if $limit is set and $use_pager is
+ *   Maximum number of nodes displayed on one page. if $limit is set and $use_pager is
  *   not, this will be the maximum number of records returned. This is ignored
- *   if using a view set to return a random result. If $use_pager is set and 
- *   this field is not, you'll get a SQL error. Don't do that!
+ *   if using a view set to return a random result.
+ *   If NULL, the setting defined for the $view will be used.
  * @param $use_pager
  *   If set, use a pager. Set this to the pager id you want it to use if you
  *   plan on using multiple pagers on a page. Note that the pager element id
  *   will be decremented in order to have the IDs start at 0.
+ *   If NULL, the setting defined for the $view will be used.
  * @param $type
  *    'page' -- Produce output as a page, sent through theme.
  *      The only real difference between this and block is that
@@ -1903,9 +2086,65 @@ function views_views_query_substitutions($view) {
  *    'block' -- Produce output as a block, sent through theme.
  *    'embed' -- Use this if you want to embed a view onto another page,
  *      and don't want any block or page specific things to happen to it.
+ * @param $view_args
+ *   An array containing the arguments for the view
  */
-function theme_view( $view_name, $limit = 0, $use_pager = false, $type = 'embed' ) {
+function theme_view($view_name, $limit = NULL, $use_pager = NULL, $type = 'embed', $view_args = array()) {
   if ($view = views_get_view($view_name)) {
+    $use_pager = isset($use_pager) ? $use_pager : $view->use_pager;
+    $limit_default = ($type == 'block') ? $view->nodes_per_block : $view->nodes_per_page;
+    $limit = isset($limit) ? $limit : $limit_default;
     return views_build_view($type, $view, $view_args, $use_pager, $limit);
   }
 }
+
+/**
+ * This function is used as a central place to manage some translatable text strings
+ * that are used in different places.
+ * @param $text
+ *   which string to return.
+ */
+function views_t_strings($text) {
+  switch ($text) {
+    case 'filter date':
+      return t('The "Value" can either be a date in the format: CCYY-MM-DD HH:MM:SS or the word "now" to use the current time. You may enter a positive or negative number in the "Option" field that will represent the amount of seconds that will be added or substracted to the time; this is most useful when combined with "now". If you have the jscalendar module from jstools installed, you can use a popup date picker here.');
+  }
+}
+
+/**
+ * This function fetches filter values from the $_GET object
+ */
+function views_get_filter_values($input = NULL) {
+  if (!isset($input)) {
+    $input = $_GET;
+  }
+  $values = array();
+  foreach($input as $key => $value) {
+    if(strpos($key, 'op') === 0) { // starts with op
+      $values[substr($key, 2)]['op'] = $value; // two letters in op
+    } elseif (strpos($key, 'filter') === 0) { // starts with op
+      $values[substr($key, 6)]['filter'] = $value; // six letters in filter
+    }
+  }
+  return $values;
+}
+
+/**
+ * Invalidate the views cache when taxonomy vocabulary changes.
+ */
+function views_taxonomy($op, $type, $object = NULL) {
+  if ($type == 'vocabulary' && $op == 'delete' || $op == 'insert' || $op == 'update') {
+    views_invalidate_cache();
+  }
+}
+
+function views_form_alter($form_id, &$form) {
+  if ($form_id == 'profile_field_form') {
+    views_invalidate_cache();
+  }
+}
+
+// An implementation of hook_devel_caches() from devel.module. Must be in views.module so it always is included.
+function views_devel_caches() {
+  return array('cache_views');
+}
\ No newline at end of file