url, '$arg') !== FALSE) { continue; } // 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; _views_create_menu_item($items, $view, $view->url); } $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') !== FALSE) { continue; } _views_create_menu_item($items, $view, $view->url); } } } else { $data = cache_get("views_with_inline_args:$locale", 'cache'); if ($data == 0) { // There's no cache for our language, regenerate it. views_reset_inline_args_cache($locale); $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); } } } return $items; } /** * Reset the views with inline arguments cache for a locale. */ function views_reset_inline_args_cache($locale = 'en') { $result = db_query("SELECT * FROM {view_view} WHERE page = 1"); $views_with_inline_args = array(); while ($view = db_fetch_object($result)) { // Skip over any non-argument views if (strrpos($view->url, '$arg') == FALSE) { continue; } // 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; $arg_result = db_query("SELECT * FROM {view_argument} WHERE vid = %d", $view->vid); while ($arg = db_fetch_array($arg_result)) { $view->argument[] = $arg; } $views_with_inline_args[$view->name] = $view; } 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; } } } cache_set("views_with_inline_args:$locale", 'cache', serialize($views_with_inline_args)); } /** * 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'); $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); $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); } } /** * Helper function to create a menu item for a view. */ function _views_menu_item($path, $title, $view, $args, $access, $type, $weight = NULL) { array_unshift($args, $view->name); $retval = array('path' => $path, 'title' => $title, 'callback' => 'views_view_page', 'callback arguments' => $args, 'access' => user_access('access content') && $access, 'type' => $type, ); if ($weight !== NULL) { $retval['weight'] = $weight; } return $retval; } /** * Determine what menu type a view needs to use. */ function _views_menu_type($view) { if ($view->menu) { if ($view->menu_tab_default) { $type = MENU_DEFAULT_LOCAL_TASK; } else if ($view->menu_tab) { $type = MENU_LOCAL_TASK; } else { $type = MENU_NORMAL_ITEM; } } else { $type = MENU_CALLBACK; } return $type; } /** * 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'] = filter_xss_admin(views_get_title($view, 'block-info')); } $default_views = _views_get_default_views(); $views_status = variable_get('views_defaults', array()); 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'] = filter_xss_admin(views_get_title($view, 'block')); } } return $block; } else if ($op == 'view') { return views_view_block($delta); } } // --------------------------------------------------------------------------- // View Construction /** * Ensure that all the arrays in a view exist so we don't run into array * operations on a non-array error. */ function _views_check_arrays(&$view) { $fields = array('field', 'sort', 'argument', 'filter', 'exposed_filter', 'access'); foreach($fields as $field) { if (!is_array($view->$field)) { $view->$field = array(); } } return $view; } /** * This function loads a view by name or vid; if not found in db, it looks * 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; } if (is_int($view_name)) { return; // don't bother looking if view_name is an int! } $default_views = _views_get_default_views(); if (isset($default_views[$view_name])) { return $default_views[$view_name]; } } /** * This views a view by page, and should only be used as a callback. * * @param $view_name * The name or id of the view to load * @param $args * The arguments from the end of the url. Usually passed from the menu system. * * @return * The view page. */ function views_view_page() { $args = func_get_args(); $view_name = array_shift($args); $view = views_get_view($view_name); if (!$view) { drupal_not_found(); exit; } $output = views_build_view('page', $view, $args, $view->use_pager, $view->nodes_per_page); if ($output === FALSE) { drupal_not_found(); exit; } return $output; } /** * 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) { return NULL; } global $user; if (!$user->roles) { return NULL; } $roles = array_keys($user->roles); if ($view->access && !array_intersect($roles, $view->access)) { return NULL; } $content = views_build_view('block', $view, array(), false, $view->nodes_per_block); if ($content) { $block['content'] = $content; $block['subject'] = filter_xss_admin(views_get_title($view, 'block')); return $block; } else { return NULL; } } 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 * 'page' -- Produce output as a page, sent through theme. * The only real difference between this and block is that * a page uses drupal_set_title to change the page title. * '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. * 'result' -- return an $info array. The array contains: * query: The actual query ran. * countquery: The count query that would be run if limiting was required. * summary: True if an argument was missing and a summary was generated. * level: What level the missing argument was at. * 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. * @param $args * args taken from the URL. Not relevant for many views. Can be null. * @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. To go with the * default setting, set to $view->use_pager. Note that the pager element * id will be decremented in order to have the IDs start at 0. * @param $limit * Required if $use_pager is set; 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. To go with the default * setting set to $view->nodes_per_page or $view->nodes_per_block. If * $use_pager is set and this field is not, you'll get a SQL error. Don't * do that! * @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. */ function views_build_view($type, &$view, $args = array(), $use_pager = false, $limit = 0, $page = 0, $offset = 0) { views_load_cache(); // Fix a number of annoying whines when NULL is passed in.. if ($args == NULL) { $args = array(); } views_set_current_view($view); $view->build_type = $type; $view->type = ($type == 'block' ? $view->block_type : $view->page_type); if ($view->view_args_php) { ob_start(); $result = eval($view->view_args_php); if (is_array($result)) { $args = $result; } ob_end_clean(); } // 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); } $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, _views_get_fields()); } } else { views_load_query(); $info = _views_build_query($view, $args); if ($info['fail']) { return FALSE; } } // 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']); if (is_array($info['args'])) { foreach ($info['args'] as $id => $arg) { $info['args'][$id] = str_replace($src, $dest, $arg); } } } $query = db_rewrite_sql($info['query'], 'node'); if ($type == 'queries') { return $info; } 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 + $offset, $limit) : db_query($query, $info['args'])); } $view->num_rows = db_num_rows($result); if ($type == 'result') { $info['result'] = $result; return $info; } $items = array(); while ($item = db_fetch_object($result)) { $items[] = $item; } if ($type == 'items') { $info['items'] = $items; return $info; } // Call a hook that'll let modules modify the view just before it is displayed. foreach (module_implements('views_pre_view') as $module) { $function = $module .'_views_pre_view'; $output .= $function($view, $items); } $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. foreach (module_implements('views_post_view') as $module) { $function = $module .'_views_post_view'; $output .= $function($view, $items, $output); } return $output; } // --------------------------------------------------------------------------- // 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. * @param $view * The view being themed. */ function views_theme() { $args = func_get_args(); $function = array_shift($args); $view = $args[0]; if (!($func = theme_get_function($function . "_" . $view->name))) { $func = theme_get_function($function); } if ($func) { return call_user_func_array($func, $args); } } /** * Easily theme any item to a field name. * field name will be in the format of TABLENAME_FIELDNAME * You have to understand a bit about the views data to utilize this. * * @param $function * The name of the function to call. * @param $field_name * The field being themed. */ function views_theme_field() { $args = func_get_args(); $function = array_shift($args); $field_name = array_shift($args); $view = array_pop($args); if (!($func = theme_get_function($function . '_' . $view->name . '_' . $field_name))) { if (!($func = theme_get_function($function . '_' . $field_name))) { $func = theme_get_function($function); } } if ($func) { return call_user_func_array($func, $args); } } /** * Figure out what timezone we're in; needed for some date manipulations. */ function _views_get_timezone() { global $user; if (variable_get('configurable_timezones', 1) && $user->uid && strlen($user->timezone)) { $timezone = $user->timezone; } else { $timezone = variable_get('date_default_timezone', 0); } // set up the database timezone if (in_array($GLOBALS['db_type'], array('mysql', 'mysqli'))) { static $already_set = false; 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; } } return $timezone; } /** * Figure out what the URL of the view we're currently looking at is. */ function views_get_url($view, $args) { $url = $view->url; if (!empty($url)) { $where = 1; foreach ($args as $arg) { // This odd construct prevents us from strposing once there is no // longer an $arg to replace. if ($where && $where = strpos($url, '$arg')) { $url = substr_replace($url, $arg, $where, 4); } else { $url .= "/$arg"; } } } return $url; } /** * 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) return $view->menu_title; if ($context == 'block-info') { return $view->description ? $view->description : $view->name; } if ($args === NULL) { $args = $view->args; } // Grab the title from the highest argument we got. If there is no such // title, track back until we find a title. if (is_array($args)) { $rargs = array_reverse(array_keys($args)); foreach ($rargs as $arg_id) { if ($title = $view->argument[$arg_id]['title']) { break; } } } if (!$title && ($context == 'menu' || $context == 'page')) { $title = $view->page_title; } if (!$title && $context == 'block') { $title = $view->block_title; } if (!$view->argument) { return $title; } views_load_cache(); $arginfo = _views_get_arguments(); foreach ($view->argument as $i => $arg) { if (!isset($args[$i])) { break; } $argtype = $arg['type']; if ($arg['wildcard'] == $args[$i] && $arg['wildcard_substitution'] != '') { $title = str_replace("%" . ($i + 1), $arg['wildcard_substitution'], $title); } else if (function_exists($arginfo[$argtype]['handler'])) { // call the handler $rep = $arginfo[$argtype]['handler']('title', $args[$i], $argtype); $title = str_replace("%" . ($i + 1), $rep, $title); } } return $title; } /** * Determine whether or not a view is cacheable. A view is not cacheable if * there is some kind of user input or data required. For example, views * that need to restrict to the 'current' user, or any views that require * arguments or allow click-sorting are not cacheable. */ function _views_is_cacheable(&$view) { // views with arguments are immediately not cacheable. if (!empty($view->argument) || !empty($view->exposed_filter) || !empty($view->no_cache)) { return false; } $filters = _views_get_filters(); foreach ($view->filter as $i => $filter) { if ($filters[$filter['field']]['cacheable'] == 'no') { return false; } } foreach ($view->field as $i => $field) { if ($field['sortable']) { return false; } } return true; } /** * Invalidate the views cache, forcing a rebuild on the next grab of table data. */ function views_invalidate_cache() { cache_clear_all('views_', 'cache', true); } // --------------------------------------------------------------------------- // Database functions /** * 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'); } /** * Delete a view from the database. */ function _views_delete_view($view) { $view->vid = intval($view->vid); if (!$view->vid) { return; } db_query("DELETE FROM {view_view} where vid=%d", $view->vid); 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); } /** * Load a view from the database -- public version of the function. */ function views_load_view($arg) { return _views_load_view($arg); } /** * Load a view from the database. * (deprecated; use views_load_view in favor of this function). */ function _views_load_view($arg) { static $cache = array(); $which = is_numeric($arg) ? 'vid' : 'name'; if (isset($cache[$which][$arg])) { return $cache[$which][$arg]; } $where = (is_numeric($arg) ? "v.vid = %d" : "v.name = '%s'"); $view = db_fetch_object(db_query("SELECT v.* FROM {view_view} v WHERE $where", $arg)); if (!$view->name) { return NULL; } $view->access = ($view->access ? explode(', ', $view->access) : array()); // load the sorting criteria too. $result = db_query("SELECT * FROM {view_sort} vs WHERE vid = $view->vid ORDER BY position ASC"); $view->sort = array(); while ($sort = db_fetch_array($result)) { if (substr($sort['field'], 0, 2) == 'n.') { $sort['field'] = 'node' . substr($sort['field'], 1); } $sort['id'] = $sort['field']; $view->sort[] = $sort; } $result = db_query("SELECT * FROM {view_argument} WHERE vid = $view->vid ORDER BY position ASC"); $view->argument = array(); while ($arg = db_fetch_array($result)) { $arg['id'] = $arg['type']; $view->argument[] = $arg; } $result = db_query("SELECT * FROM {view_tablefield} WHERE vid = $view->vid ORDER BY position ASC"); $view->field = array(); while ($arg = db_fetch_array($result)) { if ($arg['tablename'] == 'n') { $arg['tablename'] = 'node'; } $arg['id'] = $arg['fullname'] = "$arg[tablename].$arg[field]"; $arg['queryname'] = "$arg[tablename]_$arg[field]"; $view->field[] = $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)) { if (substr($filter['field'], 0, 2) == 'n.') { $filter['field'] = 'node' . substr($filter['field'], 1); } if ($filter['operator'] == 'AND' || $filter['operator'] == 'OR' || $filter['operator'] == 'NOR' || $filters[$filter['field']]['value-type'] == 'array' ) { if ($filter['value'] !== NULL && $filter['value'] !== '') { $filter['value'] = explode(',', $filter['value']); } else { $filter['value'] = array(); } } $filter['id'] = $filter['field']; $view->filter[] = $filter; } $result = db_query("SELECT * FROM {view_exposed_filter} WHERE vid = $view->vid ORDER BY position ASC"); $view->exposed_filter = array(); while ($arg = db_fetch_array($result)) { $arg['id'] = $arg['field']; $view->exposed_filter[] = $arg; } $cache['vid'][$view->vid] = $view; $cache['name'][$view->name] = $view; return $view; } /** * Save a view to the database. */ function _views_save_view($view) { _views_check_arrays($view); // cache the query if (_views_is_cacheable($view)) { views_load_query(); $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->access = implode(', ', $view->access); $view->changed = time(); $fields = _views_view_fields(); if ($view->vid) { // update // Prepare the query: foreach ($view as $key => $value) { if (in_array($key, $fields)) { $q[] = db_escape_string($key) ." = '%s'"; $v[] = $value; } } // Update the view in the database: 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'"); } else { // insert // This method really saves on typos, and makes it a lot easier to add fields // later on. $view->vid = db_next_id('{view_view}_vid'); // Prepare the query: foreach ($view as $key => $value) { if (in_array((string) $key, $fields)) { $k[] = db_escape_string($key); $v[] = $value; $s[] = is_numeric($value) ? '%d' : "'%s'"; } } db_query("INSERT INTO {view_view} (" . implode(", ", $k) . ") VALUES (" . implode(", ", $s) . ")", $v); } foreach ($view->sort as $i => $sort) { db_query("INSERT INTO {view_sort} (vid, position, field, sortorder, options) VALUES (%d, %d, '%s', '%s', '%s')", $view->vid, $i, $sort['field'], $sort['sortorder'], $sort['options']); } foreach ($view->argument as $i => $arg) { db_query("INSERT INTO {view_argument} (vid, type, argdefault, title, options, position, wildcard, wildcard_substitution) VALUES (%d, '%s', %d, '%s', '%s', %d, '%s', '%s')", $view->vid, $arg['type'], $arg['argdefault'], $arg['title'], $arg['options'], $i, $arg['wildcard'], $arg['wildcard_substitution']); } foreach ($view->field as $i => $arg) { db_query("INSERT INTO {view_tablefield} (vid, tablename, field, label, handler, sortable, defaultsort, options, position) VALUES (%d, '%s', '%s', '%s', '%s', %d, '%s', '%s', %d)", $view->vid, $arg['tablename'], $arg['field'], $arg['label'], $arg['handler'], $arg['sortable'], $arg['defaultsort'], $arg['options'], $i); } foreach ($view->filter as $i => $arg) { if (is_array($arg['value'])) { $arg['value'] = implode(',', $arg['value']); } db_query("INSERT INTO {view_filter} (vid, tablename, field, value, operator, options, position) VALUES (%d, '%s', '%s', '%s', '%s', '%s', %d)", $view->vid, $arg['tablename'], $arg['field'], $arg['value'], $arg['operator'], $arg['options'], $i); } 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); } } // --------------------------------------------------------------------------- // Helper functions to build views and view data /** * Helper function to make table creation a little easier. It adds the necessary * data to a $table array and returns it. */ function views_new_table($table_name, $provider, $left_table, $left_field, $right_field, $extra = NULL) { $table['name'] = $table_name; $table['provider'] = $provider; $table['join']['left']['table'] = $left_table; $table['join']['left']['field'] = $left_field; $table['join']['right']['field'] = $right_field; if ($extra) { $table['join']['extra'] = $extra; } return $table; } /** * Helper function to make table creation a little easier. It adds the necessary * data to the $table array. */ function views_table_add_field(&$table, $name, $label, $help, $others = array()) { views_table_add_data($table, 'fields', $name, $label, $help, $others); } /** * Helper function to make table creation a little easier. It adds the necessary * data to the $table array. */ function views_table_add_filter(&$table, $name, $label, $help, $others = array()) { views_table_add_data($table, 'filters', $name, $label, $help, $others); } /** * Helper function to make table creation a little easier. It adds the necessary * data to the $table array. */ function views_table_add_sort(&$table, $name, $label, $help, $others = array()) { views_table_add_data($table, 'sorts', $name, $label, $help, $others); } /** * Helper function to make table creation a little easier. It adds the necessary * data to the $table array. */ function views_table_add_data(&$table, $type, $name, $label, $help, $others = array()) { $table[$type][$name]['name'] = $label; $table[$type][$name]['help'] = $help; foreach ($others as $key => $value) { $table[$type][$name][$key] = $value; } } /** * Create a blank view. */ function views_create_view($name, $description, $access = array()) { $view = new stdClass(); _views_check_arrays($view); $view->name = $name; $view->description = $description; $view->access = $access; // ensure some things are numerically 0. $view->nodes_per_page = 0; $view->nodes_per_block = 0; return $view; } /** * Add page info to a view. */ function views_view_add_page(&$view, $title, $url, $type, $pager, $nodes_per_page, $header, $header_format, $breadcrumb_no_home = FALSE) { $view->page = TRUE; $view->page_title = $title; $view->url = $url; $view->page_type = $type; $view->use_pager = $pager; $view->nodes_per_page = $nodes_per_page; $view->page_header = $header; $view->page_header_format = $header_format; $view->breadcrumb_no_home = $breadcrumb_no_home; } /** * Add menu info to a view. */ function views_view_add_menu(&$view, $title, $tab, $tab_weight, $default_tab) { $view->menu = TRUE; $view->menu_title = $title; $view->menu_tab = $tab; $view->menu_tab_weight = $tab_weight; $view->menu_tab_default = $default_tab; } /** * Add block info to a view. */ function views_view_add_block(&$view, $title, $type, $nodes_per_block, $more, $use_page_header, $header = '', $header_format = 0) { $view->block = TRUE; $view->block_title = $title; $view->block_type = $type; $view->nodes_per_block = $nodes_per_block; $view->block_more = $more; $view->block_use_page_header = $use_page_header; $view->block_header = $header; $view->block_header_format = $header_format; } /** * Add field info to a view. */ function views_view_add_field(&$view, $table, $field, $label, $sortable = FALSE, $default_sort = 0, $handler = '') { $view->field[] = array( 'tablename' => $table, 'field' => $field, 'label' => $label, 'sortable' => $sortable, 'defaultsort' => $default_sort, 'handler' => $handler ); } /** * Add argument info to a view. */ function views_view_add_argument(&$view, $type, $default, $title, $option = '') { $view->argument[] = array( 'type' => $type, 'argdefault' => $default, 'title' => $title, 'options' => $option, ); } /** * Add filter info to a view. */ function views_view_add_filter(&$view, $table, $field, $operator, $value, $option) { $view->filter[] = array( 'tablename' => $table, 'field' => $field, 'operator' => $operator, 'value' => $value, 'options' => $option, ); } /** * Add exposed_filter info to a view. */ function views_view_add_exposed_filter(&$view, $table, $field, $optional, $is_default, $lock_operator, $single) { $view->exposed_filter[] = array( 'tablename' => $table, 'field' => $field, 'optional' => $optional, 'is_default' => $is_default, 'operator' => $lock_operator, 'single' => $single ); } /** * Add sort info to a view. */ function views_view_add_sort(&$view, $table, $field, $order, $option) { $view->sort[] = array( 'tablename' => $table, 'field' => $field, 'sortorder' => $order, 'options' => $option ); } // --------------------------------------------------------------------------- // Themeable and support for themeables. /** * Themeable function to handle displaying a specific field. */ function theme_views_handle_field($fields, $field, $data) { $info = $fields[$field['fullname']]; if ($field['handler'] && function_exists($field['handler'])) { return $field['handler']($info, $field, $data->$field['queryname'], $data); } if ($info['handler'] && is_string($info['handler']) && function_exists($info['handler'])) { return $info['handler']($info, $field, $data->$field['queryname'], $data); } return check_plain($data->$field['queryname']); } /** * Construct a header for a table view. */ function _views_construct_header($view, $fields) { foreach ($view->field as $field) { $header = array(); $info = $fields[$field['fullname']]; 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']; } } 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'] = views_css_safe('view-field-'. $field['queryname']); $header['class'] = "view-cell-header"; $headers[] = $header; } return $headers; } function theme_views_display_filters($view) { return drupal_get_form("views_filters", $view); } function views_filters($view) { $filters = _views_get_filters(); foreach ($view->exposed_filter as $count => $expose) { $id = $expose['id']; $filterinfo = $filters[$id]; foreach ($view->filter as $filter) { if ($filter['id'] == $id) { break; } } // set up the operator widget. if (!$expose['operator']) { // 'operator' is either an array or a handler $operator = $filterinfo['operator']; if (!is_array($operator) && function_exists($filterinfo['operator'])) { $operator = $filterinfo['operator']('operator', $filterinfo); } $form["op$count"] = array( '#name' => "op$count", // get rid of edit[] array. '#type' => 'select', '#default_value' => $filter['operator'], '#options' => $operator, ); if (array_key_exists("op$count", $_GET)) { $form["op$count"]["#default_value"] = $_GET["op$count"]; } } // 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); } if (!$expose['optional'] || $expose['is_default']) { $item['#default_value'] = $filter['value']; } if ($expose['single']) { unset($item['#multiple']); } if ($expose['optional'] && is_array($item['#options'])) { $item['#options'] = array('**ALL**' => t('')) + $item['#options']; } if ($item['#multiple'] && is_array($item['#options'])) { $item['#size'] = min(count($item['#options']), 8); } if (array_key_exists("filter$count", $_GET)) { $item["#default_value"] = $_GET["filter$count"]; } $form["filter$count"] = $item; } $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_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[] = drupal_render($form["op$count"]) . drupal_render($form["filter$count"]); $label[] = $expose['label']; } $row[] = drupal_render($form['submit']); $label[] = ''; // so the column count is the same. // make the 'q' come first return drupal_render($form['q']) . theme('table', $label, array($row)) . drupal_render($form); } /** * Display the nodes of a view as a list. */ function theme_views_view_list($view, $nodes, $type) { $fields = _views_get_fields(); foreach ($nodes as $node) { $item = ''; foreach ($view->field as $field) { if ($fields[$field['id']]['visible'] !== FALSE) { if ($field['label']) { $item .= "
" . $field['label'] . "
"; } $item .= "
" . views_theme_field('views_handle_field', $field['queryname'], $fields, $field, $node, $view) . "
"; } } $items[] = "
name) ."'>$item
\n"; // l($node->title, "node/$node->nid"); } if ($items) { return theme('item_list', $items); } } /** * Display the nodes of a view as a table. */ function theme_views_view_table($view, $nodes, $type) { $fields = _views_get_fields(); foreach ($nodes as $node) { $row = array(); 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 ". views_css_safe('view-field-'. $field['queryname']); $row[] = $cell; } } $rows[] = $row; } return theme('table', $view->table_header, $rows); } /** * Display the nodes of a view as teasers. */ function theme_views_view_teasers($view, $nodes, $type) { return views_theme('views_view_nodes', $view, $nodes, $type, true); } /** * Display the nodes of a view as plain nodes. */ function theme_views_view_nodes($view, $nodes, $type, $teasers = false, $links = true) { foreach ($nodes as $n) { $node = node_load($n->nid); $output .= node_view($node, $teasers, false, $links); } return $output; } function views_set_breadcrumb($view) { $breadcrumb = drupal_get_breadcrumb(); if ($view->breadcrumb_no_home) { array_shift($breadcrumb); } if ($view->args) { // Add a breadcrumb trail for each level of argument we're at. $url = $view->url; $args = array(); $where = 1; foreach ($view->args as $level => $arg) { if ($view->argument[$level]['argdefault'] != 1) { $breadcrumb[] = l(views_get_title($view, 'page', $args), $url); // For next round. } $args[] = $arg; if ($where && $where = strpos('$arg', $url)) { $url = substr_replace($url, $arg, $where, 4); } else { $url .= "/$arg"; } } } drupal_set_breadcrumb($breadcrumb); } function views_get_textarea($view, $type, $textarea) { $use_page = "block_use_page_$textarea"; $var = ($type != 'block' || $view->$use_page ? 'page_' : 'block_') . $textarea; $format = $var . '_format'; if ($view->$var) { return "
name) ."'>" . check_markup($view->$var, $view->$format, false) . "
\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')); views_set_breadcrumb($view); } if ($num_nodes) { $output .= views_get_textarea($view, $type, 'header'); } if ($type != 'block' && $view->exposed_filter) { $output .= views_theme('views_display_filters', $view); } $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 .= "
name) ."'>". views_theme($plugins[$view_type]['summary_theme'], $view, $type, $level, $nodes, $args) . '
'; } else { $output .= "
name) ."'>". views_theme($plugins[$view_type]['theme'], $view, $nodes, $type) . '
'; } $output .= views_get_textarea($view, $type, 'footer'); if ($type == 'block' && $view->block_more && $num_nodes >= $view->nodes_per_block) { $output .= theme('views_more', $view->real_url); } } else { $output .= views_get_textarea($view, $type, 'empty'); } if ($view->use_pager) { $output .= theme('pager', '', $view->pager_limit, $view->use_pager - 1); } if ($output) { $output = "
name) ."'>$output
\n"; } return $output; } /** * Format the 'more' link for a view. Personally I prefer [More] but I've * been convinced to go with simply 'more'. */ function theme_views_more($url) { return ""; } /** * Get the summary link for a view. */ function views_get_summary_link($argtype, $item, $base) { $arginfo = _views_get_arguments(); return $arginfo[$argtype]['handler']('link', $item, $argtype, $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) { foreach ($nodes as $node) { $items[] = views_get_summary_link($view->argument[$level]['type'], $node, $view->real_url) . " (" . $node->num_nodes . ")"; } if ($items) { $output .= theme('item_list', $items); } return $output; } // --------------------------------------------------------------------------- // Generic handlers. These make sense to be used in a lot of different places. /** * Field handlers accept the following arguments: * @param $fieldinfo * The array of info for that field from the global tables array. * @param $fielddata * All of the info about that field in the database. * @param $value * The value of the field fetched from the database. * @param $data * The rest of the data about the node fetched from the database, in case * the handler needs more than just the field. */ /** * Format a date. */ function views_handler_field_date($fieldinfo, $fielddata, $value, $data) { 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(); } /** * 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(); } /** * 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(); } /** * 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(); } function theme_views_nodate() { return '' . t('never') . ''; } /** * Provide a list of all standard supproted date output handlers. */ function views_handler_field_dates() { 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'), 'views_handler_field_date_custom' => t('As Custom Date'), 'views_handler_field_since' => t('As Time Ago') ); } 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) { 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%m')"; 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')"; break; case 'month': $field = "DATE_FORMAT(FROM_UNIXTIME($sortinfo[table].$sortinfo[field]), '%Y%m%)"; break; case 'year': $field = "DATE_FORMAT(FROM_UNIXTIME($sortinfo[table].$sortinfo[field]), '%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']); } 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. */ function views_handler_field_int($fieldinfo, $fielddata, $value, $data) { return intval($value); } /** * Argument handlers take up to 4 fields, which vary based upon the operation. * @param $op * The operation to perform: * 'summary': A summary view is being constructed. In this case the handler * is to add the necessary components to the query to display * the summary. It must return a $fieldinfo array with 'field' * set to the field the summary is ordered by; if this is aliased * for some reason (such as being an aggregate field) set 'fieldname' * to the alias. * 'sort': Set up the view to sort based upon the setting in $a2. * 'filter': Filter the view based upon the argument sent; essentially just * add the where clause here. * 'link': Provide a link from a summary view based upon the argument sent. * 'title': Provide the title of a view for substitution. * @param &$query * For summary, filter and link, this is the actual query object; for title this is * simply the value of the argument. * @param $a2 * For summary, this is the type of the argument. For the others, this is the info * for the argument from the global table. (Why is this not consistent? I dunno). * @param $a3 * For summary, this is the 'options' field from the db. For 'filter' this is * the argument received. For 'link' this is the base URL of the link. Not used * for 'title'. * */ // --------------------------------------------------------------------------- // Filter handlers /** * There are two kinds of filter handlers here; the easy kind simply creates an * array of options. For example, for taxonomy we provide a list of all taxonomy * terms which is placed in the select box. * * The other type is the 'custom' handler which is used to create a customized * WHERE clause for specialized filters. * * It takes 4 parameters. * @param $op * At this time it will always be 'handler'. * @param $filter * Information on the filter from the database, including 'options', 'value' and 'operator'. * @param $filterinfo * Information on the filter from the global table array. * @param &$query * The query object being worked on. */ /** * A list of and/or/nor. */ function views_handler_operator_andor() { return array('AND' => t('Is All Of'), 'OR' => t('Is One Of'), 'NOR' => t('Is None Of')); } /** * A list of or/nor. */ function views_handler_operator_or() { return array('OR' => t('Is One Of'), 'NOR' => t('Is None Of')); } /** * A list of equal or not equal to. */ function views_handler_operator_eqneq() { return array('=' => t('Is Equal To'), '!=' => t('Is Not Equal To')); } /** * A list of greater / equal / less than */ function views_handler_operator_gtlt() { return array('>' => t("Is Greater Than"), '>=' => t("Is Greater Than Or Equals"), '=' => t("Is Equal To"), '!=' => t("Is Not Equal To"), '<=' => t("Is Less Than Or Equals"), '<' => t("Is Less Than")); } /** * A list of yes/no. */ 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() */ function views_views_style_plugins() { return array( 'list' => array( 'name' => t('List View'), 'theme' => 'views_view_list', 'validate' => 'views_ui_plugin_validate_list', 'needs_fields' => true, 'weight' => -10, ), 'table' => array( 'name' => t('Table View'), 'theme' => 'views_view_table', '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, ), ); } /** * A list of options to be used in LIKE queries */ function views_handler_operator_like() { 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')); } /** * Custom filter for LIKE operations */ function views_handler_filter_like($op, $filter, $filterinfo, &$query) { switch (trim($filter['value'])) { case (''): return; break; } switch ($op) { case 'handler': $table = $filterinfo['table']; $column = $filterinfo['field']; $field = "$table.$column"; $query->ensure_table($table); switch ($filter['operator']) { case 'contains': $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']); break; case 'ends': $query->add_where("UPPER(%s) LIKE UPPER('%%%s')", $field, $filter['value']); break; case 'not': $query->add_where("UPPER(%s) NOT LIKE UPPER('%%%s%%')", $field, $filter['value']); break; case '=': $query->add_where("UPPER(%s) = UPPER('%s')", $field, $filter['value']); break; } break; } } /** * Format a field as file size. */ function views_handler_field_filesize($fieldinfo, $fielddata, $value, $data) { return format_size($value); } /** * Handle a timestamp filter. */ function views_handler_filter_timestamp($op, $filter, $filterinfo, &$query) { $value = $filter['value'] == 'now' ? "***CURRENT_TIME***" : strtotime($filter['value']); $table = $filterinfo['table']; $column = $filterinfo['field']; $field = "$table.$column"; if ($filterinfo['from_unixtime']) { $field = "from_UNIXTIME($field)"; } $query->ensure_table($table); $query->add_where("%s %s %s + %d", $field, $filter['operator'], $value, $filter['options']); } /** * Provide a form gadget for dates. */ function views_handler_filter_date_value_form() { return array( '#type' => 'textfield', '#attributes' => array('class' => 'jscalendar'), ); } /** * Substitute current time; this works with cached queries. */ function views_views_query_substitutions($view) { global $user; return array('***CURRENT_TIME***' => time()); } /** * Returns a themed view. * @param $view_name * The name of the view. * @param $limit * 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 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 * a page uses drupal_set_title to change the page title. * '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 = 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); } }