diff --git a/CHANGELOG.txt b/CHANGELOG.txt index a5a40742d601595ced384d2bbb297979cd74ac64..55b9d5b15473472ba57c348a88b52830e5362981 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -3,7 +3,7 @@ CHANGELOG for Views for Drupal 5 Views 1.0 o Initial release under drupal.org's new release system -Views 1.1-dev +Views 5.x-1.1-beta-1 Bugs fixed: o 97228: Fatal error on the views help page. @@ -34,3 +34,33 @@ Views 1.1-dev o 97290: new $offset argument to views_build_view to allow views to to skip the first N nodes [doesn't work if $ pager = true]. o 97290: New options for granularity on date sorts + o Provide a proper 'action' for exposed filters so they always go to the + view, not just the current page. + o 76601: New 'all files' field to display all files on a node as a single + field + +Views 5.x-1.2-beta-1 + + Bugs fixed: + o 100394: Fix to t() call in 'new comments'. + o 100164: alias prefixing fix. + o 100109: Incorrect caching of 'is new' filter. + o 99849: DISTINCT could break summary views. + o 99783: Comment count didn't take into account pages. Also moved handler + out of node where it didn't belong. + o Use drupal_add_feed() for RSS icons so they can be themed. + o 99323: fix link in argument handling help + o Temporary fix for cache not being loaded for menu item creation + o 100317: exported view gets improperly formed value fields on filters + o 98441: Fixed 'optional' setting on exposed filters. + o 100997: Clarified use of $arg in help text + o 100769: Getting book parents didn't require parents to be current + o 98492: Recent comments block needed to filter out moderated comments + + New features: + o 99225: CSS generator for views theme wizard + o 88067: theme_view much more useful now. + o 98492: Comment moderation status filter + o 101025: Sort by node type + +Views --dev diff --git a/modules/views_book.inc b/modules/views_book.inc index 241381647e5222295f6d5acd525c697b29d1e298..9cffb85363c34aeacc5e4398d04693033acd1c9b 100644 --- a/modules/views_book.inc +++ b/modules/views_book.inc @@ -128,7 +128,7 @@ function views_handler_field_book_parent_title_nl($fieldinfo, $fielddata, $value function views_handler_filter_book_parent_zero() { $parents = array(); - $result = db_query("SELECT DISTINCT parent FROM {book} ORDER BY parent"); + $result = db_query("SELECT DISTINCT parent FROM {book} INNER JOIN {node} n ON book.vid = n.vid ORDER BY parent"); while ($obj = db_fetch_object($result)) { $parents[$obj->parent] = "$obj->parent"; } diff --git a/modules/views_comment.inc b/modules/views_comment.inc index cba1c1fd7ef95a17ab503e62012027f0e2219e83..c1c513feed4adeaff341958f8c2fece8136ce4f0 100644 --- a/modules/views_comment.inc +++ b/modules/views_comment.inc @@ -62,6 +62,14 @@ function comment_views_tables() { 'help' => t('This will display the author of the node.'), ), ), + 'filters' => array( + 'status' => array( + 'name' => t('Comment: Pending approval'), + 'operator' => array('=' => t('Equals')), + 'list' => 'views_handler_operator_yesno', + 'list-type' => 'select', + ), + ), 'sorts' => array( 'timestamp' => array( 'name' => t('Comment: Created Time'), @@ -177,6 +185,18 @@ function views_handler_field_commentlink($fieldinfo, $fielddata, $value, $data) return l($value, "node/$data->nid", NULL, NULL, "comment-$data->comments_cid"); } +/* + * Format a field as a number of comments, plus the number of unread comments. + */ +function views_handler_comments_with_new($fieldinfo, $fielddata, $value, $data) { + $comments = intval($value); + if ($comments && $new = comment_num_new($data->nid)) { + $comments .= '
'; + $comments .= l(t('@num new', array('@num' => $new)), "node/$data->nid", NULL, comment_page_new_query($data->nid), 'new'); + } + return $comments; +} + /* * Format a field as a link to a 'mark', stating whether or not the comment has * updated since it was last viewed by the user. @@ -264,6 +284,13 @@ function comment_views_default_views() { 'options' => '', 'value' => '1', ), + array ( + 'tablename' => 'comments', + 'field' => 'status', + 'operator' => '=', + 'options' => '', + 'value' => '0', + ), ); $view->exposed_filter = array ( ); diff --git a/modules/views_node.inc b/modules/views_node.inc index 91f30aa15fd571d5469bdd8e2f1e746b27c7d3ef..9ca494adc7fd0be690dac06e9bb6f50c0025ef5d 100644 --- a/modules/views_node.inc +++ b/modules/views_node.inc @@ -119,6 +119,10 @@ function node_views_tables() { 'name' => t('Node: Title'), 'help' => t('Sort by the node title, alphabetically'), ), + 'type' => array( + 'name' => t('Node: Type'), + 'help' => t('Sort by the node type, alphabetically'), + ), 'random' => array( 'name' => t('Random'), 'handler' => 'views_handler_sort_random', @@ -416,18 +420,6 @@ function views_handler_nodetype($fieldinfo, $fielddata, $value, $data) { return node_get_types('name', $value); } -/* - * Format a field as a number of comments, plus the number of unread comments. - */ -function views_handler_comments_with_new($fieldinfo, $fielddata, $value, $data) { - $comments = intval($value); - if (module_exists('comment') && $comments && $new = comment_num_new($data->nid)) { - $comments .= '
'; - $comments .= l(t('%num new', array('%num' => $new)), "node/$data->nid", NULL, NULL, 'new'); - } - return $comments; -} - /* * Format a field as the Body of a node. */ @@ -684,19 +676,19 @@ function views_handler_filter_isnew($op, $filter, $filterinfo, &$query) { // Hey, Drupal kills old history, so nodes that haven't been updated // since NODE_NEW_LIMIT are bzzzzzzzt outta here! - $limit = NODE_NEW_LIMIT; + $limit = time() - NODE_NEW_LIMIT; $query->ensure_table('history'); if (module_exists('comment')) { $query->ensure_table('node_comment_statistics'); - $clause = "OR node_comment_statistics.last_comment_timestamp > $limit"; + $clause = ("OR node_comment_statistics.last_comment_timestamp > (***CURRENT_TIME*** - $limit)"); $clause2 = "OR history.timestamp < node_comment_statistics.last_comment_timestamp"; } // NULL means a history record doesn't exist. That's clearly new content. // Unless it's very very old content. Everything in the query is already // type safe cause none of it is coming from outside here. - $query->add_where("(history.timestamp IS NULL AND (node.changed > $limit $clause)) OR history.timestamp < node.changed $clause2"); + $query->add_where("(history.timestamp IS NULL AND (node.changed > (***CURRENT_TIME***-$limit) $clause)) OR history.timestamp < node.changed $clause2"); } /* @@ -751,10 +743,10 @@ function views_handler_sort_random($action, &$query, $sortinfo, $sort) { switch ($GLOBALS['db_type']) { case 'mysql': case 'mysqli': - $query->add_orderby('', "rand()", "ASC"); + $query->add_orderby('', "rand()", "ASC", 'random_sort'); break; case 'pgsql': - $query->add_orderby('', "random()", "ASC"); + $query->add_orderby('', "random()", "ASC", 'random_sort'); break; } } diff --git a/views.info b/views.info index cc481889be1e54601ddb947084d85be41804d7b9..c2910591235a36abb015a730c0691895c5459481 100644 --- a/views.info +++ b/views.info @@ -1,6 +1,6 @@ ; $Id$ name = Views description = The views module creates customized views of node lists. -version = 1.1 +version = $Name$ package = Views diff --git a/views.module b/views.module index 2a2db6966b1cd2c4849fc757a012648f17de0526..40835671dbe251d8e83445b53fdfcad9bcf953bf 100644 --- a/views.module +++ b/views.module @@ -97,6 +97,7 @@ function views_menu($may_cache) { * 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()) { + views_load_cache(); static $roles = NULL; if ($roles == NULL) { global $user; @@ -1157,13 +1158,14 @@ function views_filters($view) { } $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' => $_GET['q'], + '#value' => $view->real_url ? $view->real_url : $view->url, '#name' => 'q', ); } @@ -1609,6 +1611,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() */ @@ -1683,19 +1701,20 @@ function views_handler_filter_like($op, $filter, $filterinfo, &$query) { $words = trim($match[2], ',?!();:-'); $words = $phrase ? array($words) : preg_split('/ /', $words, -1, PREG_SPLIT_NO_EMPTY); foreach ($words as $word) { - $q[] = "UPPER(%s) LIKE UPPER('%%%s%%')"; - $v[] = $field; - $v[] = trim($word, " ,!?"); + $where[] = "UPPER(%s) LIKE UPPER('%%%s%%')"; + $values[] = $field; + $values[] = trim($word, " ,!?"); } } if ($filter['operator'] == 'word') { - $q = '('. implode(' OR ', $q) .')'; + $where = '('. implode(' OR ', $where) .')'; } else { - $q = implode(' AND ', $q); + $where = implode(' AND ', $where); } - array_unshift($v, $q); - call_user_func_array(array($query, 'add_where'), $v); + // 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%%')", @@ -1763,14 +1782,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 @@ -1778,9 +1798,15 @@ 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); } } + diff --git a/views_query.inc b/views_query.inc index c2be66cc55ccf7ffd07510b044b7987383587d23..617a76edcc5f29c5118dc95dee8903035e38bcde 100644 --- a/views_query.inc +++ b/views_query.inc @@ -101,7 +101,7 @@ function _views_view_build_filters(&$query, &$view) { } if ($expose['optional']) { - if (!isset($_GET["filter$count"]) && !$expose['is_default']) { + if ((!isset($_GET["filter$count"]) || $_GET["filter$count"] == '') && !$expose['is_default']) { continue 2; // skip } if ($_GET["filter$count"] == '**ALL**' || @@ -110,7 +110,7 @@ function _views_view_build_filters(&$query, &$view) { continue 2; // skip this filter entirely. } } - if (isset($_GET["filter$count"])) { + if (isset($_GET["filter$count"]) && $_GET["filter$count"] != '') { $value = $_GET["filter$count"]; if ($filterinfo['value-type'] == 'array' && !is_array($value)) { $value = array($value); @@ -249,7 +249,7 @@ function _views_build_summary(&$query, $argtype, $option, $self_sort) { if (!function_exists($arginfo[$argtype]['handler'])) { return false; } - + $primary_field = $query->fields[0]; $query->clear_fields(); $fieldinfo = $arginfo[$argtype]['handler']('summary', $query, $argtype, $option); @@ -257,7 +257,7 @@ function _views_build_summary(&$query, $argtype, $option, $self_sort) { if ($fieldinfo['fieldname']) { $query->add_field($fieldinfo[field], '', $fieldinfo[fieldname]); } - $query->add_field('count(node.nid)', '', 'num_nodes'); + $query->add_field("count($primary_field)", '', 'num_nodes'); $query->add_groupby($fieldinfo['field']); $query->set_count_field("DISTINCT($fieldinfo[field])"); if ($self_sort) { @@ -362,6 +362,8 @@ class _views_query { * Add multiple an orderby's. Right now I'm not sure why this function * is separate from add_orderby above; I don't think it needs to * be. + * + * NOTE: $alias doesn't work when adding multiple orderbys. */ function add_orderby($table, $field, $order, $alias = '') { if (!$alias && $table) { @@ -380,7 +382,14 @@ class _views_query { } foreach($field as $f) { - $as = $alias . '_' . $f; + // Only fill out this aliasing if there is a table; + // otherwise we assume it is a formula. + if ($table) { + $as = $alias . '_' . $f; + } + else { + $as = $alias; + } $this->add_field($f, $table, $as); $this->orderby[] = "$as $order"; } @@ -443,7 +452,7 @@ class _views_query { if ($joininfo) { $this->joins[$table][$i] = $joininfo; } - $this->tablequeue[] = array('table' => $table, 'num' => $i); + $this->tablequeue[] = array('table' => $table, 'num' => $i, 'alias_prefix' => $this->use_alias_prefix); } return $this->tables[$table]; } @@ -522,7 +531,10 @@ class _views_query { * @param $alias_prefix * An optional prefix for the table alias. */ - function get_table_name($table, $table_num, $alias_prefix = '') { + function get_table_name($table, $table_num, $alias_prefix = null) { + if (is_null($alias_prefix)) { + $alias_prefix = $this->use_alias_prefix; + } return ($table_num < 2 ? $alias_prefix . $table : $alias_prefix . $table . $table_num); } diff --git a/views_rss.info b/views_rss.info index c70babbab82bf80cd7a8019ffb6724fda95af294..e891f2433336f878c84b0aac74b8450dc4a61955 100644 --- a/views_rss.info +++ b/views_rss.info @@ -1,6 +1,6 @@ ; $Id$ name = Views RSS description = RSS plugin for the views feed selector argument. -version = 1.1 +version = $Name$ package = Views dependencies = views diff --git a/views_rss.module b/views_rss.module index ea9c8ae15f7cb2f77d793d63507bd148a01615e1..7f62e8a6639522bedf5608620af1bae4466022af 100644 --- a/views_rss.module +++ b/views_rss.module @@ -82,13 +82,7 @@ function views_rss_views_feed_argument($op, &$view, $arg) { $filters = drupal_query_string_encode($view->used_filters); } - drupal_add_link(array('rel' => 'alternate', - 'type' => 'application/rss+xml', - 'title' => check_plain($title), - 'href' => url($url, $filters, NULL, TRUE))); - if ($view->build_type != 'block') { - return theme('feed_icon', url($url, $filters)); - } + drupal_add_feed($title, $filters); } } diff --git a/views_theme_wizard.info b/views_theme_wizard.info index f7567625b722e64d123aefe1289248f4538611a7..5f896866939de391f0037fbcf300c487fedbb636 100644 --- a/views_theme_wizard.info +++ b/views_theme_wizard.info @@ -1,6 +1,6 @@ ; $Id$ name = Views Theme Wizard description = The views theme wizard helps create stub theming for views. -version = 1.1 +version = $Name$ package = Views dependencies = views diff --git a/views_theme_wizard.module b/views_theme_wizard.module index a730ddf25aa2dce736c4a8eade0463c4089591db..4f1db2a1644570d407a3df7759e404088ec52de8 100644 --- a/views_theme_wizard.module +++ b/views_theme_wizard.module @@ -55,6 +55,12 @@ function views_theme_wizard_form($views) { '#type' => 'markup', '#value' => '', ); + + $form['code3'] = array( + '#type' => 'markup', + '#value' => '', + ); + $form['submit'] = array( '#type' => 'button', @@ -90,6 +96,11 @@ function views_theme_wizard_generate($form, $form_values) { $form['code2']['#value'] = views_theme_wizard_generate_list_template_code($view); $form['code2']['#title'] = t('This code goes in a file named views-list-%s.tpl.php', array('%s' => $view->name)); $form['code2']['#rows'] = 20; + + $form['code3']['#type'] = 'textarea'; + $form['code3']['#value'] = views_theme_wizard_generate_list_stylesheet_code($view); + $form['code3']['#title'] = t('This code goes in a file named views-list-%s.css', array('%s' => $view->name)); + $form['code3']['#rows'] = 20; } if ($op == t('List Theme Fields')) { @@ -102,6 +113,7 @@ function views_theme_wizard_generate($form, $form_values) { $form['code2']['#value'] = views_theme_wizard_example_field($view); $form['code2']['#title'] = t('This is a basic theme function', array('%s' => $view->name)); $form['code2']['#rows'] = 20; + } return $form; } @@ -202,7 +214,9 @@ EOT; * generate a template file for a list theme */ function views_theme_wizard_generate_list_template_code($view) { - $header = <<name}.tpl.php file */ -?> + + + //now we add the stylesheet... + drupal_add_css(path_to_theme() .'/views-list-{$view->name}.css'); + + ?> EOT; @@ -259,3 +277,55 @@ EOT; return $header .$output; } +/** + * generate a stylesheet file for a list theme + */ +function views_theme_wizard_generate_list_stylesheet_code($view) { + $header = <<name + * + * The class selectors are filled with a single comment line. + * You should complete each selector according to your liking. +*/ +\n +EOT; + + $fields = _views_get_fields(); + $taken = array(); + + $output .= <<field as $id => $field) { + $field_name = views_css_safe($field['field']); + if (isset($taken[$field_name])) { + $field_name = views_css_safe($field['queryname']); + } + $taken[$field_name] = true; + $output .= << $view->url, '#size' => 60, '#maxlength' => 255, - '#description' => t('Enter the URL to use for this view in the form of \'dir/dir\'. Do not begin or end the URL with a /. Example: \'view/tracker\'. This is required if providing a page view. You can also add $arg as a placeholder for arguments passed in the URL, for example \'user/$arg/tracker\' or \'view/taxonomy/$arg\'.'), + '#description' => t('Enter the URL to use for this view in the form of \'dir/dir\'. Do not begin or end the URL with a /. Example: \'view/tracker\'. This is required if providing a page view. You can also add $arg as a placeholder for arguments passed in the URL, for example \'user/$arg/tracker\' or \'view/taxonomy/$arg\'. Note that any arguments listed here will be required, even if they are listed as optional below. You do not need to list arguments at the end of the path.'), ); $form['page-info']['page_type'] = array( @@ -803,7 +803,6 @@ function views_edit_view($view, $op = '') { '#maxlength' => 5, '#description' => t('The number of nodes to display per page. If 0, all nodes will be displayed. If not using a pager, this will be the maximum number of nodes in the list.'), '#attributes' => NULL, - '#required' => true, ); $form['page-info']['page_header_fieldset'] = array( '#type' => 'fieldset', @@ -1044,7 +1043,7 @@ function views_edit_view($view, $op = '') { '#cols' => 60, '#rows' => 6, '#description' => '

'. t('Advanced Usage Only: PHP code that returns a custom array of arguments for the view. Should not include <?php ?> delimiters.') .'

' . - '

'. t('For more information, please see the Argument Handling Code documentation in the Drupal handbook.', array('%arg' => 'http://drupal.org/node/70145')) .'

', + '

'. t('For more information, please see the Argument Handling Code documentation in the Drupal handbook.', array('%arg' => 'http://drupal.org/node/70145')) .'

', ); } else { $form['view_args_php_fieldset']['view_args_php'] = array( @@ -1872,9 +1871,8 @@ function views_create_view_code($vid) { $output .= " 'tablename' => " . var_export($fieldbits[0], true) . ",\n"; $output .= " 'field' => " . var_export($fieldbits[1], true) . ",\n"; $output .= " 'operator' => " . var_export($filter['operator'], true) . ",\n"; - $value = var_export($filter['value'], true); $output .= " 'options' => " . var_export($filter['options'], true) . ",\n"; - $output .= " 'value' => " . var_export($value, true) . ",\n"; + $output .= " 'value' => " . var_export($filter['value'], true) . ",\n"; $output .= " ),\n"; $requires[$fieldbits[0]] = 1; }