Removing translation directories
[project/views.git] / views.module
index 3d30b74..833cd0c 100644 (file)
@@ -1,6 +1,4 @@
 <?php
-// $Id$
-// $Name$
 
 // ---------------------------------------------------------------------------
 // Drupal Hooks
@@ -28,7 +26,7 @@ function views_menu($may_cache) {
     if (arg(0) == 'admin' && arg(2) == 'modules') {
       views_invalidate_cache();
     }
-    
+
     views_menu_standard_items($items);
   }
   else {
@@ -141,20 +139,36 @@ function views_menu_inline_items(&$items) {
 }
 
 /**
+ * Implementation of hook_views_tabs().
+ */
+function views_views_tabs($op) {
+  switch ($op) {
+    case 'names':
+      return array('edit', 'view', 'clone', 'export', 'add');
+      break;
+  }
+}
+
+/**
  * Add the adminstrative items to a view.
  */
 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, array('edit', 'view', 'clone', 'export'))) {
+  if (in_array($last_arg, $tabs)) {
     array_pop($view_args);
   }
   else {
     $args[] = $last_arg;
   }
-
-  $path = implode('/', $args);
   $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);
 }
 
@@ -168,7 +182,8 @@ function views_get_all_urls() {
   if ($cache == 0) {
     $views = array();
     $used = array();
-    $result = db_query("SELECT name, url FROM {view_view} WHERE page = 1");
+    // 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)) {
       $used[$view->name] = TRUE;
@@ -181,9 +196,7 @@ function views_get_all_urls() {
 
     foreach ($default_views as $name => $view) {
       if ($view->page && !$used[$name] && ($views_status[$name] == 'enabled' || (!$view->disabled && $views_status[$name] != 'disabled'))) {
-        if ($view->url{0} == '$' || strpos($view->url, '/$') !== FALSE) {
           $views[$view->name] = $view->url;
-        }
       }
     }
     cache_set("views_urls", 'cache_views', serialize($views));
@@ -208,7 +221,22 @@ function _views_create_menu_item(&$items, $view, $path, $local_task_type = MENU_
   $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);
+    }
   }
 }
 
@@ -273,7 +301,7 @@ function views_access($view, $account = NULL) {
   }
 
   // All views with an empty access setting are available to all roles.
-  if (!$view->access) { 
+  if (!$view->access) {
     return TRUE;
   }
 
@@ -494,10 +522,10 @@ function views_build_view($type, &$view, $args = array(), $use_pager = false, $l
   if ($args == NULL) {
     $args = array();
   }
-  
+
   // if no filter values are passed in, get them from the $_GET array
   if ($filters == NULL) {
-    $filters = _views_get_filter_values();
+    $filters = views_get_filter_values();
   }
 
   views_set_current_view($view);
@@ -514,6 +542,11 @@ function views_build_view($type, &$view, $args = array(), $use_pager = false, $l
     ob_end_clean();
   }
 
+  $view->use_pager = $use_pager;
+  $view->pager_limit = $limit;
+  $view->current_page = $page;
+  $view->offset = $offset;
+
   // 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';
@@ -530,17 +563,17 @@ function views_build_view($type, &$view, $args = array(), $use_pager = false, $l
     return $info;
   }
 
-  $query = db_rewrite_sql($info['query'], 'node');
-
   $items = array();
-  if ($query) {
-    if ($use_pager) {
+  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, $limit, $use_pager - 1, $cquery, $info['args']);
-      $view->total_rows = $GLOBALS['pager_total_items'][$use_pager - 1];
+      $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 = ($limit ? db_query_range($query, $info['args'], $page * $limit + $offset, $limit) : db_query($query, $info['args']));
+      $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') {
@@ -566,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.
@@ -664,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;
     }
   }
@@ -699,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' || $context == 'admin') && $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;
@@ -721,7 +756,7 @@ function views_get_title($view, $context = 'menu', $args = NULL) {
     }
   }
 
