[ Index ] |
PHP Cross Reference of BuddyPress |
[Summary view] [Print] [Text view]
1 <?php 2 3 class BB_Query { 4 var $type; 5 var $query; 6 var $query_id; 7 8 var $query_vars = array(); 9 var $not_set = array(); 10 var $request; 11 var $match_query = false; 12 13 var $results; 14 var $errors; 15 var $count = 0; 16 var $found_rows = false; 17 18 // Can optionally pass unique id string to help out filters 19 function __construct( $type = 'topic', $query = '', $id = '' ) { 20 $this->init( $type, $query, $id ); 21 22 if ( !empty($this->query) ) 23 $this->query(); 24 } 25 26 function BB_Query( $type = 'topic', $query = '', $id = '' ) { 27 $this->__construct( $type, $query, $id ); 28 } 29 30 function init( $type = null, $query = null, $id = null ) { 31 if ( !is_null($type) || !isset($this->type) ) 32 $this->type = is_null($type) ? 'topic' : $type; 33 if ( !is_null($query) || !isset($this->query) ) 34 $this->query = $query; 35 if ( !is_null($id) || !isset($this->query_id) ) 36 $this->query_id = $id; 37 38 $this->query_vars = array(); 39 $this->not_set = array(); 40 unset($this->request); 41 $this->match_query = false; 42 43 unset($this->results, $this->errors); 44 $this->count = 0; 45 $this->found_rows = false; 46 } 47 48 function &query() { 49 global $bbdb; 50 51 if ( $args = func_get_args() ) 52 call_user_func_array( array(&$this, 'init'), $args ); 53 54 if ( !$this->generate_query() ) 55 return; 56 57 do_action_ref_array( 'bb_query', array(&$this) ); 58 59 $key = md5( $this->request ); 60 if ( false === $cached_ids = wp_cache_get( $key, 'bb_query' ) ) { 61 if ( 'post' == $this->type ) { 62 $this->results = bb_cache_posts( $this->request, $this->query_vars['post_id_only'] ); // This always appends meta 63 $_the_id = 'post_id'; 64 $this->query_vars['append_meta'] = false; 65 } else { 66 $this->results = $bbdb->get_results( $this->request ); 67 $_the_id = 'topic_id'; 68 } 69 $cached_ids = array(); 70 if ( is_array($this->results) ) 71 foreach ( $this->results as $object ) 72 $cached_ids[] = $object->$_the_id; 73 wp_cache_set( $key, $cached_ids, 'bb_query' ); 74 } else { 75 if ( 'post' == $this->type ) { 76 $_query_ids = array(); 77 $_cached_posts = array(); 78 foreach ( $cached_ids as $_cached_id ) { 79 if ( false !== $_post = wp_cache_get( $_cached_id, 'bb_post' ) ) { 80 $_cached_posts[$_post->post_id] = $_post; 81 } else { 82 $_query_ids[] = $_cached_id; 83 } 84 } 85 if ( count( $_query_ids ) ) { 86 $_query_ids = join( ',', array_map( 'intval', $_query_ids ) ); 87 $results = $bbdb->get_results( "SELECT * FROM $bbdb->posts WHERE post_id IN($_query_ids)" ); 88 $results = array_merge( $results, $_cached_posts ); 89 } else { 90 $results = $_cached_posts; 91 } 92 $_the_id = 'post_id'; 93 } else { 94 $_query_ids = array(); 95 $_cached_topics = array(); 96 foreach ( $cached_ids as $_cached_id ) { 97 if ( false !== $_topic = wp_cache_get( $_cached_id, 'bb_topic' ) ) { 98 $_cached_topics[$_topic->topic_id] = $_topic; 99 } else { 100 $_query_ids[] = $_cached_id; 101 } 102 } 103 if ( count( $_query_ids ) ) { 104 $_query_ids = join( ',', array_map( 'intval', $_query_ids ) ); 105 $results = $bbdb->get_results( "SELECT * FROM $bbdb->topics WHERE topic_id IN($_query_ids)" ); 106 $results = array_merge( $results, $_cached_topics ); 107 } else { 108 $results = $_cached_topics; 109 } 110 $_the_id = 'topic_id'; 111 } 112 113 $this->results = array(); 114 $trans = array(); 115 116 foreach ( $results as $object ) 117 $trans[$object->$_the_id] = $object; 118 foreach ( $cached_ids as $cached_id ) 119 $this->results[] = $trans[$cached_id]; 120 } 121 122 $this->count = count( $this->results ); 123 124 if ( false === $this->found_rows && $this->query_vars['count'] ) // handles FOUND_ROWS() or COUNT(*) 125 $this->found_rows = bb_count_last_query( $this->request ); 126 if ( 'post' == $this->type ) { 127 if ( $this->query_vars['append_meta'] ) 128 $this->results = bb_append_meta( $this->results, 'post' ); 129 if ( $this->query_vars['cache_users'] ) 130 bb_post_author_cache( $this->results ); 131 if ( $this->query_vars['cache_topics'] ) 132 bb_cache_post_topics( $this->results ); 133 } else { 134 if ( $this->query_vars['append_meta'] ) 135 $this->results = bb_append_meta( $this->results, 'topic' ); 136 } 137 138 return $this->results; 139 } 140 141 function generate_query() { 142 if ( $args = func_get_args() ) 143 call_user_func_array( array(&$this, 'init'), $args ); 144 145 $this->parse_query(); 146 147 // Allow filter to abort query 148 if ( false === $this->query_vars ) 149 return false; 150 151 if ( 'post' == $this->type ) 152 $this->generate_post_sql(); 153 else 154 $this->generate_topic_sql(); 155 156 return $this->request; 157 } 158 159 // $defaults = vars to use if not set in GET, POST or allowed 160 // $allowed = array( key_name => value, key_name, key_name, key_name => value ); 161 // key_name => value pairs override anything from defaults, GET, POST 162 // Lone key_names are a whitelist. Only those can be set by defaults, GET, POST 163 // If there are no lone key_names, allow everything but still override with key_name => value pairs 164 // Ex: $allowed = array( 'topic_status' => 0, 'post_status' => 0, 'topic_author', 'started' ); 165 // Will only take topic_author and started values from defaults, GET, POST and will query with topic_status = 0 and post_status = 0 166 function &query_from_env( $type = 'topic', $defaults = null, $allowed = null, $id = '' ) { 167 $this->init_from_env( $type, $defaults, $allowed, $id ); 168 return $this->query(); 169 } 170 171 function init_from_env( $type = 'topic', $defaults = null, $allowed = null, $id = '' ) { 172 $vars = $this->fill_query_vars( array() ); 173 174 $defaults = wp_parse_args($defaults); 175 $get_vars = stripslashes_deep( $_GET ); 176 $post_vars = stripslashes_deep( $_POST ); 177 $allowed = wp_parse_args($allowed); 178 179 $_allowed = array(); 180 foreach ( array_keys($allowed) as $k ) { 181 if ( is_numeric($k) ) { 182 $_allowed[] = $allowed[$k]; 183 unset($allowed[$k]); 184 } elseif ( !isset($$k) ) { 185 $$k = $allowed[$k]; 186 } 187 } 188 189 extract($post_vars, EXTR_SKIP); 190 extract($get_vars, EXTR_SKIP); 191 extract($defaults, EXTR_SKIP); 192 193 $vars = $_allowed ? compact($_allowed, array_keys($allowed)) : compact(array_keys($vars)); 194 195 $this->init( $type, $vars, $id ); 196 } 197 198 function fill_query_vars( $array ) { 199 // Should use 0, '' for empty values 200 // Function should return false iff not set 201 202 // parameters commented out are handled farther down 203 204 $ints = array( 205 // 'page', // Defaults to global or number in URI 206 // 'per_page', // Defaults to page_topics 207 'tag_id', // one tag ID 208 'favorites', // one user ID, 209 'offset', // first item to query 210 'number' // number of items to retrieve 211 ); 212 213 $parse_ints = array( 214 // Both 215 'post_id', 216 'topic_id', 217 'forum_id', 218 219 // Topics 220 'topic_author_id', 221 'post_count', 222 'tag_count', 223 224 // Posts 225 'post_author_id', 226 'position' 227 ); 228 229 $dates = array( 230 'started', // topic 231 'updated', // topic 232 'posted' // post 233 ); 234 235 $others = array( 236 // Both 237 'topic', // one topic name 238 'forum', // one forum name 239 'tag', // one tag name 240 241 // Topics 242 'topic_author', // one username 243 'topic_status', // *normal, deleted, all, parse_int ( and - ) 244 'open', // *all, yes = open, no = closed, parse_int ( and - ) 245 'sticky', // *all, no = normal, forum, super = front, parse_int ( and - ) 246 'meta_key', // one meta_key ( and - ) 247 'meta_value', // range 248 'topic_title', // LIKE search. Understands "doublequoted strings" 249 'search', // generic search: topic_title OR post_text 250 // Can ONLY be used in a topic query 251 // Returns additional search_score and (concatenated) post_text columns 252 253 // Posts 254 'post_author', // one username 255 'post_status', // *noraml, deleted, all, parse_int ( and - ) 256 'post_text', // FULLTEXT search 257 // Returns additional search_score column (and (concatenated) post_text column if topic query) 258 'poster_ip', // one IPv4 address 259 260 // SQL 261 'index_hint', // A full index hint using valid index hint syntax, can be multiple hints an array 262 'order_by', // fieldname 263 'order', // *DESC, ASC 264 'count', // *false = none, true = COUNT(*), found_rows = FOUND_ROWS() 265 '_join_type', // not implemented: For benchmarking only. Will disappear. join (1 query), in (2 queries) 266 267 // Utility 268 // 'append_meta', // *true, false: topics only 269 // 'cache_users', // *true, false 270 // 'cache_topics, // *true, false: posts only 271 // 'post_id_only', // true, *false: this query is only returning post IDs 272 'cache_posts' // not implemented: none, first, last 273 ); 274 275 foreach ( $ints as $key ) 276 if ( ( false === $array[$key] = isset($array[$key]) ? (int) $array[$key] : false ) && isset($this) ) 277 $this->not_set[] = $key; 278 279 foreach ( $parse_ints as $key ) 280 if ( ( false === $array[$key] = isset($array[$key]) ? preg_replace( '/[^<=>0-9,-]/', '', $array[$key] ) : false ) && isset($this) ) 281 $this->not_set[] = $key; 282 283 foreach ( $dates as $key ) 284 if ( ( false === $array[$key] = isset($array[$key]) ? preg_replace( '/[^<>0-9-]/', '', $array[$key] ) : false ) && isset($this) ) 285 $this->not_set[] = $key; 286 287 foreach ( $others as $key ) { 288 if ( !isset($array[$key]) ) 289 $array[$key] = false; 290 if ( isset($this) && false === $array[$key] ) 291 $this->not_set[] = $key; 292 } 293 294 // Both 295 if ( isset($array['page']) ) 296 $array['page'] = (int) $array['page']; 297 elseif ( isset($GLOBALS['page']) ) 298 $array['page'] = (int) $GLOBALS['page']; 299 else 300 $array['page'] = bb_get_uri_page(); 301 302 if ( $array['page'] < 1 ) 303 $array['page'] = 1; 304 305 $array['per_page'] = isset($array['per_page']) ? (int) $array['per_page'] : 0; 306 if ( $array['per_page'] < -1 ) 307 $array['per_page'] = 1; 308 309 // Posts 310 if ( ( !$array['poster_ip'] = isset($array['poster_ip']) ? preg_replace("@[^0-9a-f:.]@i", '', $array['poster_ip']) : false ) && isset($this) ) { 311 $this->not_set[] = 'poster_ip'; 312 $array['poster_ip'] = false; 313 } 314 315 // Utility 316 $array['append_meta'] = isset($array['append_meta']) ? (int) (bool) $array['append_meta'] : 1; 317 $array['cache_users'] = isset($array['cache_users']) ? (int) (bool) $array['cache_users'] : 1; 318 $array['cache_topics'] = isset($array['cache_topics']) ? (int) (bool) $array['cache_topics'] : 1; 319 $array['post_id_only'] = isset($array['post_id_only']) ? (int) (bool) $array['post_id_only'] : 1; 320 321 // Only one FULLTEXT search per query please 322 if ( $array['search'] ) 323 $array['post_text'] = false; 324 325 return $array; 326 } 327 328 // Parse a query string and set query flag booleans. 329 function parse_query() { 330 if ( $args = func_get_args() ) 331 call_user_func_array( array(&$this, 'init'), $args ); 332 333 if ( is_array($this->query) ) 334 $this->query_vars = $this->query; 335 else 336 wp_parse_str($this->query, $this->query_vars); 337 338 do_action_ref_array('bb_parse_query', array(&$this)); 339 340 if ( false === $this->query_vars ) 341 return; 342 343 $this->query_vars = $this->fill_query_vars($this->query_vars); 344 } 345 346 function get($query_var) { 347 return isset($this->query_vars[$query_var]) ? $this->query_vars[$query_var] : null; 348 } 349 350 function set($query_var, $value) { 351 $this->query_vars[$query_var] = $value; 352 } 353 354 function generate_topic_sql( $_part_of_post_query = false ) { 355 global $bbdb; 356 357 $q =& $this->query_vars; 358 $distinct = ''; 359 $sql_calc_found_rows = 'found_rows' === $q['count'] ? 'SQL_CALC_FOUND_ROWS' : ''; // unfiltered 360 $fields = 't.*'; 361 $index_hint = ''; 362 $join = ''; 363 $where = ''; 364 $group_by = ''; 365 $having = ''; 366 $order_by = ''; 367 368 $post_where = ''; 369 $post_queries = array('post_author_id', 'post_author', 'posted', 'post_status', 'position', 'post_text', 'poster_ip'); 370 371 if ( !$_part_of_post_query && ( $q['search'] || array_diff($post_queries, $this->not_set) ) ) : 372 $join .= " JOIN $bbdb->posts as p ON ( t.topic_id = p.topic_id )"; 373 $post_where = $this->generate_post_sql( true ); 374 if ( $q['search'] ) { 375 $post_where .= ' AND ( '; 376 $post_where .= $this->generate_topic_title_sql( $q['search'] ); 377 $post_where .= ' OR '; 378 $post_where .= $this->generate_post_text_sql( $q['search'] ); 379 $post_where .= ' )'; 380 } 381 382 $group_by = 't.topic_id'; 383 384 $fields .= ", MIN(p.post_id) as post_id"; 385 386 if ( $bbdb->has_cap( 'GROUP_CONCAT', $bbdb->posts ) ) 387 $fields .= ", GROUP_CONCAT(p.post_text SEPARATOR ' ') AS post_text"; 388 else 389 $fields .= ", p.post_text"; 390 391 if ( $this->match_query ) { 392 $fields .= ", AVG($this->match_query) AS search_score"; 393 if ( !$q['order_by'] ) 394 $q['order_by'] = 'search_score'; 395 } elseif ( $q['search'] || $q['post_text'] ) { 396 $fields .= ", 0 AS search_score"; 397 } 398 endif; 399 400 if ( !$_part_of_post_query ) : 401 if ( $q['post_id'] ) : 402 $post_topics = $post_topics_no = array(); 403 $op = substr($q['post_id'], 0, 1); 404 if ( in_array($op, array('>','<')) ) : 405 $post_topics = $bbdb->get_col( "SELECT DISTINCT topic_id FROM $bbdb->posts WHERE post_id $op '" . (int) substr($q['post_id'], 1) . "'" ); 406 else : 407 $posts = explode(',', $q['post_id']); 408 $get_posts = array(); 409 foreach ( $posts as $post_id ) { 410 $post_id = (int) $post_id; 411 $_post_id = abs($post_id); 412 $get_posts[] = $_post_id; 413 } 414 bb_cache_posts( $get_posts ); 415 416 foreach ( $posts as $post_id ) : 417 $post = bb_get_post( abs($post_id) ); 418 if ( $post_id < 0 ) 419 $post_topics_no[] = $post->topic_id; 420 else 421 $post_topics[] = $post->topic_id; 422 endforeach; 423 endif; 424 if ( $post_topics ) 425 $where .= " AND t.topic_id IN (" . join(',', $post_topics) . ")"; 426 if ( $post_topics_no ) 427 $where .= " AND t.topic_id NOT IN (" . join(',', $post_topics_no) . ")"; 428 endif; 429 430 if ( $q['topic_id'] ) : 431 $where .= $this->parse_value( 't.topic_id', $q['topic_id'] ); 432 elseif ( $q['topic'] ) : 433 $q['topic'] = bb_slug_sanitize( $q['topic'] ); 434 $where .= " AND t.topic_slug = '$q[topic]'"; 435 endif; 436 437 if ( $q['forum_id'] ) : 438 $where .= $this->parse_value( 't.forum_id', $q['forum_id'] ); 439 elseif ( $q['forum'] ) : 440 if ( !$q['forum_id'] = bb_get_id_from_slug( 'forum', $q['forum'] ) ) 441 $this->error( 'query_var:forum', 'No forum by that name' ); 442 $where .= " AND t.forum_id = $q[forum_id]"; 443 endif; 444 445 if ( $q['tag'] && !is_int($q['tag_id']) ) 446 $q['tag_id'] = (int) bb_get_tag_id( $q['tag'] ); 447 448 if ( is_numeric($q['tag_id']) ) : 449 $join .= " JOIN `$bbdb->term_relationships` AS tr ON ( t.`topic_id` = tr.`object_id` AND tr.`term_taxonomy_id` = $q[tag_id] )"; 450 endif; 451 452 if ( is_numeric($q['favorites']) && $f_user = bb_get_user( $q['favorites'] ) ) 453 $where .= $this->parse_value( 't.topic_id', $f_user->favorites ); 454 endif; // !_part_of_post_query 455 456 if ( $q['topic_title'] ) 457 $where .= ' AND ' . $this->generate_topic_title_sql( $q['topic_title'] ); 458 459 if ( $q['started'] ) 460 $where .= $this->date( 't.topic_start_time', $q['started'] ); 461 462 if ( $q['updated'] ) 463 $where .= $this->date( 't.topic_time', $q['updated'] ); 464 465 if ( $q['topic_author_id'] ) : 466 $where .= $this->parse_value( 't.topic_poster', $q['topic_author_id'] ); 467 elseif ( $q['topic_author'] ) : 468 $user = bb_get_user( $q['topic_author'], array( 'by' => 'login' ) ); 469 if ( !$q['topic_author_id'] = (int) $user->ID ) 470 $this->error( 'query_var:user', 'No user by that name' ); 471 $where .= " AND t.topic_poster = $q[topic_author_id]"; 472 endif; 473 474 if ( !$q['topic_status'] ) : 475 $where .= " AND t.topic_status = '0'"; 476 elseif ( false === strpos($q['topic_status'], 'all') ) : 477 $stati = array( 'normal' => 0, 'deleted' => 1 ); 478 $q['topic_status'] = str_replace(array_keys($stati), array_values($stati), $q['topic_status']); 479 $where .= $this->parse_value( 't.topic_status', $q['topic_status'] ); 480 endif; 481 482 if ( false !== $q['open'] && false === strpos($q['open'], 'all') ) : 483 $stati = array( 'no' => 0, 'closed' => 0, 'yes' => 1, 'open' => 1 ); 484 $q['open'] = str_replace(array_keys($stati), array_values($stati), $q['open']); 485 $where .= $this->parse_value( 't.topic_open', $q['open'] ); 486 endif; 487 488 if ( false !== $q['sticky'] && false === strpos($q['sticky'], 'all') ) : 489 $stickies = array( 'no' => 0, 'normal' => 0, 'forum' => 1, 'super' => 2, 'front' => 2, 'sticky' => '-0' ); 490 $q['sticky'] = str_replace(array_keys($stickies), array_values($stickies), $q['sticky']); 491 $where .= $this->parse_value( 't.topic_sticky', $q['sticky'] ); 492 endif; 493 494 if ( false !== $q['post_count'] ) 495 $where .= $this->parse_value( 't.topic_posts', $q['post_count'] ); 496 497 if ( false !== $q['tag_count'] ) 498 $where .= $this->parse_value( 't.tag_count', $q['tag_count'] ); 499 500 if ( $q['meta_key'] && $q['meta_key'] = preg_replace('|[^a-z0-9_-]|i', '', $q['meta_key']) ) : 501 if ( '-' == substr($q['meta_key'], 0, 1) ) : 502 $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 ) . "' )"; 503 $where .= " AND tm.meta_key IS NULL"; 504 else : 505 $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]' )"; 506 507 if ( $q['meta_value'] ) : 508 $q['meta_value'] = maybe_serialize( $q['meta_value'] ); 509 if ( strpos( $q['meta_value'], 'NULL' ) !== false ) 510 $join = ' LEFT' . $join; 511 $where .= $this->parse_value( 'tm.meta_value', $q['meta_value'] ); 512 endif; 513 endif; 514 endif; 515 516 // Just getting topic part for inclusion in post query 517 if ( $_part_of_post_query ) 518 return $where; 519 520 $where .= $post_where; 521 522 if ( $where ) // Get rid of initial " AND " (this is pre-filters) 523 $where = substr($where, 5); 524 525 if ( $q['index_hint'] ) 526 $index_hint = $q['index_hint']; 527 528 if ( $q['order_by'] ) 529 $order_by = $q['order_by']; 530 else 531 $order_by = 't.topic_time'; 532 533 $bits = compact( array('distinct', 'sql_calc_found_rows', 'fields', 'index_hint', 'join', 'where', 'group_by', 'having', 'order_by') ); 534 $this->request = $this->_filter_sql( $bits, "$bbdb->topics AS t" ); 535 return $this->request; 536 } 537 538 function generate_post_sql( $_part_of_topic_query = false ) { 539 global $bbdb; 540 541 $q =& $this->query_vars; 542 $distinct = ''; 543 $sql_calc_found_rows = 'found_rows' === $q['count'] ? 'SQL_CALC_FOUND_ROWS' : ''; // unfiltered 544 $fields = 'p.*'; 545 $index_hint = ''; 546 $join = ''; 547 $where = ''; 548 $group_by = ''; 549 $having = ''; 550 $order_by = ''; 551 552 $topic_where = ''; 553 $topic_queries = array( 'topic_author_id', 'topic_author', 'topic_status', 'post_count', 'tag_count', 'started', 'updated', 'open', 'sticky', 'meta_key', 'meta_value', 'topic_title' ); 554 if ( !$_part_of_topic_query && array_diff($topic_queries, $this->not_set) ) : 555 $join .= " JOIN $bbdb->topics as t ON ( t.topic_id = p.topic_id )"; 556 $topic_where = $this->generate_topic_sql( true ); 557 endif; 558 559 if ( !$_part_of_topic_query ) : 560 if ( $q['post_id'] ) 561 $where .= $this->parse_value( 'p.post_id', $q['post_id'] ); 562 563 if ( $q['topic_id'] ) : 564 $where .= $this->parse_value( 'p.topic_id', $q['topic_id'] ); 565 elseif ( $q['topic'] ) : 566 if ( !$q['topic_id'] = bb_get_id_from_slug( 'topic', $q['topic'] ) ) 567 $this->error( 'query_var:topic', 'No topic by that name' ); 568 $where .= " AND p.topic_id = " . $q['topic_id']; 569 endif; 570 571 if ( $q['forum_id'] ) : 572 $where .= $this->parse_value( 'p.forum_id', $q['forum_id'] ); 573 elseif ( $q['forum'] ) : 574 if ( !$q['forum_id'] = bb_get_id_from_slug( 'forum', $q['forum'] ) ) 575 $this->error( 'query_var:forum', 'No forum by that name' ); 576 $where .= " AND p.forum_id = " . $q['forum_id']; 577 endif; 578 579 if ( $q['tag'] && !is_int($q['tag_id']) ) 580 $q['tag_id'] = (int) bb_get_tag_id( $q['tag'] ); 581 582 if ( is_numeric($q['tag_id']) ) : 583 $join .= " JOIN `$bbdb->term_relationships` AS tr ON ( p.`topic_id` = tr.`object_id` AND tr.`term_taxonomy_id` = $q[tag_id] )"; 584 endif; 585 586 if ( is_numeric($q['favorites']) && $f_user = bb_get_user( $q['favorites'] ) ) 587 $where .= $this->parse_value( 'p.topic_id', $f_user->favorites ); 588 endif; // !_part_of_topic_query 589 590 if ( $q['post_text'] ) : 591 $where .= ' AND ' . $this->generate_post_text_sql( $q['post_text'] ); 592 if ( $this->match_query ) { 593 $fields .= ", $this->match_query AS search_score"; 594 if ( !$q['order_by'] ) 595 $q['order_by'] = 'search_score'; 596 } else { 597 $fields .= ', 0 AS search_score'; 598 } 599 endif; 600 601 if ( $q['posted'] ) 602 $where .= $this->date( 'p.post_time', $q['posted'] ); 603 604 if ( $q['post_author_id'] ) : 605 $where .= $this->parse_value( 'p.poster_id', $q['post_author_id'] ); 606 elseif ( $q['post_author'] ) : 607 $user = bb_get_user( $q['post_author'], array( 'by' => 'login' ) ); 608 if ( !$q['post_author_id'] = (int) $user->ID ) 609 $this->error( 'query_var:user', 'No user by that name' ); 610 $where .= " AND p.poster_id = $q[post_author_id]"; 611 endif; 612 613 if ( !$q['post_status'] ) : 614 $where .= " AND p.post_status = '0'"; 615 elseif ( false === strpos($q['post_status'], 'all') ) : 616 $stati = array( 'normal' => 0, 'deleted' => 1 ); 617 $q['post_status'] = str_replace(array_keys($stati), array_values($stati), $q['post_status']); 618 $where .= $this->parse_value( 'p.post_status', $q['post_status'] ); 619 endif; 620 621 if ( false !== $q['position'] ) 622 $where .= $this->parse_value( 'p.post_position', $q['position'] ); 623 624 if ( false !== $q['poster_ip'] ) 625 $where .= " AND poster_ip = '" . $q['poster_ip'] . "'"; 626 627 // Just getting post part for inclusion in topic query 628 if ( $_part_of_topic_query ) 629 return $where; 630 631 $where .= $topic_where; 632 633 if ( $where ) // Get rid of initial " AND " (this is pre-filters) 634 $where = substr($where, 5); 635 636 if ( $q['index_hint'] ) 637 $index_hint = $q['index_hint']; 638 639 if ( $q['order_by'] ) 640 $order_by = $q['order_by']; 641 else 642 $order_by = 'p.post_time'; 643 644 $bits = compact( array('distinct', 'sql_calc_found_rows', 'fields', 'index_hint', 'join', 'where', 'group_by', 'having', 'order_by') ); 645 $this->request = $this->_filter_sql( $bits, "$bbdb->posts AS p" ); 646 647 return $this->request; 648 } 649 650 function generate_topic_title_sql( $string ) { 651 global $bbdb; 652 $string = trim($string); 653 654 if ( !preg_match_all('/".*?("|$)|((?<=[\s",+])|^)[^\s",+]+/', $string, $matches) ) { 655 $string = $bbdb->escape($string); 656 return "(t.topic_title LIKE '%$string%')"; 657 } 658 659 $where = ''; 660 661 foreach ( $matches[0] as $match ) { 662 $term = trim($match, "\"\n\r "); 663 $term = $bbdb->escape($term); 664 $where .= " AND t.topic_title LIKE '%$term%'"; 665 } 666 667 if ( count($matches[0]) > 1 && $string != $matches[0][0] ) { 668 $string = $bbdb->escape($string); 669 $where .= " OR t.topic_title LIKE '%$string%'"; 670 } 671 672 return '(' . substr($where, 5) . ')'; 673 } 674 675 function generate_post_text_sql( $string ) { 676 global $bbdb; 677 $string = trim($string); 678 $_string = $bbdb->escape( $string ); 679 if ( strlen($string) < 5 ) 680 return "p.post_text LIKE '%$_string%'"; 681 682 return $this->match_query = "MATCH(p.post_text) AGAINST('$_string')"; 683 } 684 685 function _filter_sql( $bits, $from ) { 686 global $bbdb; 687 688 $q =& $this->query_vars; 689 690 // MySQL 5.1 allows multiple index hints per query - earlier versions only get the first hint 691 if ( $bits['index_hint'] ) { 692 if ( !is_array( $bits['index_hint'] ) ) { 693 $bits['index_hint'] = array( (string) $bits['index_hint'] ); 694 } 695 if ( $bbdb->has_cap( 'index_hint_for_any' ) ) { 696 // 5.1 <= MySQL 697 $_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'; 698 } elseif ( $bbdb->has_cap( 'index_hint_for_join' ) ) { 699 // 5.0 <= MySQL < 5.1 700 $_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'; 701 } else { 702 // MySQL < 5.0 703 $_regex = '/\s*(USE|IGNORE|FORCE)\s+(INDEX|KEY)\s+\(\s*`?[a-z0-9_]+`?(\s*,\s*`?[a-z0-9_]+`?)*\s*\)\s*/i'; 704 } 705 $_index_hint = array(); 706 foreach ( $bits['index_hint'] as $_hint ) { 707 if ( preg_match( $_regex, $_hint ) ) { 708 $_index_hint[] = trim( $_hint ); 709 } 710 } 711 unset( $_regex, $_hint ); 712 if ( $bbdb->has_cap( 'index_hint_lists' ) ) { 713 // 5.1 <= MySQL 714 $bits['index_hint'] = join( ' ', $_index_hint ); 715 } else { 716 // MySQL < 5.1 717 $bits['index_hint'] = isset( $_index_hint[0] ) ? $_index_hint[0] : ''; 718 } 719 unset( $_index_hint ); 720 } 721 722 $q['order'] = strtoupper($q['order']); 723 if ( $q['order'] && in_array($q['order'], array('ASC', 'DESC')) ) 724 $bits['order_by'] .= " $q[order]"; 725 else 726 $bits['order_by'] .= " DESC"; 727 728 $bits['limit'] = ''; 729 730 // When offset and number are provided, skip per_page and limit checks 731 if ( !empty( $q['offset'] ) && !empty( $q['number'] ) ) { 732 $bits['limit'] .= $q['offset'] . ", " . $q['number']; 733 734 // Else proceed as normal 735 } else { 736 if ( !$q['per_page'] ) { 737 $q['per_page'] = (int) bb_get_option( 'page_topics' ); 738 } 739 740 if ( $q['per_page'] > 0 ) { 741 if ( $q['page'] > 1 ) { 742 $bits['limit'] .= $q['per_page'] * ( $q['page'] - 1 ) . ", "; 743 } 744 $bits['limit'] .= $q['per_page']; 745 } 746 } 747 748 $name = "get_{$this->type}s_"; 749 750 // Unfiltered 751 $sql_calc_found_rows = $bits['sql_calc_found_rows']; 752 unset($bits['sql_calc_found_rows']); 753 754 foreach ( $bits as $bit => $value ) { 755 if ( $this->query_id ) 756 $value = apply_filters( "{$this->query_id}_$bit", $value ); 757 $$bit = apply_filters( "$name$bit", $value ); 758 } 759 760 if ( $where ) 761 $where = "WHERE $where"; 762 if ( $group_by ) 763 $group_by = "GROUP BY $group_by"; 764 if ( $having ) 765 $having = "HAVING $having"; 766 if ( $order_by ) 767 $order_by = "ORDER BY $order_by"; 768 if ( $limit ) 769 $limit = "LIMIT $limit"; 770 771 return "SELECT $distinct $sql_calc_found_rows $fields FROM $from $index_hint $join $where $group_by $having $order_by $limit"; 772 } 773 774 function parse_value( $field, $value = '' ) { 775 if ( !$value && !is_numeric($value) ) 776 return ''; 777 778 global $bbdb; 779 780 $op = substr($value, 0, 1); 781 782 // #, =whatever, <#, >#. Cannot do < and > at same time 783 if ( in_array($op, array('<', '=', '>')) ) : 784 $value = substr($value, 1); 785 $value = is_numeric($value) ? (float) $value : $bbdb->escape( $value ); 786 return " AND $field $op '$value'"; 787 elseif ( false === strpos($value, ',') && 'NULL' !== $value && '-NULL' !== $value ) : 788 $value = is_numeric($value) ? (float) $value : $bbdb->escape( $value ); 789 return '-' == $op ? " AND $field != '" . substr($value, 1) . "'" : " AND $field = '$value'"; 790 endif; 791 792 $y = $n = array(); 793 foreach ( explode(',', $value) as $v ) { 794 $v = is_numeric($v) ? (int) $v : $bbdb->escape( $v ); 795 if ( '-' == substr($v, 0, 1) ) 796 if ( $v === '-NULL' ) 797 $not_null_flag = true; 798 else 799 $n[] = substr($v, 1); 800 else 801 if ( $v === 'NULL' ) 802 $null_flag = true; 803 else 804 $y[] = $v; 805 } 806 807 $r = ''; 808 if ( $y ) { 809 $r .= " AND "; 810 if ( $null_flag ) 811 $r .= "("; 812 $r .= "$field IN ('" . join("','", $y) . "')"; 813 if ( $null_flag ) 814 $r .= " OR $field IS NULL)"; 815 } elseif ( $null_flag ) { 816 $r .= " AND $field IS NULL"; 817 } 818 819 if ( $n ) { 820 $r .= " AND "; 821 if ( $not_null_flag ) 822 $r .= "("; 823 $r .= "$field NOT IN ('" . join("','", $n) . "')"; 824 if ( $not_null_flag ) 825 $r .= " AND $field IS NOT NULL)"; 826 } elseif ( $not_null_flag ) { 827 $r .= " AND $field IS NOT NULL"; 828 } 829 830 return $r; 831 } 832 833 function date( $field, $date ) { 834 if ( !$date && !is_int($date) ) 835 return ''; 836 837 if ( $is_range = false !== strpos( $date, '--' ) ) 838 $dates = explode( '--', $date, 2 ); 839 else 840 $dates = array( $date ); 841 842 $op = false; 843 $r = ''; 844 foreach ( $dates as $date ) { 845 if ( $is_range ) { 846 $op = $op ? '<' : '>'; 847 $date = (int) substr($date, 0, 14); 848 } else { 849 $op = substr($date, 0, 1); 850 if ( !in_array($op, array('>', '<')) ) 851 break; 852 $date = (int) substr($date, 1, 14); 853 } 854 if ( strlen($date) < 14 ) 855 $date .= str_repeat('0', 14 - strlen($date)); 856 $r .= " AND $field $op $date"; 857 } 858 if ( $r ) 859 return $r; 860 861 $date = (int) $date; 862 $r = " AND YEAR($field) = " . substr($date, 0, 4); 863 if ( strlen($date) > 5 ) 864 $r .= " AND MONTH($field) = " . substr($date, 4, 2); 865 if ( strlen($date) > 7 ) 866 $r .= " AND DAYOFMONTH($field) = " . substr($date, 6, 2); 867 if ( strlen($date) > 9 ) 868 $r .= " AND HOUR($field) = " . substr($date, 8, 2); 869 if ( strlen($date) > 11 ) 870 $r .= " AND MINUTE($field) = " . substr($date, 10, 2); 871 if ( strlen($date) > 13 ) 872 $r .= " AND SECOND($field) = " . substr($date, 12, 2); 873 return $r; 874 } 875 876 function error( $code, $message ) { 877 if ( is_wp_error($this->errors) ) 878 $this->errors->add( $code, $message ); 879 else 880 $this->errors = new WP_Error( $code, $message ); 881 } 882 } 883 884 class BB_Query_Form extends BB_Query { 885 var $defaults; 886 var $allowed; 887 888 // Can optionally pass unique id string to help out filters 889 function __construct( $type = 'topic', $defaults = '', $allowed = '', $id = '' ) { 890 $this->defaults = wp_parse_args( $defaults ); 891 $this->allowed = wp_parse_args( $allowed ); 892 if ( !empty($defaults) || !empty($allowed) ) 893 $this->query_from_env($type, $defaults, $allowed, $id); 894 } 895 896 function BB_Query_Form( $type = 'topic', $defaults = '', $allowed = '', $id = '' ) { 897 $this->__construct( $type, $defaults, $allowed, $id ); 898 } 899 900 function form( $args = null ) { 901 $_post = 'post' == $this->type; 902 903 $defaults = array( 904 'search' => true, 905 'forum' => true, 906 'tag' => false, 907 'open' => false, 908 'topic_author' => false, 909 'post_author' => false, 910 'topic_status' => false, 911 'post_status' => false, 912 'topic_title' => false, 913 'poster_ip' => false, 914 915 'method' => 'get', 916 'submit' => __('Search »'), 917 'action' => '' 918 ); 919 $defaults['id'] = $_post ? 'post-search-form' : 'topic-search-form'; 920 921 $args = wp_parse_args( $args, $defaults ); 922 extract( $args, EXTR_SKIP ); 923 924 $id = esc_attr( $id ); 925 $method = 'get' == strtolower($method) ? 'get' : 'post'; 926 $submit = esc_attr( $submit ); 927 if ( !$action = esc_url( $action ) ) 928 $action = ''; 929 930 if ( $this->query_vars ) 931 $query_vars =& $this->query_vars; 932 else 933 $query_vars = $this->fill_query_vars( $this->defaults ); 934 935 extract($query_vars, EXTR_PREFIX_ALL, 'q'); 936 937 $r = "<form action='$action' method='$method' id='$id' class='search-form'>\n"; 938 939 $r .= "\t<fieldset>\n"; 940 941 if ( $search ) { 942 if ( $_post ) { 943 $s_value = esc_attr( $q_post_text ); 944 $s_name = 'post_text'; 945 $s_id = 'post-text'; 946 } else { 947 $s_value = esc_attr( $q_search ); 948 $s_name = $s_id = 'search'; 949 } 950 $r .= "\t<div><label for=\"$s_id\">" . __('Search term') . "</label>\n"; 951 $r .= "\t\t<div><input name='$s_name' id='$s_id' type='text' class='text-input' value='$s_value' /></div>\n"; 952 $r .= "\t</div>\n\n"; 953 } 954 955 if ( $forum ) { 956 $r .= "\t<div><label for=\"forum-id\">" . __('Forum') . "</label>\n"; 957 $r .= "\t\t<div>" . bb_get_forum_dropdown( array( 'selected' => $q_forum_id, 'none' => __('Any') ) ) . "</div>\n"; 958 $r .= "\t</div>\n\n"; 959 } 960 961 if ( $tag ) { 962 $q_tag = esc_attr( $q_tag ); 963 $r .= "\t<div><label for=\"topic-tag\">" . __('Tag') . "</label>\n"; 964 $r .= "\t\t<div><input name='tag' id='topic-tag' type='text' class='text-input' value='$q_tag' /></div>\n"; 965 $r .= "\t</div>\n\n"; 966 } 967 968 if ( $topic_author ) { 969 $q_topic_author = esc_attr( $q_topic_author ); 970 $r .= "\t<div><label for=\"topic-author\">" . __('Topic author') . "</label>\n"; 971 $r .= "\t\t<div><input name='topic_author' id='topic-author' type='text' class='text-input' value='$q_topic_author' /></div>\n"; 972 $r .= "\t</div>\n\n"; 973 } 974 975 if ( $post_author ) { 976 $q_post_author = esc_attr( $q_post_author ); 977 $r .= "\t<div><label for=\"post-author\">" . __('Post author') . "</label>\n"; 978 $r .= "\t\t<div><input name='post_author' id='post-author' type='text' class='text-input' value='$q_post_author' /></div>\n"; 979 $r .= "\t</div>\n\n"; 980 } 981 982 $stati = apply_filters( 'bb_query_form_post_status', array( 'all' => _x( 'All', 'post status' ), '0' => __('Normal'), '1' => __('Deleted') ), $this->type ); 983 984 if ( $topic_status ) { 985 $r .= "\t<div><label for=\"topic-status\">" . __('Topic status') . "</label>\n"; 986 $r .= "\t\t<div><select name='topic_status' id='topic-status'>\n"; 987 foreach ( $stati as $status => $label ) { 988 $selected = (string) $status == (string) $q_topic_status ? " selected='selected'" : ''; 989 $r .= "\t\t\t<option value='$status'$selected>$label</option>\n"; 990 } 991 $r .= "\t\t</select></div>\n"; 992 $r .= "\t</div>\n\n"; 993 } 994 995 if ( $post_status ) { 996 $r .= "\t<div><label for=\"post-status\">" . __('Post status') . "</label>\n"; 997 $r .= "\t\t<div><select name='post_status' id='post-status'>\n"; 998 foreach ( $stati as $status => $label ) { 999 $selected = (string) $status == (string) $q_post_status ? " selected='selected'" : ''; 1000 $r .= "\t\t\t<option value='$status'$selected>$label</option>\n"; 1001 } 1002 $r .= "\t\t</select></div>\n"; 1003 $r .= "\t</div>\n\n"; 1004 } 1005 1006 if ( $poster_ip ) { 1007 $r .= "\t<div><label for=\"poster-ip\">" . __('Poster IP address') . "</label>\n"; 1008 $r .= "\t\t<div><input name='poster_ip' id='poster-ip' type='text' class='text-input' value='$q_poster_ip' /></div>\n"; 1009 $r .= "\t</div>\n\n"; 1010 } 1011 1012 if ( $open ) { 1013 $r .= "\t<div><label for=\"topic-open\">" . __('Open?') . "</label>\n"; 1014 $r .= "\t\t<div><select name='open' id='topic-open'>\n"; 1015 foreach ( array( 'all' => _x( 'All', 'posting status' ), '1' => _x( 'Open', 'posting status' ), '0' => __('Closed') ) as $status => $label ) { 1016 $label = esc_html( $label ); 1017 $selected = (string) $status == (string) $q_open ? " selected='selected'" : ''; 1018 $r .= "\t\t\t<option value='$status'$selected>$label</option>\n"; 1019 } 1020 $r .= "\t\t</select></div>\n"; 1021 $r .= "\t</div>\n\n"; 1022 } 1023 1024 if ( $topic_title ) { 1025 $q_topic_title = esc_attr( $q_topic_title ); 1026 $r .= "\t<div><label for=\"topic-title\">" . __('Title') . "</label>\n"; 1027 $r .= "\t\t<div><input name='topic_title' id='topic-title' type='text' class='text-input' value='$q_topic_title' /></div>\n"; 1028 $r .= "\t</div>\n\n"; 1029 } 1030 1031 $r .= apply_filters( 'bb_query_form_inputs', '', $args, $query_vars ); 1032 1033 $r .= "\t<div class=\"submit\"><label for=\"$id-submit\">" . __('Search') . "</label>\n"; 1034 $r .= "\t\t<div><input type='submit' class='button submit-input' value='$submit' id='$id-submit' /></div>\n"; 1035 $r .= "\t</div>\n"; 1036 1037 $r .= "\t</fieldset>\n\n"; 1038 1039 do_action( 'bb_query_form', $args, $query_vars ); 1040 1041 $r .= "</form>\n\n"; 1042 1043 echo $r; 1044 } 1045 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Dec 7 01:01:35 2017 | Cross-referenced by PHPXref 0.7.1 |