init( $type, $query, $id ); if ( !empty($this->query) ) $this->query(); } function BB_Query( $type = 'topic', $query = '', $id = '' ) { $this->__construct( $type, $query, $id ); } function init( $type = null, $query = null, $id = null ) { if ( !is_null($type) || !isset($this->type) ) $this->type = is_null($type) ? 'topic' : $type; if ( !is_null($query) || !isset($this->query) ) $this->query = $query; if ( !is_null($id) || !isset($this->query_id) ) $this->query_id = $id; $this->query_vars = array(); $this->not_set = array(); unset($this->request); $this->match_query = false; unset($this->results, $this->errors); $this->count = 0; $this->found_rows = false; } function &query() { global $bbdb; if ( $args = func_get_args() ) call_user_func_array( array(&$this, 'init'), $args ); if ( !$this->generate_query() ) return; do_action_ref_array( 'bb_query', array(&$this) ); $key = md5( $this->request ); if ( false === $cached_ids = wp_cache_get( $key, 'bb_query' ) ) { if ( 'post' == $this->type ) { $this->results = bb_cache_posts( $this->request, $this->query_vars['post_id_only'] ); // This always appends meta $_the_id = 'post_id'; $this->query_vars['append_meta'] = false; } else { $this->results = $bbdb->get_results( $this->request ); $_the_id = 'topic_id'; } $cached_ids = array(); if ( is_array($this->results) ) foreach ( $this->results as $object ) $cached_ids[] = $object->$_the_id; wp_cache_set( $key, $cached_ids, 'bb_query' ); } else { if ( 'post' == $this->type ) { $_query_ids = array(); $_cached_posts = array(); foreach ( $cached_ids as $_cached_id ) { if ( false !== $_post = wp_cache_get( $_cached_id, 'bb_post' ) ) { $_cached_posts[$_post->post_id] = $_post; } else { $_query_ids[] = $_cached_id; } } if ( count( $_query_ids ) ) { $_query_ids = join( ',', array_map( 'intval', $_query_ids ) ); $results = $bbdb->get_results( "SELECT * FROM $bbdb->posts WHERE post_id IN($_query_ids)" ); $results = array_merge( $results, $_cached_posts ); } else { $results = $_cached_posts; } $_the_id = 'post_id'; } else { $_query_ids = array(); $_cached_topics = array(); foreach ( $cached_ids as $_cached_id ) { if ( false !== $_topic = wp_cache_get( $_cached_id, 'bb_topic' ) ) { $_cached_topics[$_topic->topic_id] = $_topic; } else { $_query_ids[] = $_cached_id; } } if ( count( $_query_ids ) ) { $_query_ids = join( ',', array_map( 'intval', $_query_ids ) ); $results = $bbdb->get_results( "SELECT * FROM $bbdb->topics WHERE topic_id IN($_query_ids)" ); $results = array_merge( $results, $_cached_topics ); } else { $results = $_cached_topics; } $_the_id = 'topic_id'; } $this->results = array(); $trans = array(); foreach ( $results as $object ) $trans[$object->$_the_id] = $object; foreach ( $cached_ids as $cached_id ) $this->results[] = $trans[$cached_id]; } $this->count = count( $this->results ); if ( false === $this->found_rows && $this->query_vars['count'] ) // handles FOUND_ROWS() or COUNT(*) $this->found_rows = bb_count_last_query( $this->request ); if ( 'post' == $this->type ) { if ( $this->query_vars['append_meta'] ) $this->results = bb_append_meta( $this->results, 'post' ); if ( $this->query_vars['cache_users'] ) bb_post_author_cache( $this->results ); if ( $this->query_vars['cache_topics'] ) bb_cache_post_topics( $this->results ); } else { if ( $this->query_vars['append_meta'] ) $this->results = bb_append_meta( $this->results, 'topic' ); } return $this->results; } function generate_query() { if ( $args = func_get_args() ) call_user_func_array( array(&$this, 'init'), $args ); $this->parse_query(); // Allow filter to abort query if ( false === $this->query_vars ) return false; if ( 'post' == $this->type ) $this->generate_post_sql(); else $this->generate_topic_sql(); return $this->request; } // $defaults = vars to use if not set in GET, POST or allowed // $allowed = array( key_name => value, key_name, key_name, key_name => value ); // key_name => value pairs override anything from defaults, GET, POST // Lone key_names are a whitelist. Only those can be set by defaults, GET, POST // If there are no lone key_names, allow everything but still override with key_name => value pairs // Ex: $allowed = array( 'topic_status' => 0, 'post_status' => 0, 'topic_author', 'started' ); // Will only take topic_author and started values from defaults, GET, POST and will query with topic_status = 0 and post_status = 0 function &query_from_env( $type = 'topic', $defaults = null, $allowed = null, $id = '' ) { $this->init_from_env( $type, $defaults, $allowed, $id ); return $this->query(); } function init_from_env( $type = 'topic', $defaults = null, $allowed = null, $id = '' ) { $vars = $this->fill_query_vars( array() ); $defaults = wp_parse_args($defaults); $get_vars = stripslashes_deep( $_GET ); $post_vars = stripslashes_deep( $_POST ); $allowed = wp_parse_args($allowed); $_allowed = array(); foreach ( array_keys($allowed) as $k ) { if ( is_numeric($k) ) { $_allowed[] = $allowed[$k]; unset($allowed[$k]); } elseif ( !isset($$k) ) { $$k = $allowed[$k]; } } extract($post_vars, EXTR_SKIP); extract($get_vars, EXTR_SKIP); extract($defaults, EXTR_SKIP); $vars = $_allowed ? compact($_allowed, array_keys($allowed)) : compact(array_keys($vars)); $this->init( $type, $vars, $id ); } function fill_query_vars( $array ) { // Should use 0, '' for empty values // Function should return false iff not set // parameters commented out are handled farther down $ints = array( // 'page', // Defaults to global or number in URI // 'per_page', // Defaults to page_topics 'tag_id', // one tag ID 'favorites', // one user ID, 'offset', // first item to query 'number' // number of items to retrieve ); $parse_ints = array( // Both 'post_id', 'topic_id', 'forum_id', // Topics 'topic_author_id', 'post_count', 'tag_count', // Posts 'post_author_id', 'position' ); $dates = array( 'started', // topic 'updated', // topic 'posted' // post ); $others = array( // Both 'topic', // one topic name 'forum', // one forum name 'tag', // one tag name // Topics 'topic_author', // one username 'topic_status', // *normal, deleted, all, parse_int ( and - ) 'open', // *all, yes = open, no = closed, parse_int ( and - ) 'sticky', // *all, no = normal, forum, super = front, parse_int ( and - ) 'meta_key', // one meta_key ( and - ) 'meta_value', // range 'topic_title', // LIKE search. Understands "doublequoted strings" 'search', // generic search: topic_title OR post_text // Can ONLY be used in a topic query // Returns additional search_score and (concatenated) post_text columns // Posts 'post_author', // one username 'post_status', // *noraml, deleted, all, parse_int ( and - ) 'post_text', // FULLTEXT search // Returns additional search_score column (and (concatenated) post_text column if topic query) 'poster_ip', // one IPv4 address // SQL 'index_hint', // A full index hint using valid index hint syntax, can be multiple hints an array 'order_by', // fieldname 'order', // *DESC, ASC 'count', // *false = none, true = COUNT(*), found_rows = FOUND_ROWS() '_join_type', // not implemented: For benchmarking only. Will disappear. join (1 query), in (2 queries) // Utility // 'append_meta', // *true, false: topics only // 'cache_users', // *true, false // 'cache_topics, // *true, false: posts only // 'post_id_only', // true, *false: this query is only returning post IDs 'cache_posts' // not implemented: none, first, last ); foreach ( $ints as $key ) if ( ( false === $array[$key] = isset($array[$key]) ? (int) $array[$key] : false ) && isset($this) ) $this->not_set[] = $key; foreach ( $parse_ints as $key ) if ( ( false === $array[$key] = isset($array[$key]) ? preg_replace( '/[^<=>0-9,-]/', '', $array[$key] ) : false ) && isset($this) ) $this->not_set[] = $key; foreach ( $dates as $key ) if ( ( false === $array[$key] = isset($array[$key]) ? preg_replace( '/[^<>0-9-]/', '', $array[$key] ) : false ) && isset($this) ) $this->not_set[] = $key; foreach ( $others as $key ) { if ( !isset($array[$key]) ) $array[$key] = false; if ( isset($this) && false === $array[$key] ) $this->not_set[] = $key; } // Both if ( isset($array['page']) ) $array['page'] = (int) $array['page']; elseif ( isset($GLOBALS['page']) ) $array['page'] = (int) $GLOBALS['page']; else $array['page'] = bb_get_uri_page(); if ( $array['page'] < 1 ) $array['page'] = 1; $array['per_page'] = isset($array['per_page']) ? (int) $array['per_page'] : 0; if ( $array['per_page'] < -1 ) $array['per_page'] = 1; // Posts if ( ( !$array['poster_ip'] = isset($array['poster_ip']) ? preg_replace("@[^0-9a-f:.]@i", '', $array['poster_ip']) : false ) && isset($this) ) { $this->not_set[] = 'poster_ip'; $array['poster_ip'] = false; } // Utility $array['append_meta'] = isset($array['append_meta']) ? (int) (bool) $array['append_meta'] : 1; $array['cache_users'] = isset($array['cache_users']) ? (int) (bool) $array['cache_users'] : 1; $array['cache_topics'] = isset($array['cache_topics']) ? (int) (bool) $array['cache_topics'] : 1; $array['post_id_only'] = isset($array['post_id_only']) ? (int) (bool) $array['post_id_only'] : 1; // Only one FULLTEXT search per query please if ( $array['search'] ) $array['post_text'] = false; return $array; } // Parse a query string and set query flag booleans. function parse_query() { if ( $args = func_get_args() ) call_user_func_array( array(&$this, 'init'), $args ); if ( is_array($this->query) ) $this->query_vars = $this->query; else wp_parse_str($this->query, $this->query_vars); do_action_ref_array('bb_parse_query', array(&$this)); if ( false === $this->query_vars ) return; $this->query_vars = $this->fill_query_vars($this->query_vars); } function get($query_var) { return isset($this->query_vars[$query_var]) ? $this->query_vars[$query_var] : null; } function set($query_var, $value) { $this->query_vars[$query_var] = $value; } function generate_topic_sql( $_part_of_post_query = false ) { global $bbdb; $q =& $this->query_vars; $distinct = ''; $sql_calc_found_rows = 'found_rows' === $q['count'] ? 'SQL_CALC_FOUND_ROWS' : ''; // unfiltered $fields = 't.*'; $index_hint = ''; $join = ''; $where = ''; $group_by = ''; $having = ''; $order_by = ''; $post_where = ''; $post_queries = array('post_author_id', 'post_author', 'posted', 'post_status', 'position', 'post_text', 'poster_ip'); if ( !$_part_of_post_query && ( $q['search'] || array_diff($post_queries, $this->not_set) ) ) : $join .= " JOIN $bbdb->posts as p ON ( t.topic_id = p.topic_id )"; $post_where = $this->generate_post_sql( true ); if ( $q['search'] ) { $post_where .= ' AND ( '; $post_where .= $this->generate_topic_title_sql( $q['search'] ); $post_where .= ' OR '; $post_where .= $this->generate_post_text_sql( $q['search'] ); $post_where .= ' )'; } $group_by = 't.topic_id'; $fields .= ", MIN(p.post_id) as post_id"; if ( $bbdb->has_cap( 'GROUP_CONCAT', $bbdb->posts ) ) $fields .= ", GROUP_CONCAT(p.post_text SEPARATOR ' ') AS post_text"; else $fields .= ", p.post_text"; if ( $this->match_query ) { $fields .= ", AVG($this->match_query) AS search_score"; if ( !$q['order_by'] ) $q['order_by'] = 'search_score'; } elseif ( $q['search'] || $q['post_text'] ) { $fields .= ", 0 AS search_score"; } endif; if ( !$_part_of_post_query ) : if ( $q['post_id'] ) : $post_topics = $post_topics_no = array(); $op = substr($q['post_id'], 0, 1); if ( in_array($op, array('>','<')) ) : $post_topics = $bbdb->get_col( "SELECT DISTINCT topic_id FROM $bbdb->posts WHERE post_id $op '" . (int) substr($q['post_id'], 1) . "'" ); else : $posts = explode(',', $q['post_id']); $get_posts = array(); foreach ( $posts as $post_id ) { $post_id = (int) $post_id; $_post_id = abs($post_id); $get_posts[] = $_post_id; } bb_cache_posts( $get_posts ); foreach ( $posts as $post_id ) : $post = bb_get_post( abs($post_id) ); if ( $post_id < 0 ) $post_topics_no[] = $post->topic_id; else $post_topics[] = $post->topic_id; endforeach; endif; if ( $post_topics ) $where .= " AND t.topic_id IN (" . join(',', $post_topics) . ")"; if ( $post_topics_no ) $where .= " AND t.topic_id NOT IN (" . join(',', $post_topics_no) . ")"; endif; if ( $q['topic_id'] ) : $where .= $this->parse_value( 't.topic_id', $q['topic_id'] ); elseif ( $q['topic'] ) : $q['topic'] = bb_slug_sanitize( $q['topic'] ); $where .= " AND t.topic_slug = '$q[topic]'"; endif; if ( $q['forum_id'] ) : $where .= $this->parse_value( 't.forum_id', $q['forum_id'] ); elseif ( $q['forum'] ) : if ( !$q['forum_id'] = bb_get_id_from_slug( 'forum', $q['forum'] ) ) $this->error( 'query_var:forum', 'No forum by that name' ); $where .= " AND t.forum_id = $q[forum_id]"; endif; if ( $q['tag'] && !is_int($q['tag_id']) ) $q['tag_id'] = (int) bb_get_tag_id( $q['tag'] ); if ( is_numeric($q['tag_id']) ) : $join .= " JOIN `$bbdb->term_relationships` AS tr ON ( t.`topic_id` = tr.`object_id` AND tr.`term_taxonomy_id` = $q[tag_id] )"; endif; if ( is_numeric($q['favorites']) && $f_user = bb_get_user( $q['favorites'] ) ) $where .= $this->parse_value( 't.topic_id', $f_user->favorites ); endif; // !_part_of_post_query if ( $q['topic_title'] ) $where .= ' AND ' . $this->generate_topic_title_sql( $q['topic_title'] ); if ( $q['started'] ) $where .= $this->date( 't.topic_start_time', $q['started'] ); if ( $q['updated'] ) $where .= $this->date( 't.topic_time', $q['updated'] ); if ( $q['topic_author_id'] ) : $where .= $this->parse_value( 't.topic_poster', $q['topic_author_id'] ); elseif ( $q['topic_author'] ) : $user = bb_get_user( $q['topic_author'], array( 'by' => 'login' ) ); if ( !$q['topic_author_id'] = (int) $user->ID ) $this->error( 'query_var:user', 'No user by that name' ); $where .= " AND t.topic_poster = $q[topic_author_id]"; endif; if ( !$q['topic_status'] ) : $where .= " AND t.topic_status = '0'"; elseif ( false === strpos($q['topic_status'], 'all') ) : $stati = array( 'normal' => 0, 'deleted' => 1 ); $q['topic_status'] = str_replace(array_keys($stati), array_values($stati), $q['topic_status']); $where .= $this->parse_value( 't.topic_status', $q['topic_status'] ); endif; if ( false !== $q['open'] && false === strpos($q['open'], 'all') ) : $stati = array( 'no' => 0, 'closed' => 0, 'yes' => 1, 'open' => 1 ); $q['open'] = str_replace(array_keys($stati), array_values($stati), $q['open']); $where .= $this->parse_value( 't.topic_open', $q['open'] ); endif; if ( false !== $q['sticky'] && false === strpos($q['sticky'], 'all') ) : $stickies = array( 'no' => 0, 'normal' => 0, 'forum' => 1, 'super' => 2, 'front' => 2, 'sticky' => '-0' ); $q['sticky'] = str_replace(array_keys($stickies), array_values($stickies), $q['sticky']); $where .= $this->parse_value( 't.topic_sticky', $q['sticky'] ); endif; if ( false !== $q['post_count'] ) $where .= $this->parse_value( 't.topic_posts', $q['post_count'] ); if ( false !== $q['tag_count'] ) $where .= $this->parse_value( 't.tag_count', $q['tag_count'] ); if ( $q['meta_key'] && $q['meta_key'] = preg_replace('|[^a-z0-9_-]|i', '', $q['meta_key']) ) : if ( '-' == substr($q['meta_key'], 0, 1) ) : $join .= " LEFT JOIN $bbdb->meta AS tm ON ( tm.object_type = 'bb_topic' AND t.topic_id = tm.object_id AND tm.meta_key = '" . substr( $q['meta_key'], 1 ) . "' )"; $where .= " AND tm.meta_key IS NULL"; else : $join .= " JOIN $bbdb->meta AS tm ON ( tm.object_type = 'bb_topic' AND t.topic_id = tm.object_id AND tm.meta_key = '$q[meta_key]' )"; if ( $q['meta_value'] ) : $q['meta_value'] = maybe_serialize( $q['meta_value'] ); if ( strpos( $q['meta_value'], 'NULL' ) !== false ) $join = ' LEFT' . $join; $where .= $this->parse_value( 'tm.meta_value', $q['meta_value'] ); endif; endif; endif; // Just getting topic part for inclusion in post query if ( $_part_of_post_query ) return $where; $where .= $post_where; if ( $where ) // Get rid of initial " AND " (this is pre-filters) $where = substr($where, 5); if ( $q['index_hint'] ) $index_hint = $q['index_hint']; if ( $q['order_by'] ) $order_by = $q['order_by']; else $order_by = 't.topic_time'; $bits = compact( array('distinct', 'sql_calc_found_rows', 'fields', 'index_hint', 'join', 'where', 'group_by', 'having', 'order_by') ); $this->request = $this->_filter_sql( $bits, "$bbdb->topics AS t" ); return $this->request; } function generate_post_sql( $_part_of_topic_query = false ) { global $bbdb; $q =& $this->query_vars; $distinct = ''; $sql_calc_found_rows = 'found_rows' === $q['count'] ? 'SQL_CALC_FOUND_ROWS' : ''; // unfiltered $fields = 'p.*'; $index_hint = ''; $join = ''; $where = ''; $group_by = ''; $having = ''; $order_by = ''; $topic_where = ''; $topic_queries = array( 'topic_author_id', 'topic_author', 'topic_status', 'post_count', 'tag_count', 'started', 'updated', 'open', 'sticky', 'meta_key', 'meta_value', 'topic_title' ); if ( !$_part_of_topic_query && array_diff($topic_queries, $this->not_set) ) : $join .= " JOIN $bbdb->topics as t ON ( t.topic_id = p.topic_id )"; $topic_where = $this->generate_topic_sql( true ); endif; if ( !$_part_of_topic_query ) : if ( $q['post_id'] ) $where .= $this->parse_value( 'p.post_id', $q['post_id'] ); if ( $q['topic_id'] ) : $where .= $this->parse_value( 'p.topic_id', $q['topic_id'] ); elseif ( $q['topic'] ) : if ( !$q['topic_id'] = bb_get_id_from_slug( 'topic', $q['topic'] ) ) $this->error( 'query_var:topic', 'No topic by that name' ); $where .= " AND p.topic_id = " . $q['topic_id']; endif; if ( $q['forum_id'] ) : $where .= $this->parse_value( 'p.forum_id', $q['forum_id'] ); elseif ( $q['forum'] ) : if ( !$q['forum_id'] = bb_get_id_from_slug( 'forum', $q['forum'] ) ) $this->error( 'query_var:forum', 'No forum by that name' ); $where .= " AND p.forum_id = " . $q['forum_id']; endif; if ( $q['tag'] && !is_int($q['tag_id']) ) $q['tag_id'] = (int) bb_get_tag_id( $q['tag'] ); if ( is_numeric($q['tag_id']) ) : $join .= " JOIN `$bbdb->term_relationships` AS tr ON ( p.`topic_id` = tr.`object_id` AND tr.`term_taxonomy_id` = $q[tag_id] )"; endif; if ( is_numeric($q['favorites']) && $f_user = bb_get_user( $q['favorites'] ) ) $where .= $this->parse_value( 'p.topic_id', $f_user->favorites ); endif; // !_part_of_topic_query if ( $q['post_text'] ) : $where .= ' AND ' . $this->generate_post_text_sql( $q['post_text'] ); if ( $this->match_query ) { $fields .= ", $this->match_query AS search_score"; if ( !$q['order_by'] ) $q['order_by'] = 'search_score'; } else { $fields .= ', 0 AS search_score'; } endif; if ( $q['posted'] ) $where .= $this->date( 'p.post_time', $q['posted'] ); if ( $q['post_author_id'] ) : $where .= $this->parse_value( 'p.poster_id', $q['post_author_id'] ); elseif ( $q['post_author'] ) : $user = bb_get_user( $q['post_author'], array( 'by' => 'login' ) ); if ( !$q['post_author_id'] = (int) $user->ID ) $this->error( 'query_var:user', 'No user by that name' ); $where .= " AND p.poster_id = $q[post_author_id]"; endif; if ( !$q['post_status'] ) : $where .= " AND p.post_status = '0'"; elseif ( false === strpos($q['post_status'], 'all') ) : $stati = array( 'normal' => 0, 'deleted' => 1 ); $q['post_status'] = str_replace(array_keys($stati), array_values($stati), $q['post_status']); $where .= $this->parse_value( 'p.post_status', $q['post_status'] ); endif; if ( false !== $q['position'] ) $where .= $this->parse_value( 'p.post_position', $q['position'] ); if ( false !== $q['poster_ip'] ) $where .= " AND poster_ip = '" . $q['poster_ip'] . "'"; // Just getting post part for inclusion in topic query if ( $_part_of_topic_query ) return $where; $where .= $topic_where; if ( $where ) // Get rid of initial " AND " (this is pre-filters) $where = substr($where, 5); if ( $q['index_hint'] ) $index_hint = $q['index_hint']; if ( $q['order_by'] ) $order_by = $q['order_by']; else $order_by = 'p.post_time'; $bits = compact( array('distinct', 'sql_calc_found_rows', 'fields', 'index_hint', 'join', 'where', 'group_by', 'having', 'order_by') ); $this->request = $this->_filter_sql( $bits, "$bbdb->posts AS p" ); return $this->request; } function generate_topic_title_sql( $string ) { global $bbdb; $string = trim($string); if ( !preg_match_all('/".*?("|$)|((?<=[\s",+])|^)[^\s",+]+/', $string, $matches) ) { $string = $bbdb->escape($string); return "(t.topic_title LIKE '%$string%')"; } $where = ''; foreach ( $matches[0] as $match ) { $term = trim($match, "\"\n\r "); $term = $bbdb->escape($term); $where .= " AND t.topic_title LIKE '%$term%'"; } if ( count($matches[0]) > 1 && $string != $matches[0][0] ) { $string = $bbdb->escape($string); $where .= " OR t.topic_title LIKE '%$string%'"; } return '(' . substr($where, 5) . ')'; } function generate_post_text_sql( $string ) { global $bbdb; $string = trim($string); $_string = $bbdb->escape( $string ); if ( strlen($string) < 5 ) return "p.post_text LIKE '%$_string%'"; return $this->match_query = "MATCH(p.post_text) AGAINST('$_string')"; } function _filter_sql( $bits, $from ) { global $bbdb; $q =& $this->query_vars; // MySQL 5.1 allows multiple index hints per query - earlier versions only get the first hint if ( $bits['index_hint'] ) { if ( !is_array( $bits['index_hint'] ) ) { $bits['index_hint'] = array( (string) $bits['index_hint'] ); } if ( $bbdb->has_cap( 'index_hint_for_any' ) ) { // 5.1 <= MySQL $_regex = '/\s*(USE|IGNORE|FORCE)\s+(INDEX|KEY)\s+(FOR\s+(JOIN|ORDER\s+BY|GROUP\s+BY)\s+)?\(\s*`?[a-z0-9_]+`?(\s*,\s*`?[a-z0-9_]+`?)*\s*\)\s*/i'; } elseif ( $bbdb->has_cap( 'index_hint_for_join' ) ) { // 5.0 <= MySQL < 5.1 $_regex = '/\s*(USE|IGNORE|FORCE)\s+(INDEX|KEY)\s+(FOR\s+JOIN\s+)?\(\s*`?[a-z0-9_]+`?(\s*,\s*`?[a-z0-9_]+`?)*\s*\)\s*/i'; } else { // MySQL < 5.0 $_regex = '/\s*(USE|IGNORE|FORCE)\s+(INDEX|KEY)\s+\(\s*`?[a-z0-9_]+`?(\s*,\s*`?[a-z0-9_]+`?)*\s*\)\s*/i'; } $_index_hint = array(); foreach ( $bits['index_hint'] as $_hint ) { if ( preg_match( $_regex, $_hint ) ) { $_index_hint[] = trim( $_hint ); } } unset( $_regex, $_hint ); if ( $bbdb->has_cap( 'index_hint_lists' ) ) { // 5.1 <= MySQL $bits['index_hint'] = join( ' ', $_index_hint ); } else { // MySQL < 5.1 $bits['index_hint'] = isset( $_index_hint[0] ) ? $_index_hint[0] : ''; } unset( $_index_hint ); } $q['order'] = strtoupper($q['order']); if ( $q['order'] && in_array($q['order'], array('ASC', 'DESC')) ) $bits['order_by'] .= " $q[order]"; else $bits['order_by'] .= " DESC"; $bits['limit'] = ''; // When offset and number are provided, skip per_page and limit checks if ( !empty( $q['offset'] ) && !empty( $q['number'] ) ) { $bits['limit'] .= $q['offset'] . ", " . $q['number']; // Else proceed as normal } else { if ( !$q['per_page'] ) { $q['per_page'] = (int) bb_get_option( 'page_topics' ); } if ( $q['per_page'] > 0 ) { if ( $q['page'] > 1 ) { $bits['limit'] .= $q['per_page'] * ( $q['page'] - 1 ) . ", "; } $bits['limit'] .= $q['per_page']; } } $name = "get_{$this->type}s_"; // Unfiltered $sql_calc_found_rows = $bits['sql_calc_found_rows']; unset($bits['sql_calc_found_rows']); foreach ( $bits as $bit => $value ) { if ( $this->query_id ) $value = apply_filters( "{$this->query_id}_$bit", $value ); $$bit = apply_filters( "$name$bit", $value ); } if ( $where ) $where = "WHERE $where"; if ( $group_by ) $group_by = "GROUP BY $group_by"; if ( $having ) $having = "HAVING $having"; if ( $order_by ) $order_by = "ORDER BY $order_by"; if ( $limit ) $limit = "LIMIT $limit"; return "SELECT $distinct $sql_calc_found_rows $fields FROM $from $index_hint $join $where $group_by $having $order_by $limit"; } function parse_value( $field, $value = '' ) { if ( !$value && !is_numeric($value) ) return ''; global $bbdb; $op = substr($value, 0, 1); // #, =whatever, <#, >#. Cannot do < and > at same time if ( in_array($op, array('<', '=', '>')) ) : $value = substr($value, 1); $value = is_numeric($value) ? (float) $value : $bbdb->escape( $value ); return " AND $field $op '$value'"; elseif ( false === strpos($value, ',') && 'NULL' !== $value && '-NULL' !== $value ) : $value = is_numeric($value) ? (float) $value : $bbdb->escape( $value ); return '-' == $op ? " AND $field != '" . substr($value, 1) . "'" : " AND $field = '$value'"; endif; $y = $n = array(); foreach ( explode(',', $value) as $v ) { $v = is_numeric($v) ? (int) $v : $bbdb->escape( $v ); if ( '-' == substr($v, 0, 1) ) if ( $v === '-NULL' ) $not_null_flag = true; else $n[] = substr($v, 1); else if ( $v === 'NULL' ) $null_flag = true; else $y[] = $v; } $r = ''; if ( $y ) { $r .= " AND "; if ( $null_flag ) $r .= "("; $r .= "$field IN ('" . join("','", $y) . "')"; if ( $null_flag ) $r .= " OR $field IS NULL)"; } elseif ( $null_flag ) { $r .= " AND $field IS NULL"; } if ( $n ) { $r .= " AND "; if ( $not_null_flag ) $r .= "("; $r .= "$field NOT IN ('" . join("','", $n) . "')"; if ( $not_null_flag ) $r .= " AND $field IS NOT NULL)"; } elseif ( $not_null_flag ) { $r .= " AND $field IS NOT NULL"; } return $r; } function date( $field, $date ) { if ( !$date && !is_int($date) ) return ''; if ( $is_range = false !== strpos( $date, '--' ) ) $dates = explode( '--', $date, 2 ); else $dates = array( $date ); $op = false; $r = ''; foreach ( $dates as $date ) { if ( $is_range ) { $op = $op ? '<' : '>'; $date = (int) substr($date, 0, 14); } else { $op = substr($date, 0, 1); if ( !in_array($op, array('>', '<')) ) break; $date = (int) substr($date, 1, 14); } if ( strlen($date) < 14 ) $date .= str_repeat('0', 14 - strlen($date)); $r .= " AND $field $op $date"; } if ( $r ) return $r; $date = (int) $date; $r = " AND YEAR($field) = " . substr($date, 0, 4); if ( strlen($date) > 5 ) $r .= " AND MONTH($field) = " . substr($date, 4, 2); if ( strlen($date) > 7 ) $r .= " AND DAYOFMONTH($field) = " . substr($date, 6, 2); if ( strlen($date) > 9 ) $r .= " AND HOUR($field) = " . substr($date, 8, 2); if ( strlen($date) > 11 ) $r .= " AND MINUTE($field) = " . substr($date, 10, 2); if ( strlen($date) > 13 ) $r .= " AND SECOND($field) = " . substr($date, 12, 2); return $r; } function error( $code, $message ) { if ( is_wp_error($this->errors) ) $this->errors->add( $code, $message ); else $this->errors = new WP_Error( $code, $message ); } } class BB_Query_Form extends BB_Query { var $defaults; var $allowed; // Can optionally pass unique id string to help out filters function __construct( $type = 'topic', $defaults = '', $allowed = '', $id = '' ) { $this->defaults = wp_parse_args( $defaults ); $this->allowed = wp_parse_args( $allowed ); if ( !empty($defaults) || !empty($allowed) ) $this->query_from_env($type, $defaults, $allowed, $id); } function BB_Query_Form( $type = 'topic', $defaults = '', $allowed = '', $id = '' ) { $this->__construct( $type, $defaults, $allowed, $id ); } function form( $args = null ) { $_post = 'post' == $this->type; $defaults = array( 'search' => true, 'forum' => true, 'tag' => false, 'open' => false, 'topic_author' => false, 'post_author' => false, 'topic_status' => false, 'post_status' => false, 'topic_title' => false, 'poster_ip' => false, 'method' => 'get', 'submit' => __('Search »'), 'action' => '' ); $defaults['id'] = $_post ? 'post-search-form' : 'topic-search-form'; $args = wp_parse_args( $args, $defaults ); extract( $args, EXTR_SKIP ); $id = esc_attr( $id ); $method = 'get' == strtolower($method) ? 'get' : 'post'; $submit = esc_attr( $submit ); if ( !$action = esc_url( $action ) ) $action = ''; if ( $this->query_vars ) $query_vars =& $this->query_vars; else $query_vars = $this->fill_query_vars( $this->defaults ); extract($query_vars, EXTR_PREFIX_ALL, 'q'); $r = "
\n"; $r .= "\t
\n"; if ( $search ) { if ( $_post ) { $s_value = esc_attr( $q_post_text ); $s_name = 'post_text'; $s_id = 'post-text'; } else { $s_value = esc_attr( $q_search ); $s_name = $s_id = 'search'; } $r .= "\t
\n"; $r .= "\t\t
\n"; $r .= "\t
\n\n"; } if ( $forum ) { $r .= "\t
\n"; $r .= "\t\t
" . bb_get_forum_dropdown( array( 'selected' => $q_forum_id, 'none' => __('Any') ) ) . "
\n"; $r .= "\t
\n\n"; } if ( $tag ) { $q_tag = esc_attr( $q_tag ); $r .= "\t
\n"; $r .= "\t\t
\n"; $r .= "\t
\n\n"; } if ( $topic_author ) { $q_topic_author = esc_attr( $q_topic_author ); $r .= "\t
\n"; $r .= "\t\t
\n"; $r .= "\t
\n\n"; } if ( $post_author ) { $q_post_author = esc_attr( $q_post_author ); $r .= "\t
\n"; $r .= "\t\t
\n"; $r .= "\t
\n\n"; } $stati = apply_filters( 'bb_query_form_post_status', array( 'all' => _x( 'All', 'post status' ), '0' => __('Normal'), '1' => __('Deleted') ), $this->type ); if ( $topic_status ) { $r .= "\t
\n"; $r .= "\t\t
\n"; $r .= "\t
\n\n"; } if ( $post_status ) { $r .= "\t
\n"; $r .= "\t\t
\n"; $r .= "\t
\n\n"; } if ( $poster_ip ) { $r .= "\t
\n"; $r .= "\t\t
\n"; $r .= "\t
\n\n"; } if ( $open ) { $r .= "\t
\n"; $r .= "\t\t
\n"; $r .= "\t
\n\n"; } if ( $topic_title ) { $q_topic_title = esc_attr( $q_topic_title ); $r .= "\t
\n"; $r .= "\t\t
\n"; $r .= "\t
\n\n"; } $r .= apply_filters( 'bb_query_form_inputs', '', $args, $query_vars ); $r .= "\t
\n"; $r .= "\t\t
\n"; $r .= "\t
\n"; $r .= "\t
\n\n"; do_action( 'bb_query_form', $args, $query_vars ); $r .= "
\n\n"; echo $r; } }