-  if (!$title && ($context == 'menu' || $context == 'page' || $context == 'admin')) {
+  if (!$title && ($context == 'menu' || $context == 'menu-parent' || $context == 'page' || $context == 'admin')) {
     $title = $view->page_title;
   }
 
@@ -795,7 +830,50 @@ function views_invalidate_cache() {
  * 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', 'view_args_php', 'is_cacheable');
+  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',
+  );
 }
 
 /**
@@ -811,6 +889,8 @@ 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.
@@ -930,8 +1010,8 @@ 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;
       }
     }
@@ -955,10 +1035,10 @@ 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];
       }
     }
 
@@ -989,6 +1069,7 @@ function _views_save_view($view) {
   }
   cache_clear_all('views_urls', 'cache_views');
   cache_clear_all(); // clear the page cache as well.
+  return $view->vid;
 }
 
 // ---------------------------------------------------------------------------
@@ -1226,12 +1307,13 @@ function theme_views_display_filters($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(
@@ -1246,6 +1328,10 @@ function views_filters($view) {
 }
 
 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'];
@@ -1286,8 +1372,8 @@ function _views_build_filters_form($view) {
     }
 
     if ($expose['single']) {
-      unset($item['#multiple']);\r
-      // 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.\r
+      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'])) {
@@ -1303,7 +1389,7 @@ function _views_build_filters_form($view) {
     }
     $form["filter$count"] = $item;
   }
-  
+
   return $form;
 }
 
@@ -1335,7 +1421,7 @@ 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 ". views_css_safe('view-label-'. $field['queryname']) ."'>" . $field['label'] . "</div>";
         }
@@ -1404,7 +1490,7 @@ function views_set_breadcrumb($view) {
         // 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 {
@@ -1595,6 +1681,7 @@ function views_handler_sort_date_options() {
 }
 
 function views_handler_sort_date($op, &$query, $sortinfo, $sort) {
+  $timezone = _views_get_timezone();
   switch($sort['options']) {
     case 'normal':
     default:
@@ -1602,19 +1689,19 @@ function views_handler_sort_date($op, &$query, $sortinfo, $sort) {
       $field = $sortinfo['field'];
       break;
     case 'minute':
-      $field = "DATE_FORMAT(FROM_UNIXTIME($sortinfo[table].$sortinfo[field]), '%Y%m%%d%H%m')";
+      $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]), '%Y%m%%d')";
+      $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]), '%Y%m%)";
+      $field = "DATE_FORMAT(FROM_UNIXTIME($sortinfo[table].$sortinfo[field]+$timezone), '%Y%m')";
       break;
     case 'year':
-      $field = "DATE_FORMAT(FROM_UNIXTIME($sortinfo[table].$sortinfo[field]), '%Y%')";
+      $field = "DATE_FORMAT(FROM_UNIXTIME($sortinfo[table].$sortinfo[field]+$timezone), '%Y')";
       break;
   }
   $alias = $as = $sortinfo['table'] . '_' . $sortinfo['field'];
@@ -1804,7 +1891,7 @@ function views_url_node($token, $argument, $arg) {
   if (!$node) {
     return FALSE;
   }
-  
+
   if ($argument && $node->type != $argument) {
     return FALSE;
   }
@@ -1903,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) {
@@ -1994,7 +2098,6 @@ function theme_view($view_name, $limit = NULL, $use_pager = NULL, $type = 'embed
   }
 }
 
-
 /**
  * This function is used as a central place to manage some translatable text strings
  * that are used in different places.
@@ -2011,9 +2114,12 @@ function views_t_strings($text) {
 /**
  * This function fetches filter values from the $_GET object
  */
-function _views_get_filter_values() {
+function views_get_filter_values($input = NULL) {
+  if (!isset($input)) {
+    $input = $_GET;
+  }
   $values = array();
-  foreach($_GET as $key => $value) {
+  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
@@ -2021,4 +2127,24 @@ function _views_get_filter_values() {
     }
   }
   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