Changeset 16099
- Timestamp:
- 10/30/2010 07:02:06 AM (14 years ago)
- Location:
- trunk
- Files:
-
- 4 edited
- 4 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/wp-admin/setup-config.php
r16066 r16099 43 43 require_once(ABSPATH . WPINC . '/compat.php'); 44 44 require_once(ABSPATH . WPINC . '/functions.php'); 45 require_once(ABSPATH . WPINC . '/class es.php');45 require_once(ABSPATH . WPINC . '/class-wp-error.php'); 46 46 require_once(ABSPATH . WPINC . '/version.php'); 47 47 -
trunk/wp-includes/class-wp-ajax-response.php
r16091 r16099 10 10 * @package WordPress 11 11 */ 12 13 /**14 * WordPress environment setup class.15 *16 * @package WordPress17 * @since 2.0.018 */19 class WP {20 /**21 * Public query variables.22 *23 * Long list of public query variables.24 *25 * @since 2.0.026 * @access public27 * @var array28 */29 var $public_query_vars = array('m', 'p', 'posts', 'w', 'cat', 'withcomments', 'withoutcomments', 's', 'search', 'exact', 'sentence', 'debug', 'calendar', 'page', 'paged', 'more', 'tb', 'pb', 'author', 'order', 'orderby', 'year', 'monthnum', 'day', 'hour', 'minute', 'second', 'name', 'category_name', 'tag', 'feed', 'author_name', 'static', 'pagename', 'page_id', 'error', 'comments_popup', 'attachment', 'attachment_id', 'subpost', 'subpost_id', 'preview', 'robots', 'taxonomy', 'term', 'cpage', 'post_type');30 31 /**32 * Private query variables.33 *34 * Long list of private query variables.35 *36 * @since 2.0.037 * @var array38 */39 var $private_query_vars = array('offset', 'posts_per_page', 'posts_per_archive_page', 'showposts', 'nopaging', 'post_type', 'post_status', 'category__in', 'category__not_in', 'category__and', 'tag__in', 'tag__not_in', 'tag__and', 'tag_slug__in', 'tag_slug__and', 'tag_id', 'post_mime_type', 'perm', 'comments_per_page', 'post__in', 'post__not_in');40 41 /**42 * Extra query variables set by the user.43 *44 * @since 2.1.045 * @var array46 */47 var $extra_query_vars = array();48 49 /**50 * Query variables for setting up the WordPress Query Loop.51 *52 * @since 2.0.053 * @var array54 */55 var $query_vars;56 57 /**58 * String parsed to set the query variables.59 *60 * @since 2.0.061 * @var string62 */63 var $query_string;64 65 /**66 * Permalink or requested URI.67 *68 * @since 2.0.069 * @var string70 */71 var $request;72 73 /**74 * Rewrite rule the request matched.75 *76 * @since 2.0.077 * @var string78 */79 var $matched_rule;80 81 /**82 * Rewrite query the request matched.83 *84 * @since 2.0.085 * @var string86 */87 var $matched_query;88 89 /**90 * Whether already did the permalink.91 *92 * @since 2.0.093 * @var bool94 */95 var $did_permalink = false;96 97 /**98 * Add name to list of public query variables.99 *100 * @since 2.1.0101 *102 * @param string $qv Query variable name.103 */104 function add_query_var($qv) {105 if ( !in_array($qv, $this->public_query_vars) )106 $this->public_query_vars[] = $qv;107 }108 109 /**110 * Set the value of a query variable.111 *112 * @since 2.3.0113 *114 * @param string $key Query variable name.115 * @param mixed $value Query variable value.116 */117 function set_query_var($key, $value) {118 $this->query_vars[$key] = $value;119 }120 121 /**122 * Parse request to find correct WordPress query.123 *124 * Sets up the query variables based on the request. There are also many125 * filters and actions that can be used to further manipulate the result.126 *127 * @since 2.0.0128 *129 * @param array|string $extra_query_vars Set the extra query variables.130 */131 function parse_request($extra_query_vars = '') {132 global $wp_rewrite;133 134 $this->query_vars = array();135 $post_type_query_vars = array();136 137 if ( is_array($extra_query_vars) )138 $this->extra_query_vars = & $extra_query_vars;139 else if (! empty($extra_query_vars))140 parse_str($extra_query_vars, $this->extra_query_vars);141 142 // Process PATH_INFO, REQUEST_URI, and 404 for permalinks.143 144 // Fetch the rewrite rules.145 $rewrite = $wp_rewrite->wp_rewrite_rules();146 147 if ( ! empty($rewrite) ) {148 // If we match a rewrite rule, this will be cleared.149 $error = '404';150 $this->did_permalink = true;151 152 if ( isset($_SERVER['PATH_INFO']) )153 $pathinfo = $_SERVER['PATH_INFO'];154 else155 $pathinfo = '';156 $pathinfo_array = explode('?', $pathinfo);157 $pathinfo = str_replace("%", "%25", $pathinfo_array[0]);158 $req_uri = $_SERVER['REQUEST_URI'];159 $req_uri_array = explode('?', $req_uri);160 $req_uri = $req_uri_array[0];161 $self = $_SERVER['PHP_SELF'];162 $home_path = parse_url(home_url());163 if ( isset($home_path['path']) )164 $home_path = $home_path['path'];165 else166 $home_path = '';167 $home_path = trim($home_path, '/');168 169 // Trim path info from the end and the leading home path from the170 // front. For path info requests, this leaves us with the requesting171 // filename, if any. For 404 requests, this leaves us with the172 // requested permalink.173 $req_uri = str_replace($pathinfo, '', $req_uri);174 $req_uri = trim($req_uri, '/');175 $req_uri = preg_replace("|^$home_path|", '', $req_uri);176 $req_uri = trim($req_uri, '/');177 $pathinfo = trim($pathinfo, '/');178 $pathinfo = preg_replace("|^$home_path|", '', $pathinfo);179 $pathinfo = trim($pathinfo, '/');180 $self = trim($self, '/');181 $self = preg_replace("|^$home_path|", '', $self);182 $self = trim($self, '/');183 184 // The requested permalink is in $pathinfo for path info requests and185 // $req_uri for other requests.186 if ( ! empty($pathinfo) && !preg_match('|^.*' . $wp_rewrite->index . '$|', $pathinfo) ) {187 $request = $pathinfo;188 } else {189 // If the request uri is the index, blank it out so that we don't try to match it against a rule.190 if ( $req_uri == $wp_rewrite->index )191 $req_uri = '';192 $request = $req_uri;193 }194 195 $this->request = $request;196 197 // Look for matches.198 $request_match = $request;199 foreach ( (array) $rewrite as $match => $query) {200 // Don't try to match against AtomPub calls201 if ( $req_uri == 'wp-app.php' )202 break;203 204 // If the requesting file is the anchor of the match, prepend it205 // to the path info.206 if ( (! empty($req_uri)) && (strpos($match, $req_uri) === 0) && ($req_uri != $request) )207 $request_match = $req_uri . '/' . $request;208 209 if ( preg_match("#^$match#", $request_match, $matches) ||210 preg_match("#^$match#", urldecode($request_match), $matches) ) {211 // Got a match.212 $this->matched_rule = $match;213 214 // Trim the query of everything up to the '?'.215 $query = preg_replace("!^.+\?!", '', $query);216 217 // Substitute the substring matches into the query.218 $query = addslashes(WP_MatchesMapRegex::apply($query, $matches));219 220 $this->matched_query = $query;221 222 // Parse the query.223 parse_str($query, $perma_query_vars);224 225 // If we're processing a 404 request, clear the error var226 // since we found something.227 if ( isset($_GET['error']) )228 unset($_GET['error']);229 230 if ( isset($error) )231 unset($error);232 233 break;234 }235 }236 237 // If req_uri is empty or if it is a request for ourself, unset error.238 if ( empty($request) || $req_uri == $self || strpos($_SERVER['PHP_SELF'], 'wp-admin/') !== false ) {239 if ( isset($_GET['error']) )240 unset($_GET['error']);241 242 if ( isset($error) )243 unset($error);244 245 if ( isset($perma_query_vars) && strpos($_SERVER['PHP_SELF'], 'wp-admin/') !== false )246 unset($perma_query_vars);247 248 $this->did_permalink = false;249 }250 }251 252 $this->public_query_vars = apply_filters('query_vars', $this->public_query_vars);253 254 foreach ( $GLOBALS['wp_post_types'] as $post_type => $t )255 if ( $t->query_var )256 $post_type_query_vars[$t->query_var] = $post_type;257 258 foreach ( $this->public_query_vars as $wpvar ) {259 if ( isset( $this->extra_query_vars[$wpvar] ) )260 $this->query_vars[$wpvar] = $this->extra_query_vars[$wpvar];261 elseif ( isset( $_POST[$wpvar] ) )262 $this->query_vars[$wpvar] = $_POST[$wpvar];263 elseif ( isset( $_GET[$wpvar] ) )264 $this->query_vars[$wpvar] = $_GET[$wpvar];265 elseif ( isset( $perma_query_vars[$wpvar] ) )266 $this->query_vars[$wpvar] = $perma_query_vars[$wpvar];267 268 if ( !empty( $this->query_vars[$wpvar] ) ) {269 if ( ! is_array( $this->query_vars[$wpvar] ) ) {270 $this->query_vars[$wpvar] = (string) $this->query_vars[$wpvar];271 } else {272 foreach ( $this->query_vars[$wpvar] as $vkey => $v ) {273 if ( !is_object( $v ) ) {274 $this->query_vars[$wpvar][$vkey] = (string) $v;275 }276 }277 }278 279 if ( isset($post_type_query_vars[$wpvar] ) ) {280 $this->query_vars['post_type'] = $post_type_query_vars[$wpvar];281 $this->query_vars['name'] = $this->query_vars[$wpvar];282 }283 }284 }285 286 // Limit publicly queried post_types to those that are publicly_queryable287 if ( isset( $this->query_vars['post_type']) ) {288 $queryable_post_types = get_post_types( array('publicly_queryable' => true) );289 if ( ! is_array( $this->query_vars['post_type'] ) ) {290 if ( ! in_array( $this->query_vars['post_type'], $queryable_post_types ) )291 unset( $this->query_vars['post_type'] );292 } else {293 $this->query_vars['post_type'] = array_intersect( $this->query_vars['post_type'], $queryable_post_types );294 }295 }296 297 foreach ( (array) $this->private_query_vars as $var) {298 if ( isset($this->extra_query_vars[$var]) )299 $this->query_vars[$var] = $this->extra_query_vars[$var];300 }301 302 if ( isset($error) )303 $this->query_vars['error'] = $error;304 305 $this->query_vars = apply_filters('request', $this->query_vars);306 307 do_action_ref_array('parse_request', array(&$this));308 }309 310 /**311 * Send additional HTTP headers for caching, content type, etc.312 *313 * Sets the X-Pingback header, 404 status (if 404), Content-type. If showing314 * a feed, it will also send last-modified, etag, and 304 status if needed.315 *316 * @since 2.0.0317 */318 function send_headers() {319 $headers = array('X-Pingback' => get_bloginfo('pingback_url'));320 $status = null;321 $exit_required = false;322 323 if ( is_user_logged_in() )324 $headers = array_merge($headers, wp_get_nocache_headers());325 if ( !empty($this->query_vars['error']) && '404' == $this->query_vars['error'] ) {326 $status = 404;327 if ( !is_user_logged_in() )328 $headers = array_merge($headers, wp_get_nocache_headers());329 $headers['Content-Type'] = get_option('html_type') . '; charset=' . get_option('blog_charset');330 } else if ( empty($this->query_vars['feed']) ) {331 $headers['Content-Type'] = get_option('html_type') . '; charset=' . get_option('blog_charset');332 } else {333 // We're showing a feed, so WP is indeed the only thing that last changed334 if ( !empty($this->query_vars['withcomments'])335 || ( empty($this->query_vars['withoutcomments'])336 && ( !empty($this->query_vars['p'])337 || !empty($this->query_vars['name'])338 || !empty($this->query_vars['page_id'])339 || !empty($this->query_vars['pagename'])340 || !empty($this->query_vars['attachment'])341 || !empty($this->query_vars['attachment_id'])342 )343 )344 )345 $wp_last_modified = mysql2date('D, d M Y H:i:s', get_lastcommentmodified('GMT'), 0).' GMT';346 else347 $wp_last_modified = mysql2date('D, d M Y H:i:s', get_lastpostmodified('GMT'), 0).' GMT';348 $wp_etag = '"' . md5($wp_last_modified) . '"';349 $headers['Last-Modified'] = $wp_last_modified;350 $headers['ETag'] = $wp_etag;351 352 // Support for Conditional GET353 if (isset($_SERVER['HTTP_IF_NONE_MATCH']))354 $client_etag = stripslashes(stripslashes($_SERVER['HTTP_IF_NONE_MATCH']));355 else $client_etag = false;356 357 $client_last_modified = empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? '' : trim($_SERVER['HTTP_IF_MODIFIED_SINCE']);358 // If string is empty, return 0. If not, attempt to parse into a timestamp359 $client_modified_timestamp = $client_last_modified ? strtotime($client_last_modified) : 0;360 361 // Make a timestamp for our most recent modification...362 $wp_modified_timestamp = strtotime($wp_last_modified);363 364 if ( ($client_last_modified && $client_etag) ?365 (($client_modified_timestamp >= $wp_modified_timestamp) && ($client_etag == $wp_etag)) :366 (($client_modified_timestamp >= $wp_modified_timestamp) || ($client_etag == $wp_etag)) ) {367 $status = 304;368 $exit_required = true;369 }370 }371 372 $headers = apply_filters('wp_headers', $headers, $this);373 374 if ( ! empty( $status ) )375 status_header( $status );376 foreach( (array) $headers as $name => $field_value )377 @header("{$name}: {$field_value}");378 379 if ( $exit_required )380 exit();381 382 do_action_ref_array('send_headers', array(&$this));383 }384 385 /**386 * Sets the query string property based off of the query variable property.387 *388 * The 'query_string' filter is deprecated, but still works. Plugins should389 * use the 'request' filter instead.390 *391 * @since 2.0.0392 */393 function build_query_string() {394 $this->query_string = '';395 foreach ( (array) array_keys($this->query_vars) as $wpvar) {396 if ( '' != $this->query_vars[$wpvar] ) {397 $this->query_string .= (strlen($this->query_string) < 1) ? '' : '&';398 if ( !is_scalar($this->query_vars[$wpvar]) ) // Discard non-scalars.399 continue;400 $this->query_string .= $wpvar . '=' . rawurlencode($this->query_vars[$wpvar]);401 }402 }403 404 // query_string filter deprecated. Use request filter instead.405 if ( has_filter('query_string') ) { // Don't bother filtering and parsing if no plugins are hooked in.406 $this->query_string = apply_filters('query_string', $this->query_string);407 parse_str($this->query_string, $this->query_vars);408 }409 }410 411 /**412 * Set up the WordPress Globals.413 *414 * The query_vars property will be extracted to the GLOBALS. So care should415 * be taken when naming global variables that might interfere with the416 * WordPress environment.417 *418 * @global string $query_string Query string for the loop.419 * @global int $more Only set, if single page or post.420 * @global int $single If single page or post. Only set, if single page or post.421 *422 * @since 2.0.0423 */424 function register_globals() {425 global $wp_query;426 // Extract updated query vars back into global namespace.427 foreach ( (array) $wp_query->query_vars as $key => $value) {428 $GLOBALS[$key] = $value;429 }430 431 $GLOBALS['query_string'] = $this->query_string;432 $GLOBALS['posts'] = & $wp_query->posts;433 $GLOBALS['post'] = (isset($wp_query->post)) ? $wp_query->post : null;434 $GLOBALS['request'] = $wp_query->request;435 436 if ( is_single() || is_page() ) {437 $GLOBALS['more'] = 1;438 $GLOBALS['single'] = 1;439 }440 }441 442 /**443 * Set up the current user.444 *445 * @since 2.0.0446 */447 function init() {448 wp_get_current_user();449 }450 451 /**452 * Set up the Loop based on the query variables.453 *454 * @uses WP::$query_vars455 * @since 2.0.0456 */457 function query_posts() {458 global $wp_the_query;459 $this->build_query_string();460 $wp_the_query->query($this->query_vars);461 }462 463 /**464 * Set the Headers for 404, if nothing is found for requested URL.465 *466 * Issue a 404 if a request doesn't match any posts and doesn't match467 * any object (e.g. an existing-but-empty category, tag, author) and a 404 was not already468 * issued, and if the request was not a search or the homepage.469 *470 * Otherwise, issue a 200.471 *472 * @since 2.0.0473 */474 function handle_404() {475 global $wp_query;476 477 if ( !is_admin() && ( 0 == count( $wp_query->posts ) ) && !is_404() && !is_robots() && !is_search() && !is_home() ) {478 // Don't 404 for these queries if they matched an object.479 if ( ( is_tag() || is_category() || is_tax() || is_author() ) && $wp_query->get_queried_object() && !is_paged() ) {480 if ( !is_404() )481 status_header( 200 );482 return;483 }484 $wp_query->set_404();485 status_header( 404 );486 nocache_headers();487 } elseif ( !is_404() ) {488 status_header( 200 );489 }490 }491 492 /**493 * Sets up all of the variables required by the WordPress environment.494 *495 * The action 'wp' has one parameter that references the WP object. It496 * allows for accessing the properties and methods to further manipulate the497 * object.498 *499 * @since 2.0.0500 *501 * @param string|array $query_args Passed to {@link parse_request()}502 */503 function main($query_args = '') {504 $this->init();505 $this->parse_request($query_args);506 $this->send_headers();507 $this->query_posts();508 $this->handle_404();509 $this->register_globals();510 do_action_ref_array('wp', array(&$this));511 }512 513 /**514 * PHP4 Constructor - Does nothing.515 *516 * Call main() method when ready to run setup.517 *518 * @since 2.0.0519 *520 * @return WP521 */522 function WP() {523 // Empty.524 }525 }526 527 /**528 * WordPress Error class.529 *530 * Container for checking for WordPress errors and error messages. Return531 * WP_Error and use {@link is_wp_error()} to check if this class is returned.532 * Many core WordPress functions pass this class in the event of an error and533 * if not handled properly will result in code errors.534 *535 * @package WordPress536 * @since 2.1.0537 */538 class WP_Error {539 /**540 * Stores the list of errors.541 *542 * @since 2.1.0543 * @var array544 * @access private545 */546 var $errors = array();547 548 /**549 * Stores the list of data for error codes.550 *551 * @since 2.1.0552 * @var array553 * @access private554 */555 var $error_data = array();556 557 /**558 * PHP4 Constructor - Sets up error message.559 *560 * If code parameter is empty then nothing will be done. It is possible to561 * add multiple messages to the same code, but with other methods in the562 * class.563 *564 * All parameters are optional, but if the code parameter is set, then the565 * data parameter is optional.566 *567 * @since 2.1.0568 *569 * @param string|int $code Error code570 * @param string $message Error message571 * @param mixed $data Optional. Error data.572 * @return WP_Error573 */574 function WP_Error($code = '', $message = '', $data = '') {575 if ( empty($code) )576 return;577 578 $this->errors[$code][] = $message;579 580 if ( ! empty($data) )581 $this->error_data[$code] = $data;582 }583 584 /**585 * Retrieve all error codes.586 *587 * @since 2.1.0588 * @access public589 *590 * @return array List of error codes, if avaiable.591 */592 function get_error_codes() {593 if ( empty($this->errors) )594 return array();595 596 return array_keys($this->errors);597 }598 599 /**600 * Retrieve first error code available.601 *602 * @since 2.1.0603 * @access public604 *605 * @return string|int Empty string, if no error codes.606 */607 function get_error_code() {608 $codes = $this->get_error_codes();609 610 if ( empty($codes) )611 return '';612 613 return $codes[0];614 }615 616 /**617 * Retrieve all error messages or error messages matching code.618 *619 * @since 2.1.0620 *621 * @param string|int $code Optional. Retrieve messages matching code, if exists.622 * @return array Error strings on success, or empty array on failure (if using codee parameter).623 */624 function get_error_messages($code = '') {625 // Return all messages if no code specified.626 if ( empty($code) ) {627 $all_messages = array();628 foreach ( (array) $this->errors as $code => $messages )629 $all_messages = array_merge($all_messages, $messages);630 631 return $all_messages;632 }633 634 if ( isset($this->errors[$code]) )635 return $this->errors[$code];636 else637 return array();638 }639 640 /**641 * Get single error message.642 *643 * This will get the first message available for the code. If no code is644 * given then the first code available will be used.645 *646 * @since 2.1.0647 *648 * @param string|int $code Optional. Error code to retrieve message.649 * @return string650 */651 function get_error_message($code = '') {652 if ( empty($code) )653 $code = $this->get_error_code();654 $messages = $this->get_error_messages($code);655 if ( empty($messages) )656 return '';657 return $messages[0];658 }659 660 /**661 * Retrieve error data for error code.662 *663 * @since 2.1.0664 *665 * @param string|int $code Optional. Error code.666 * @return mixed Null, if no errors.667 */668 function get_error_data($code = '') {669 if ( empty($code) )670 $code = $this->get_error_code();671 672 if ( isset($this->error_data[$code]) )673 return $this->error_data[$code];674 return null;675 }676 677 /**678 * Append more error messages to list of error messages.679 *680 * @since 2.1.0681 * @access public682 *683 * @param string|int $code Error code.684 * @param string $message Error message.685 * @param mixed $data Optional. Error data.686 */687 function add($code, $message, $data = '') {688 $this->errors[$code][] = $message;689 if ( ! empty($data) )690 $this->error_data[$code] = $data;691 }692 693 /**694 * Add data for error code.695 *696 * The error code can only contain one error data.697 *698 * @since 2.1.0699 *700 * @param mixed $data Error data.701 * @param string|int $code Error code.702 */703 function add_data($data, $code = '') {704 if ( empty($code) )705 $code = $this->get_error_code();706 707 $this->error_data[$code] = $data;708 }709 }710 711 /**712 * Check whether variable is a WordPress Error.713 *714 * Looks at the object and if a WP_Error class. Does not check to see if the715 * parent is also WP_Error, so can't inherit WP_Error and still use this716 * function.717 *718 * @since 2.1.0719 *720 * @param mixed $thing Check if unknown variable is WordPress Error object.721 * @return bool True, if WP_Error. False, if not WP_Error.722 */723 function is_wp_error($thing) {724 if ( is_object($thing) && is_a($thing, 'WP_Error') )725 return true;726 return false;727 }728 729 /**730 * A class for displaying various tree-like structures.731 *732 * Extend the Walker class to use it, see examples at the below. Child classes733 * do not need to implement all of the abstract methods in the class. The child734 * only needs to implement the methods that are needed. Also, the methods are735 * not strictly abstract in that the parameter definition needs to be followed.736 * The child classes can have additional parameters.737 *738 * @package WordPress739 * @since 2.1.0740 * @abstract741 */742 class Walker {743 /**744 * What the class handles.745 *746 * @since 2.1.0747 * @var string748 * @access public749 */750 var $tree_type;751 752 /**753 * DB fields to use.754 *755 * @since 2.1.0756 * @var array757 * @access protected758 */759 var $db_fields;760 761 /**762 * Max number of pages walked by the paged walker763 *764 * @since 2.7.0765 * @var int766 * @access protected767 */768 var $max_pages = 1;769 770 /**771 * Starts the list before the elements are added.772 *773 * Additional parameters are used in child classes. The args parameter holds774 * additional values that may be used with the child class methods. This775 * method is called at the start of the output list.776 *777 * @since 2.1.0778 * @abstract779 *780 * @param string $output Passed by reference. Used to append additional content.781 */782 function start_lvl(&$output) {}783 784 /**785 * Ends the list of after the elements are added.786 *787 * Additional parameters are used in child classes. The args parameter holds788 * additional values that may be used with the child class methods. This789 * method finishes the list at the end of output of the elements.790 *791 * @since 2.1.0792 * @abstract793 *794 * @param string $output Passed by reference. Used to append additional content.795 */796 function end_lvl(&$output) {}797 798 /**799 * Start the element output.800 *801 * Additional parameters are used in child classes. The args parameter holds802 * additional values that may be used with the child class methods. Includes803 * the element output also.804 *805 * @since 2.1.0806 * @abstract807 *808 * @param string $output Passed by reference. Used to append additional content.809 */810 function start_el(&$output) {}811 812 /**813 * Ends the element output, if needed.814 *815 * Additional parameters are used in child classes. The args parameter holds816 * additional values that may be used with the child class methods.817 *818 * @since 2.1.0819 * @abstract820 *821 * @param string $output Passed by reference. Used to append additional content.822 */823 function end_el(&$output) {}824 825 /**826 * Traverse elements to create list from elements.827 *828 * Display one element if the element doesn't have any children otherwise,829 * display the element and its children. Will only traverse up to the max830 * depth and no ignore elements under that depth. It is possible to set the831 * max depth to include all depths, see walk() method.832 *833 * This method shouldn't be called directly, use the walk() method instead.834 *835 * @since 2.5.0836 *837 * @param object $element Data object838 * @param array $children_elements List of elements to continue traversing.839 * @param int $max_depth Max depth to traverse.840 * @param int $depth Depth of current element.841 * @param array $args842 * @param string $output Passed by reference. Used to append additional content.843 * @return null Null on failure with no changes to parameters.844 */845 function display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output ) {846 847 if ( !$element )848 return;849 850 $id_field = $this->db_fields['id'];851 852 //display this element853 if ( is_array( $args[0] ) )854 $args[0]['has_children'] = ! empty( $children_elements[$element->$id_field] );855 $cb_args = array_merge( array(&$output, $element, $depth), $args);856 call_user_func_array(array(&$this, 'start_el'), $cb_args);857 858 $id = $element->$id_field;859 860 // descend only when the depth is right and there are childrens for this element861 if ( ($max_depth == 0 || $max_depth > $depth+1 ) && isset( $children_elements[$id]) ) {862 863 foreach( $children_elements[ $id ] as $child ){864 865 if ( !isset($newlevel) ) {866 $newlevel = true;867 //start the child delimiter868 $cb_args = array_merge( array(&$output, $depth), $args);869 call_user_func_array(array(&$this, 'start_lvl'), $cb_args);870 }871 $this->display_element( $child, $children_elements, $max_depth, $depth + 1, $args, $output );872 }873 unset( $children_elements[ $id ] );874 }875 876 if ( isset($newlevel) && $newlevel ){877 //end the child delimiter878 $cb_args = array_merge( array(&$output, $depth), $args);879 call_user_func_array(array(&$this, 'end_lvl'), $cb_args);880 }881 882 //end this element883 $cb_args = array_merge( array(&$output, $element, $depth), $args);884 call_user_func_array(array(&$this, 'end_el'), $cb_args);885 }886 887 /**888 * Display array of elements hierarchically.889 *890 * It is a generic function which does not assume any existing order of891 * elements. max_depth = -1 means flatly display every element. max_depth =892 * 0 means display all levels. max_depth > 0 specifies the number of893 * display levels.894 *895 * @since 2.1.0896 *897 * @param array $elements898 * @param int $max_depth899 * @return string900 */901 function walk( $elements, $max_depth) {902 903 $args = array_slice(func_get_args(), 2);904 $output = '';905 906 if ($max_depth < -1) //invalid parameter907 return $output;908 909 if (empty($elements)) //nothing to walk910 return $output;911 912 $id_field = $this->db_fields['id'];913 $parent_field = $this->db_fields['parent'];914 915 // flat display916 if ( -1 == $max_depth ) {917 $empty_array = array();918 foreach ( $elements as $e )919 $this->display_element( $e, $empty_array, 1, 0, $args, $output );920 return $output;921 }922 923 /*924 * need to display in hierarchical order925 * separate elements into two buckets: top level and children elements926 * children_elements is two dimensional array, eg.927 * children_elements[10][] contains all sub-elements whose parent is 10.928 */929 $top_level_elements = array();930 $children_elements = array();931 foreach ( $elements as $e) {932 if ( 0 == $e->$parent_field )933 $top_level_elements[] = $e;934 else935 $children_elements[ $e->$parent_field ][] = $e;936 }937 938 /*939 * when none of the elements is top level940 * assume the first one must be root of the sub elements941 */942 if ( empty($top_level_elements) ) {943 944 $first = array_slice( $elements, 0, 1 );945 $root = $first[0];946 947 $top_level_elements = array();948 $children_elements = array();949 foreach ( $elements as $e) {950 if ( $root->$parent_field == $e->$parent_field )951 $top_level_elements[] = $e;952 else953 $children_elements[ $e->$parent_field ][] = $e;954 }955 }956 957 foreach ( $top_level_elements as $e )958 $this->display_element( $e, $children_elements, $max_depth, 0, $args, $output );959 960 /*961 * if we are displaying all levels, and remaining children_elements is not empty,962 * then we got orphans, which should be displayed regardless963 */964 if ( ( $max_depth == 0 ) && count( $children_elements ) > 0 ) {965 $empty_array = array();966 foreach ( $children_elements as $orphans )967 foreach( $orphans as $op )968 $this->display_element( $op, $empty_array, 1, 0, $args, $output );969 }970 971 return $output;972 }973 974 /**975 * paged_walk() - produce a page of nested elements976 *977 * Given an array of hierarchical elements, the maximum depth, a specific page number,978 * and number of elements per page, this function first determines all top level root elements979 * belonging to that page, then lists them and all of their children in hierarchical order.980 *981 * @package WordPress982 * @since 2.7983 * @param int $max_depth = 0 means display all levels; $max_depth > 0 specifies the number of display levels.984 * @param int $page_num the specific page number, beginning with 1.985 * @return XHTML of the specified page of elements986 */987 function paged_walk( $elements, $max_depth, $page_num, $per_page ) {988 989 /* sanity check */990 if ( empty($elements) || $max_depth < -1 )991 return '';992 993 $args = array_slice( func_get_args(), 4 );994 $output = '';995 996 $id_field = $this->db_fields['id'];997 $parent_field = $this->db_fields['parent'];998 999 $count = -1;1000 if ( -1 == $max_depth )1001 $total_top = count( $elements );1002 if ( $page_num < 1 || $per_page < 0 ) {1003 // No paging1004 $paging = false;1005 $start = 0;1006 if ( -1 == $max_depth )1007 $end = $total_top;1008 $this->max_pages = 1;1009 } else {1010 $paging = true;1011 $start = ( (int)$page_num - 1 ) * (int)$per_page;1012 $end = $start + $per_page;1013 if ( -1 == $max_depth )1014 $this->max_pages = ceil($total_top / $per_page);1015 }1016 1017 // flat display1018 if ( -1 == $max_depth ) {1019 if ( !empty($args[0]['reverse_top_level']) ) {1020 $elements = array_reverse( $elements );1021 $oldstart = $start;1022 $start = $total_top - $end;1023 $end = $total_top - $oldstart;1024 }1025 1026 $empty_array = array();1027 foreach ( $elements as $e ) {1028 $count++;1029 if ( $count < $start )1030 continue;1031 if ( $count >= $end )1032 break;1033 $this->display_element( $e, $empty_array, 1, 0, $args, $output );1034 }1035 return $output;1036 }1037 1038 /*1039 * separate elements into two buckets: top level and children elements1040 * children_elements is two dimensional array, eg.1041 * children_elements[10][] contains all sub-elements whose parent is 10.1042 */1043 $top_level_elements = array();1044 $children_elements = array();1045 foreach ( $elements as $e) {1046 if ( 0 == $e->$parent_field )1047 $top_level_elements[] = $e;1048 else1049 $children_elements[ $e->$parent_field ][] = $e;1050 }1051 1052 $total_top = count( $top_level_elements );1053 if ( $paging )1054 $this->max_pages = ceil($total_top / $per_page);1055 else1056 $end = $total_top;1057 1058 if ( !empty($args[0]['reverse_top_level']) ) {1059 $top_level_elements = array_reverse( $top_level_elements );1060 $oldstart = $start;1061 $start = $total_top - $end;1062 $end = $total_top - $oldstart;1063 }1064 if ( !empty($args[0]['reverse_children']) ) {1065 foreach ( $children_elements as $parent => $children )1066 $children_elements[$parent] = array_reverse( $children );1067 }1068 1069 foreach ( $top_level_elements as $e ) {1070 $count++;1071 1072 //for the last page, need to unset earlier children in order to keep track of orphans1073 if ( $end >= $total_top && $count < $start )1074 $this->unset_children( $e, $children_elements );1075 1076 if ( $count < $start )1077 continue;1078 1079 if ( $count >= $end )1080 break;1081 1082 $this->display_element( $e, $children_elements, $max_depth, 0, $args, $output );1083 }1084 1085 if ( $end >= $total_top && count( $children_elements ) > 0 ) {1086 $empty_array = array();1087 foreach ( $children_elements as $orphans )1088 foreach( $orphans as $op )1089 $this->display_element( $op, $empty_array, 1, 0, $args, $output );1090 }1091 1092 return $output;1093 }1094 1095 function get_number_of_root_elements( $elements ){1096 1097 $num = 0;1098 $parent_field = $this->db_fields['parent'];1099 1100 foreach ( $elements as $e) {1101 if ( 0 == $e->$parent_field )1102 $num++;1103 }1104 return $num;1105 }1106 1107 // unset all the children for a given top level element1108 function unset_children( $e, &$children_elements ){1109 1110 if ( !$e || !$children_elements )1111 return;1112 1113 $id_field = $this->db_fields['id'];1114 $id = $e->$id_field;1115 1116 if ( !empty($children_elements[$id]) && is_array($children_elements[$id]) )1117 foreach ( (array) $children_elements[$id] as $child )1118 $this->unset_children( $child, $children_elements );1119 1120 if ( isset($children_elements[$id]) )1121 unset( $children_elements[$id] );1122 1123 }1124 }1125 1126 /**1127 * Create HTML list of pages.1128 *1129 * @package WordPress1130 * @since 2.1.01131 * @uses Walker1132 */1133 class Walker_Page extends Walker {1134 /**1135 * @see Walker::$tree_type1136 * @since 2.1.01137 * @var string1138 */1139 var $tree_type = 'page';1140 1141 /**1142 * @see Walker::$db_fields1143 * @since 2.1.01144 * @todo Decouple this.1145 * @var array1146 */1147 var $db_fields = array ('parent' => 'post_parent', 'id' => 'ID');1148 1149 /**1150 * @see Walker::start_lvl()1151 * @since 2.1.01152 *1153 * @param string $output Passed by reference. Used to append additional content.1154 * @param int $depth Depth of page. Used for padding.1155 */1156 function start_lvl(&$output, $depth) {1157 $indent = str_repeat("\t", $depth);1158 $output .= "\n$indent<ul class='children'>\n";1159 }1160 1161 /**1162 * @see Walker::end_lvl()1163 * @since 2.1.01164 *1165 * @param string $output Passed by reference. Used to append additional content.1166 * @param int $depth Depth of page. Used for padding.1167 */1168 function end_lvl(&$output, $depth) {1169 $indent = str_repeat("\t", $depth);1170 $output .= "$indent</ul>\n";1171 }1172 1173 /**1174 * @see Walker::start_el()1175 * @since 2.1.01176 *1177 * @param string $output Passed by reference. Used to append additional content.1178 * @param object $page Page data object.1179 * @param int $depth Depth of page. Used for padding.1180 * @param int $current_page Page ID.1181 * @param array $args1182 */1183 function start_el(&$output, $page, $depth, $args, $current_page) {1184 if ( $depth )1185 $indent = str_repeat("\t", $depth);1186 else1187 $indent = '';1188 1189 extract($args, EXTR_SKIP);1190 $css_class = array('page_item', 'page-item-'.$page->ID);1191 if ( !empty($current_page) ) {1192 $_current_page = get_page( $current_page );1193 if ( isset($_current_page->ancestors) && in_array($page->ID, (array) $_current_page->ancestors) )1194 $css_class[] = 'current_page_ancestor';1195 if ( $page->ID == $current_page )1196 $css_class[] = 'current_page_item';1197 elseif ( $_current_page && $page->ID == $_current_page->post_parent )1198 $css_class[] = 'current_page_parent';1199 } elseif ( $page->ID == get_option('page_for_posts') ) {1200 $css_class[] = 'current_page_parent';1201 }1202 1203 $css_class = implode(' ', apply_filters('page_css_class', $css_class, $page));1204 1205 $output .= $indent . '<li class="' . $css_class . '"><a href="' . get_permalink($page->ID) . '" title="' . esc_attr( wp_strip_all_tags( apply_filters( 'the_title', $page->post_title, $page->ID ) ) ) . '">' . $link_before . apply_filters( 'the_title', $page->post_title, $page->ID ) . $link_after . '</a>';1206 1207 if ( !empty($show_date) ) {1208 if ( 'modified' == $show_date )1209 $time = $page->post_modified;1210 else1211 $time = $page->post_date;1212 1213 $output .= " " . mysql2date($date_format, $time);1214 }1215 }1216 1217 /**1218 * @see Walker::end_el()1219 * @since 2.1.01220 *1221 * @param string $output Passed by reference. Used to append additional content.1222 * @param object $page Page data object. Not used.1223 * @param int $depth Depth of page. Not Used.1224 */1225 function end_el(&$output, $page, $depth) {1226 $output .= "</li>\n";1227 }1228 1229 }1230 1231 /**1232 * Create HTML dropdown list of pages.1233 *1234 * @package WordPress1235 * @since 2.1.01236 * @uses Walker1237 */1238 class Walker_PageDropdown extends Walker {1239 /**1240 * @see Walker::$tree_type1241 * @since 2.1.01242 * @var string1243 */1244 var $tree_type = 'page';1245 1246 /**1247 * @see Walker::$db_fields1248 * @since 2.1.01249 * @todo Decouple this1250 * @var array1251 */1252 var $db_fields = array ('parent' => 'post_parent', 'id' => 'ID');1253 1254 /**1255 * @see Walker::start_el()1256 * @since 2.1.01257 *1258 * @param string $output Passed by reference. Used to append additional content.1259 * @param object $page Page data object.1260 * @param int $depth Depth of page in reference to parent pages. Used for padding.1261 * @param array $args Uses 'selected' argument for selected page to set selected HTML attribute for option element.1262 */1263 function start_el(&$output, $page, $depth, $args) {1264 $pad = str_repeat(' ', $depth * 3);1265 1266 $output .= "\t<option class=\"level-$depth\" value=\"$page->ID\"";1267 if ( $page->ID == $args['selected'] )1268 $output .= ' selected="selected"';1269 $output .= '>';1270 $title = esc_html($page->post_title);1271 $output .= "$pad$title";1272 $output .= "</option>\n";1273 }1274 }1275 1276 /**1277 * Create HTML list of categories.1278 *1279 * @package WordPress1280 * @since 2.1.01281 * @uses Walker1282 */1283 class Walker_Category extends Walker {1284 /**1285 * @see Walker::$tree_type1286 * @since 2.1.01287 * @var string1288 */1289 var $tree_type = 'category';1290 1291 /**1292 * @see Walker::$db_fields1293 * @since 2.1.01294 * @todo Decouple this1295 * @var array1296 */1297 var $db_fields = array ('parent' => 'parent', 'id' => 'term_id');1298 1299 /**1300 * @see Walker::start_lvl()1301 * @since 2.1.01302 *1303 * @param string $output Passed by reference. Used to append additional content.1304 * @param int $depth Depth of category. Used for tab indentation.1305 * @param array $args Will only append content if style argument value is 'list'.1306 */1307 function start_lvl(&$output, $depth, $args) {1308 if ( 'list' != $args['style'] )1309 return;1310 1311 $indent = str_repeat("\t", $depth);1312 $output .= "$indent<ul class='children'>\n";1313 }1314 1315 /**1316 * @see Walker::end_lvl()1317 * @since 2.1.01318 *1319 * @param string $output Passed by reference. Used to append additional content.1320 * @param int $depth Depth of category. Used for tab indentation.1321 * @param array $args Will only append content if style argument value is 'list'.1322 */1323 function end_lvl(&$output, $depth, $args) {1324 if ( 'list' != $args['style'] )1325 return;1326 1327 $indent = str_repeat("\t", $depth);1328 $output .= "$indent</ul>\n";1329 }1330 1331 /**1332 * @see Walker::start_el()1333 * @since 2.1.01334 *1335 * @param string $output Passed by reference. Used to append additional content.1336 * @param object $category Category data object.1337 * @param int $depth Depth of category in reference to parents.1338 * @param array $args1339 */1340 function start_el(&$output, $category, $depth, $args) {1341 extract($args);1342 1343 $cat_name = esc_attr( $category->name );1344 $cat_name = apply_filters( 'list_cats', $cat_name, $category );1345 $link = '<a href="' . esc_attr( get_term_link($category) ) . '" ';1346 if ( $use_desc_for_title == 0 || empty($category->description) )1347 $link .= 'title="' . sprintf(__( 'View all posts filed under %s' ), $cat_name) . '"';1348 else1349 $link .= 'title="' . esc_attr( strip_tags( apply_filters( 'category_description', $category->description, $category ) ) ) . '"';1350 $link .= '>';1351 $link .= $cat_name . '</a>';1352 1353 if ( !empty($feed_image) || !empty($feed) ) {1354 $link .= ' ';1355 1356 if ( empty($feed_image) )1357 $link .= '(';1358 1359 $link .= '<a href="' . get_term_feed_link( $category->term_id, $category->taxonomy, $feed_type ) . '"';1360 1361 if ( empty($feed) ) {1362 $alt = ' alt="' . sprintf(__( 'Feed for all posts filed under %s' ), $cat_name ) . '"';1363 } else {1364 $title = ' title="' . $feed . '"';1365 $alt = ' alt="' . $feed . '"';1366 $name = $feed;1367 $link .= $title;1368 }1369 1370 $link .= '>';1371 1372 if ( empty($feed_image) )1373 $link .= $name;1374 else1375 $link .= "<img src='$feed_image'$alt$title" . ' />';1376 1377 $link .= '</a>';1378 1379 if ( empty($feed_image) )1380 $link .= ')';1381 }1382 1383 if ( !empty($show_count) )1384 $link .= ' (' . intval($category->count) . ')';1385 1386 if ( !empty($show_date) )1387 $link .= ' ' . gmdate('Y-m-d', $category->last_update_timestamp);1388 1389 if ( 'list' == $args['style'] ) {1390 $output .= "\t<li";1391 $class = 'cat-item cat-item-' . $category->term_id;1392 if ( !empty($current_category) ) {1393 $_current_category = get_term( $current_category, $category->taxonomy );1394 if ( $category->term_id == $current_category )1395 $class .= ' current-cat';1396 elseif ( $category->term_id == $_current_category->parent )1397 $class .= ' current-cat-parent';1398 }1399 $output .= ' class="' . $class . '"';1400 $output .= ">$link\n";1401 } else {1402 $output .= "\t$link<br />\n";1403 }1404 }1405 1406 /**1407 * @see Walker::end_el()1408 * @since 2.1.01409 *1410 * @param string $output Passed by reference. Used to append additional content.1411 * @param object $page Not used.1412 * @param int $depth Depth of category. Not used.1413 * @param array $args Only uses 'list' for whether should append to output.1414 */1415 function end_el(&$output, $page, $depth, $args) {1416 if ( 'list' != $args['style'] )1417 return;1418 1419 $output .= "</li>\n";1420 }1421 1422 }1423 1424 /**1425 * Create HTML dropdown list of Categories.1426 *1427 * @package WordPress1428 * @since 2.1.01429 * @uses Walker1430 */1431 class Walker_CategoryDropdown extends Walker {1432 /**1433 * @see Walker::$tree_type1434 * @since 2.1.01435 * @var string1436 */1437 var $tree_type = 'category';1438 1439 /**1440 * @see Walker::$db_fields1441 * @since 2.1.01442 * @todo Decouple this1443 * @var array1444 */1445 var $db_fields = array ('parent' => 'parent', 'id' => 'term_id');1446 1447 /**1448 * @see Walker::start_el()1449 * @since 2.1.01450 *1451 * @param string $output Passed by reference. Used to append additional content.1452 * @param object $category Category data object.1453 * @param int $depth Depth of category. Used for padding.1454 * @param array $args Uses 'selected', 'show_count', and 'show_last_update' keys, if they exist.1455 */1456 function start_el(&$output, $category, $depth, $args) {1457 $pad = str_repeat(' ', $depth * 3);1458 1459 $cat_name = apply_filters('list_cats', $category->name, $category);1460 $output .= "\t<option class=\"level-$depth\" value=\"".$category->term_id."\"";1461 if ( $category->term_id == $args['selected'] )1462 $output .= ' selected="selected"';1463 $output .= '>';1464 $output .= $pad.$cat_name;1465 if ( $args['show_count'] )1466 $output .= ' ('. $category->count .')';1467 if ( $args['show_last_update'] ) {1468 $format = 'Y-m-d';1469 $output .= ' ' . gmdate($format, $category->last_update_timestamp);1470 }1471 $output .= "</option>\n";1472 }1473 }1474 12 1475 13 /** … … 1609 147 } 1610 148 1611 /**1612 * Helper class to remove the need to use eval to replace $matches[] in query strings.1613 *1614 * @since 2.9.01615 */1616 class WP_MatchesMapRegex {1617 /**1618 * store for matches1619 *1620 * @access private1621 * @var array1622 */1623 var $_matches;1624 1625 /**1626 * store for mapping result1627 *1628 * @access public1629 * @var string1630 */1631 var $output;1632 1633 /**1634 * subject to perform mapping on (query string containing $matches[] references1635 *1636 * @access private1637 * @var string1638 */1639 var $_subject;1640 1641 /**1642 * regexp pattern to match $matches[] references1643 *1644 * @var string1645 */1646 var $_pattern = '(\$matches\[[1-9]+[0-9]*\])'; // magic number1647 1648 /**1649 * constructor1650 *1651 * @param string $subject subject if regex1652 * @param array $matches data to use in map1653 * @return self1654 */1655 function WP_MatchesMapRegex($subject, $matches) {1656 $this->_subject = $subject;1657 $this->_matches = $matches;1658 $this->output = $this->_map();1659 }1660 1661 /**1662 * Substitute substring matches in subject.1663 *1664 * static helper function to ease use1665 *1666 * @access public1667 * @param string $subject subject1668 * @param array $matches data used for subsitution1669 * @return string1670 */1671 function apply($subject, $matches) {1672 $oSelf =& new WP_MatchesMapRegex($subject, $matches);1673 return $oSelf->output;1674 }1675 1676 /**1677 * do the actual mapping1678 *1679 * @access private1680 * @return string1681 */1682 function _map() {1683 $callback = array(&$this, 'callback');1684 return preg_replace_callback($this->_pattern, $callback, $this->_subject);1685 }1686 1687 /**1688 * preg_replace_callback hook1689 *1690 * @access public1691 * @param array $matches preg_replace regexp matches1692 * @return string1693 */1694 function callback($matches) {1695 $index = intval(substr($matches[0], 9, -1));1696 return ( isset( $this->_matches[$index] ) ? urlencode($this->_matches[$index]) : '' );1697 }1698 1699 }1700 1701 149 ?> -
trunk/wp-includes/class-wp-error.php
r16091 r16099 10 10 * @package WordPress 11 11 */ 12 13 /**14 * WordPress environment setup class.15 *16 * @package WordPress17 * @since 2.0.018 */19 class WP {20 /**21 * Public query variables.22 *23 * Long list of public query variables.24 *25 * @since 2.0.026 * @access public27 * @var array28 */29 var $public_query_vars = array('m', 'p', 'posts', 'w', 'cat', 'withcomments', 'withoutcomments', 's', 'search', 'exact', 'sentence', 'debug', 'calendar', 'page', 'paged', 'more', 'tb', 'pb', 'author', 'order', 'orderby', 'year', 'monthnum', 'day', 'hour', 'minute', 'second', 'name', 'category_name', 'tag', 'feed', 'author_name', 'static', 'pagename', 'page_id', 'error', 'comments_popup', 'attachment', 'attachment_id', 'subpost', 'subpost_id', 'preview', 'robots', 'taxonomy', 'term', 'cpage', 'post_type');30 31 /**32 * Private query variables.33 *34 * Long list of private query variables.35 *36 * @since 2.0.037 * @var array38 */39 var $private_query_vars = array('offset', 'posts_per_page', 'posts_per_archive_page', 'showposts', 'nopaging', 'post_type', 'post_status', 'category__in', 'category__not_in', 'category__and', 'tag__in', 'tag__not_in', 'tag__and', 'tag_slug__in', 'tag_slug__and', 'tag_id', 'post_mime_type', 'perm', 'comments_per_page', 'post__in', 'post__not_in');40 41 /**42 * Extra query variables set by the user.43 *44 * @since 2.1.045 * @var array46 */47 var $extra_query_vars = array();48 49 /**50 * Query variables for setting up the WordPress Query Loop.51 *52 * @since 2.0.053 * @var array54 */55 var $query_vars;56 57 /**58 * String parsed to set the query variables.59 *60 * @since 2.0.061 * @var string62 */63 var $query_string;64 65 /**66 * Permalink or requested URI.67 *68 * @since 2.0.069 * @var string70 */71 var $request;72 73 /**74 * Rewrite rule the request matched.75 *76 * @since 2.0.077 * @var string78 */79 var $matched_rule;80 81 /**82 * Rewrite query the request matched.83 *84 * @since 2.0.085 * @var string86 */87 var $matched_query;88 89 /**90 * Whether already did the permalink.91 *92 * @since 2.0.093 * @var bool94 */95 var $did_permalink = false;96 97 /**98 * Add name to list of public query variables.99 *100 * @since 2.1.0101 *102 * @param string $qv Query variable name.103 */104 function add_query_var($qv) {105 if ( !in_array($qv, $this->public_query_vars) )106 $this->public_query_vars[] = $qv;107 }108 109 /**110 * Set the value of a query variable.111 *112 * @since 2.3.0113 *114 * @param string $key Query variable name.115 * @param mixed $value Query variable value.116 */117 function set_query_var($key, $value) {118 $this->query_vars[$key] = $value;119 }120 121 /**122 * Parse request to find correct WordPress query.123 *124 * Sets up the query variables based on the request. There are also many125 * filters and actions that can be used to further manipulate the result.126 *127 * @since 2.0.0128 *129 * @param array|string $extra_query_vars Set the extra query variables.130 */131 function parse_request($extra_query_vars = '') {132 global $wp_rewrite;133 134 $this->query_vars = array();135 $post_type_query_vars = array();136 137 if ( is_array($extra_query_vars) )138 $this->extra_query_vars = & $extra_query_vars;139 else if (! empty($extra_query_vars))140 parse_str($extra_query_vars, $this->extra_query_vars);141 142 // Process PATH_INFO, REQUEST_URI, and 404 for permalinks.143 144 // Fetch the rewrite rules.145 $rewrite = $wp_rewrite->wp_rewrite_rules();146 147 if ( ! empty($rewrite) ) {148 // If we match a rewrite rule, this will be cleared.149 $error = '404';150 $this->did_permalink = true;151 152 if ( isset($_SERVER['PATH_INFO']) )153 $pathinfo = $_SERVER['PATH_INFO'];154 else155 $pathinfo = '';156 $pathinfo_array = explode('?', $pathinfo);157 $pathinfo = str_replace("%", "%25", $pathinfo_array[0]);158 $req_uri = $_SERVER['REQUEST_URI'];159 $req_uri_array = explode('?', $req_uri);160 $req_uri = $req_uri_array[0];161 $self = $_SERVER['PHP_SELF'];162 $home_path = parse_url(home_url());163 if ( isset($home_path['path']) )164 $home_path = $home_path['path'];165 else166 $home_path = '';167 $home_path = trim($home_path, '/');168 169 // Trim path info from the end and the leading home path from the170 // front. For path info requests, this leaves us with the requesting171 // filename, if any. For 404 requests, this leaves us with the172 // requested permalink.173 $req_uri = str_replace($pathinfo, '', $req_uri);174 $req_uri = trim($req_uri, '/');175 $req_uri = preg_replace("|^$home_path|", '', $req_uri);176 $req_uri = trim($req_uri, '/');177 $pathinfo = trim($pathinfo, '/');178 $pathinfo = preg_replace("|^$home_path|", '', $pathinfo);179 $pathinfo = trim($pathinfo, '/');180 $self = trim($self, '/');181 $self = preg_replace("|^$home_path|", '', $self);182 $self = trim($self, '/');183 184 // The requested permalink is in $pathinfo for path info requests and185 // $req_uri for other requests.186 if ( ! empty($pathinfo) && !preg_match('|^.*' . $wp_rewrite->index . '$|', $pathinfo) ) {187 $request = $pathinfo;188 } else {189 // If the request uri is the index, blank it out so that we don't try to match it against a rule.190 if ( $req_uri == $wp_rewrite->index )191 $req_uri = '';192 $request = $req_uri;193 }194 195 $this->request = $request;196 197 // Look for matches.198 $request_match = $request;199 foreach ( (array) $rewrite as $match => $query) {200 // Don't try to match against AtomPub calls201 if ( $req_uri == 'wp-app.php' )202 break;203 204 // If the requesting file is the anchor of the match, prepend it205 // to the path info.206 if ( (! empty($req_uri)) && (strpos($match, $req_uri) === 0) && ($req_uri != $request) )207 $request_match = $req_uri . '/' . $request;208 209 if ( preg_match("#^$match#", $request_match, $matches) ||210 preg_match("#^$match#", urldecode($request_match), $matches) ) {211 // Got a match.212 $this->matched_rule = $match;213 214 // Trim the query of everything up to the '?'.215 $query = preg_replace("!^.+\?!", '', $query);216 217 // Substitute the substring matches into the query.218 $query = addslashes(WP_MatchesMapRegex::apply($query, $matches));219 220 $this->matched_query = $query;221 222 // Parse the query.223 parse_str($query, $perma_query_vars);224 225 // If we're processing a 404 request, clear the error var226 // since we found something.227 if ( isset($_GET['error']) )228 unset($_GET['error']);229 230 if ( isset($error) )231 unset($error);232 233 break;234 }235 }236 237 // If req_uri is empty or if it is a request for ourself, unset error.238 if ( empty($request) || $req_uri == $self || strpos($_SERVER['PHP_SELF'], 'wp-admin/') !== false ) {239 if ( isset($_GET['error']) )240 unset($_GET['error']);241 242 if ( isset($error) )243 unset($error);244 245 if ( isset($perma_query_vars) && strpos($_SERVER['PHP_SELF'], 'wp-admin/') !== false )246 unset($perma_query_vars);247 248 $this->did_permalink = false;249 }250 }251 252 $this->public_query_vars = apply_filters('query_vars', $this->public_query_vars);253 254 foreach ( $GLOBALS['wp_post_types'] as $post_type => $t )255 if ( $t->query_var )256 $post_type_query_vars[$t->query_var] = $post_type;257 258 foreach ( $this->public_query_vars as $wpvar ) {259 if ( isset( $this->extra_query_vars[$wpvar] ) )260 $this->query_vars[$wpvar] = $this->extra_query_vars[$wpvar];261 elseif ( isset( $_POST[$wpvar] ) )262 $this->query_vars[$wpvar] = $_POST[$wpvar];263 elseif ( isset( $_GET[$wpvar] ) )264 $this->query_vars[$wpvar] = $_GET[$wpvar];265 elseif ( isset( $perma_query_vars[$wpvar] ) )266 $this->query_vars[$wpvar] = $perma_query_vars[$wpvar];267 268 if ( !empty( $this->query_vars[$wpvar] ) ) {269 if ( ! is_array( $this->query_vars[$wpvar] ) ) {270 $this->query_vars[$wpvar] = (string) $this->query_vars[$wpvar];271 } else {272 foreach ( $this->query_vars[$wpvar] as $vkey => $v ) {273 if ( !is_object( $v ) ) {274 $this->query_vars[$wpvar][$vkey] = (string) $v;275 }276 }277 }278 279 if ( isset($post_type_query_vars[$wpvar] ) ) {280 $this->query_vars['post_type'] = $post_type_query_vars[$wpvar];281 $this->query_vars['name'] = $this->query_vars[$wpvar];282 }283 }284 }285 286 // Limit publicly queried post_types to those that are publicly_queryable287 if ( isset( $this->query_vars['post_type']) ) {288 $queryable_post_types = get_post_types( array('publicly_queryable' => true) );289 if ( ! is_array( $this->query_vars['post_type'] ) ) {290 if ( ! in_array( $this->query_vars['post_type'], $queryable_post_types ) )291 unset( $this->query_vars['post_type'] );292 } else {293 $this->query_vars['post_type'] = array_intersect( $this->query_vars['post_type'], $queryable_post_types );294 }295 }296 297 foreach ( (array) $this->private_query_vars as $var) {298 if ( isset($this->extra_query_vars[$var]) )299 $this->query_vars[$var] = $this->extra_query_vars[$var];300 }301 302 if ( isset($error) )303 $this->query_vars['error'] = $error;304 305 $this->query_vars = apply_filters('request', $this->query_vars);306 307 do_action_ref_array('parse_request', array(&$this));308 }309 310 /**311 * Send additional HTTP headers for caching, content type, etc.312 *313 * Sets the X-Pingback header, 404 status (if 404), Content-type. If showing314 * a feed, it will also send last-modified, etag, and 304 status if needed.315 *316 * @since 2.0.0317 */318 function send_headers() {319 $headers = array('X-Pingback' => get_bloginfo('pingback_url'));320 $status = null;321 $exit_required = false;322 323 if ( is_user_logged_in() )324 $headers = array_merge($headers, wp_get_nocache_headers());325 if ( !empty($this->query_vars['error']) && '404' == $this->query_vars['error'] ) {326 $status = 404;327 if ( !is_user_logged_in() )328 $headers = array_merge($headers, wp_get_nocache_headers());329 $headers['Content-Type'] = get_option('html_type') . '; charset=' . get_option('blog_charset');330 } else if ( empty($this->query_vars['feed']) ) {331 $headers['Content-Type'] = get_option('html_type') . '; charset=' . get_option('blog_charset');332 } else {333 // We're showing a feed, so WP is indeed the only thing that last changed334 if ( !empty($this->query_vars['withcomments'])335 || ( empty($this->query_vars['withoutcomments'])336 && ( !empty($this->query_vars['p'])337 || !empty($this->query_vars['name'])338 || !empty($this->query_vars['page_id'])339 || !empty($this->query_vars['pagename'])340 || !empty($this->query_vars['attachment'])341 || !empty($this->query_vars['attachment_id'])342 )343 )344 )345 $wp_last_modified = mysql2date('D, d M Y H:i:s', get_lastcommentmodified('GMT'), 0).' GMT';346 else347 $wp_last_modified = mysql2date('D, d M Y H:i:s', get_lastpostmodified('GMT'), 0).' GMT';348 $wp_etag = '"' . md5($wp_last_modified) . '"';349 $headers['Last-Modified'] = $wp_last_modified;350 $headers['ETag'] = $wp_etag;351 352 // Support for Conditional GET353 if (isset($_SERVER['HTTP_IF_NONE_MATCH']))354 $client_etag = stripslashes(stripslashes($_SERVER['HTTP_IF_NONE_MATCH']));355 else $client_etag = false;356 357 $client_last_modified = empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? '' : trim($_SERVER['HTTP_IF_MODIFIED_SINCE']);358 // If string is empty, return 0. If not, attempt to parse into a timestamp359 $client_modified_timestamp = $client_last_modified ? strtotime($client_last_modified) : 0;360 361 // Make a timestamp for our most recent modification...362 $wp_modified_timestamp = strtotime($wp_last_modified);363 364 if ( ($client_last_modified && $client_etag) ?365 (($client_modified_timestamp >= $wp_modified_timestamp) && ($client_etag == $wp_etag)) :366 (($client_modified_timestamp >= $wp_modified_timestamp) || ($client_etag == $wp_etag)) ) {367 $status = 304;368 $exit_required = true;369 }370 }371 372 $headers = apply_filters('wp_headers', $headers, $this);373 374 if ( ! empty( $status ) )375 status_header( $status );376 foreach( (array) $headers as $name => $field_value )377 @header("{$name}: {$field_value}");378 379 if ( $exit_required )380 exit();381 382 do_action_ref_array('send_headers', array(&$this));383 }384 385 /**386 * Sets the query string property based off of the query variable property.387 *388 * The 'query_string' filter is deprecated, but still works. Plugins should389 * use the 'request' filter instead.390 *391 * @since 2.0.0392 */393 function build_query_string() {394 $this->query_string = '';395 foreach ( (array) array_keys($this->query_vars) as $wpvar) {396 if ( '' != $this->query_vars[$wpvar] ) {397 $this->query_string .= (strlen($this->query_string) < 1) ? '' : '&';398 if ( !is_scalar($this->query_vars[$wpvar]) ) // Discard non-scalars.399 continue;400 $this->query_string .= $wpvar . '=' . rawurlencode($this->query_vars[$wpvar]);401 }402 }403 404 // query_string filter deprecated. Use request filter instead.405 if ( has_filter('query_string') ) { // Don't bother filtering and parsing if no plugins are hooked in.406 $this->query_string = apply_filters('query_string', $this->query_string);407 parse_str($this->query_string, $this->query_vars);408 }409 }410 411 /**412 * Set up the WordPress Globals.413 *414 * The query_vars property will be extracted to the GLOBALS. So care should415 * be taken when naming global variables that might interfere with the416 * WordPress environment.417 *418 * @global string $query_string Query string for the loop.419 * @global int $more Only set, if single page or post.420 * @global int $single If single page or post. Only set, if single page or post.421 *422 * @since 2.0.0423 */424 function register_globals() {425 global $wp_query;426 // Extract updated query vars back into global namespace.427 foreach ( (array) $wp_query->query_vars as $key => $value) {428 $GLOBALS[$key] = $value;429 }430 431 $GLOBALS['query_string'] = $this->query_string;432 $GLOBALS['posts'] = & $wp_query->posts;433 $GLOBALS['post'] = (isset($wp_query->post)) ? $wp_query->post : null;434 $GLOBALS['request'] = $wp_query->request;435 436 if ( is_single() || is_page() ) {437 $GLOBALS['more'] = 1;438 $GLOBALS['single'] = 1;439 }440 }441 442 /**443 * Set up the current user.444 *445 * @since 2.0.0446 */447 function init() {448 wp_get_current_user();449 }450 451 /**452 * Set up the Loop based on the query variables.453 *454 * @uses WP::$query_vars455 * @since 2.0.0456 */457 function query_posts() {458 global $wp_the_query;459 $this->build_query_string();460 $wp_the_query->query($this->query_vars);461 }462 463 /**464 * Set the Headers for 404, if nothing is found for requested URL.465 *466 * Issue a 404 if a request doesn't match any posts and doesn't match467 * any object (e.g. an existing-but-empty category, tag, author) and a 404 was not already468 * issued, and if the request was not a search or the homepage.469 *470 * Otherwise, issue a 200.471 *472 * @since 2.0.0473 */474 function handle_404() {475 global $wp_query;476 477 if ( !is_admin() && ( 0 == count( $wp_query->posts ) ) && !is_404() && !is_robots() && !is_search() && !is_home() ) {478 // Don't 404 for these queries if they matched an object.479 if ( ( is_tag() || is_category() || is_tax() || is_author() ) && $wp_query->get_queried_object() && !is_paged() ) {480 if ( !is_404() )481 status_header( 200 );482 return;483 }484 $wp_query->set_404();485 status_header( 404 );486 nocache_headers();487 } elseif ( !is_404() ) {488 status_header( 200 );489 }490 }491 492 /**493 * Sets up all of the variables required by the WordPress environment.494 *495 * The action 'wp' has one parameter that references the WP object. It496 * allows for accessing the properties and methods to further manipulate the497 * object.498 *499 * @since 2.0.0500 *501 * @param string|array $query_args Passed to {@link parse_request()}502 */503 function main($query_args = '') {504 $this->init();505 $this->parse_request($query_args);506 $this->send_headers();507 $this->query_posts();508 $this->handle_404();509 $this->register_globals();510 do_action_ref_array('wp', array(&$this));511 }512 513 /**514 * PHP4 Constructor - Does nothing.515 *516 * Call main() method when ready to run setup.517 *518 * @since 2.0.0519 *520 * @return WP521 */522 function WP() {523 // Empty.524 }525 }526 12 527 13 /** … … 727 213 } 728 214 729 /**730 * A class for displaying various tree-like structures.731 *732 * Extend the Walker class to use it, see examples at the below. Child classes733 * do not need to implement all of the abstract methods in the class. The child734 * only needs to implement the methods that are needed. Also, the methods are735 * not strictly abstract in that the parameter definition needs to be followed.736 * The child classes can have additional parameters.737 *738 * @package WordPress739 * @since 2.1.0740 * @abstract741 */742 class Walker {743 /**744 * What the class handles.745 *746 * @since 2.1.0747 * @var string748 * @access public749 */750 var $tree_type;751 752 /**753 * DB fields to use.754 *755 * @since 2.1.0756 * @var array757 * @access protected758 */759 var $db_fields;760 761 /**762 * Max number of pages walked by the paged walker763 *764 * @since 2.7.0765 * @var int766 * @access protected767 */768 var $max_pages = 1;769 770 /**771 * Starts the list before the elements are added.772 *773 * Additional parameters are used in child classes. The args parameter holds774 * additional values that may be used with the child class methods. This775 * method is called at the start of the output list.776 *777 * @since 2.1.0778 * @abstract779 *780 * @param string $output Passed by reference. Used to append additional content.781 */782 function start_lvl(&$output) {}783 784 /**785 * Ends the list of after the elements are added.786 *787 * Additional parameters are used in child classes. The args parameter holds788 * additional values that may be used with the child class methods. This789 * method finishes the list at the end of output of the elements.790 *791 * @since 2.1.0792 * @abstract793 *794 * @param string $output Passed by reference. Used to append additional content.795 */796 function end_lvl(&$output) {}797 798 /**799 * Start the element output.800 *801 * Additional parameters are used in child classes. The args parameter holds802 * additional values that may be used with the child class methods. Includes803 * the element output also.804 *805 * @since 2.1.0806 * @abstract807 *808 * @param string $output Passed by reference. Used to append additional content.809 */810 function start_el(&$output) {}811 812 /**813 * Ends the element output, if needed.814 *815 * Additional parameters are used in child classes. The args parameter holds816 * additional values that may be used with the child class methods.817 *818 * @since 2.1.0819 * @abstract820 *821 * @param string $output Passed by reference. Used to append additional content.822 */823 function end_el(&$output) {}824 825 /**826 * Traverse elements to create list from elements.827 *828 * Display one element if the element doesn't have any children otherwise,829 * display the element and its children. Will only traverse up to the max830 * depth and no ignore elements under that depth. It is possible to set the831 * max depth to include all depths, see walk() method.832 *833 * This method shouldn't be called directly, use the walk() method instead.834 *835 * @since 2.5.0836 *837 * @param object $element Data object838 * @param array $children_elements List of elements to continue traversing.839 * @param int $max_depth Max depth to traverse.840 * @param int $depth Depth of current element.841 * @param array $args842 * @param string $output Passed by reference. Used to append additional content.843 * @return null Null on failure with no changes to parameters.844 */845 function display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output ) {846 847 if ( !$element )848 return;849 850 $id_field = $this->db_fields['id'];851 852 //display this element853 if ( is_array( $args[0] ) )854 $args[0]['has_children'] = ! empty( $children_elements[$element->$id_field] );855 $cb_args = array_merge( array(&$output, $element, $depth), $args);856 call_user_func_array(array(&$this, 'start_el'), $cb_args);857 858 $id = $element->$id_field;859 860 // descend only when the depth is right and there are childrens for this element861 if ( ($max_depth == 0 || $max_depth > $depth+1 ) && isset( $children_elements[$id]) ) {862 863 foreach( $children_elements[ $id ] as $child ){864 865 if ( !isset($newlevel) ) {866 $newlevel = true;867 //start the child delimiter868 $cb_args = array_merge( array(&$output, $depth), $args);869 call_user_func_array(array(&$this, 'start_lvl'), $cb_args);870 }871 $this->display_element( $child, $children_elements, $max_depth, $depth + 1, $args, $output );872 }873 unset( $children_elements[ $id ] );874 }875 876 if ( isset($newlevel) && $newlevel ){877 //end the child delimiter878 $cb_args = array_merge( array(&$output, $depth), $args);879 call_user_func_array(array(&$this, 'end_lvl'), $cb_args);880 }881 882 //end this element883 $cb_args = array_merge( array(&$output, $element, $depth), $args);884 call_user_func_array(array(&$this, 'end_el'), $cb_args);885 }886 887 /**888 * Display array of elements hierarchically.889 *890 * It is a generic function which does not assume any existing order of891 * elements. max_depth = -1 means flatly display every element. max_depth =892 * 0 means display all levels. max_depth > 0 specifies the number of893 * display levels.894 *895 * @since 2.1.0896 *897 * @param array $elements898 * @param int $max_depth899 * @return string900 */901 function walk( $elements, $max_depth) {902 903 $args = array_slice(func_get_args(), 2);904 $output = '';905 906 if ($max_depth < -1) //invalid parameter907 return $output;908 909 if (empty($elements)) //nothing to walk910 return $output;911 912 $id_field = $this->db_fields['id'];913 $parent_field = $this->db_fields['parent'];914 915 // flat display916 if ( -1 == $max_depth ) {917 $empty_array = array();918 foreach ( $elements as $e )919 $this->display_element( $e, $empty_array, 1, 0, $args, $output );920 return $output;921 }922 923 /*924 * need to display in hierarchical order925 * separate elements into two buckets: top level and children elements926 * children_elements is two dimensional array, eg.927 * children_elements[10][] contains all sub-elements whose parent is 10.928 */929 $top_level_elements = array();930 $children_elements = array();931 foreach ( $elements as $e) {932 if ( 0 == $e->$parent_field )933 $top_level_elements[] = $e;934 else935 $children_elements[ $e->$parent_field ][] = $e;936 }937 938 /*939 * when none of the elements is top level940 * assume the first one must be root of the sub elements941 */942 if ( empty($top_level_elements) ) {943 944 $first = array_slice( $elements, 0, 1 );945 $root = $first[0];946 947 $top_level_elements = array();948 $children_elements = array();949 foreach ( $elements as $e) {950 if ( $root->$parent_field == $e->$parent_field )951 $top_level_elements[] = $e;952 else953 $children_elements[ $e->$parent_field ][] = $e;954 }955 }956 957 foreach ( $top_level_elements as $e )958 $this->display_element( $e, $children_elements, $max_depth, 0, $args, $output );959 960 /*961 * if we are displaying all levels, and remaining children_elements is not empty,962 * then we got orphans, which should be displayed regardless963 */964 if ( ( $max_depth == 0 ) && count( $children_elements ) > 0 ) {965 $empty_array = array();966 foreach ( $children_elements as $orphans )967 foreach( $orphans as $op )968 $this->display_element( $op, $empty_array, 1, 0, $args, $output );969 }970 971 return $output;972 }973 974 /**975 * paged_walk() - produce a page of nested elements976 *977 * Given an array of hierarchical elements, the maximum depth, a specific page number,978 * and number of elements per page, this function first determines all top level root elements979 * belonging to that page, then lists them and all of their children in hierarchical order.980 *981 * @package WordPress982 * @since 2.7983 * @param int $max_depth = 0 means display all levels; $max_depth > 0 specifies the number of display levels.984 * @param int $page_num the specific page number, beginning with 1.985 * @return XHTML of the specified page of elements986 */987 function paged_walk( $elements, $max_depth, $page_num, $per_page ) {988 989 /* sanity check */990 if ( empty($elements) || $max_depth < -1 )991 return '';992 993 $args = array_slice( func_get_args(), 4 );994 $output = '';995 996 $id_field = $this->db_fields['id'];997 $parent_field = $this->db_fields['parent'];998 999 $count = -1;1000 if ( -1 == $max_depth )1001 $total_top = count( $elements );1002 if ( $page_num < 1 || $per_page < 0 ) {1003 // No paging1004 $paging = false;1005 $start = 0;1006 if ( -1 == $max_depth )1007 $end = $total_top;1008 $this->max_pages = 1;1009 } else {1010 $paging = true;1011 $start = ( (int)$page_num - 1 ) * (int)$per_page;1012 $end = $start + $per_page;1013 if ( -1 == $max_depth )1014 $this->max_pages = ceil($total_top / $per_page);1015 }1016 1017 // flat display1018 if ( -1 == $max_depth ) {1019 if ( !empty($args[0]['reverse_top_level']) ) {1020 $elements = array_reverse( $elements );1021 $oldstart = $start;1022 $start = $total_top - $end;1023 $end = $total_top - $oldstart;1024 }1025 1026 $empty_array = array();1027 foreach ( $elements as $e ) {1028 $count++;1029 if ( $count < $start )1030 continue;1031 if ( $count >= $end )1032 break;1033 $this->display_element( $e, $empty_array, 1, 0, $args, $output );1034 }1035 return $output;1036 }1037 1038 /*1039 * separate elements into two buckets: top level and children elements1040 * children_elements is two dimensional array, eg.1041 * children_elements[10][] contains all sub-elements whose parent is 10.1042 */1043 $top_level_elements = array();1044 $children_elements = array();1045 foreach ( $elements as $e) {1046 if ( 0 == $e->$parent_field )1047 $top_level_elements[] = $e;1048 else1049 $children_elements[ $e->$parent_field ][] = $e;1050 }1051 1052 $total_top = count( $top_level_elements );1053 if ( $paging )1054 $this->max_pages = ceil($total_top / $per_page);1055 else1056 $end = $total_top;1057 1058 if ( !empty($args[0]['reverse_top_level']) ) {1059 $top_level_elements = array_reverse( $top_level_elements );1060 $oldstart = $start;1061 $start = $total_top - $end;1062 $end = $total_top - $oldstart;1063 }1064 if ( !empty($args[0]['reverse_children']) ) {1065 foreach ( $children_elements as $parent => $children )1066 $children_elements[$parent] = array_reverse( $children );1067 }1068 1069 foreach ( $top_level_elements as $e ) {1070 $count++;1071 1072 //for the last page, need to unset earlier children in order to keep track of orphans1073 if ( $end >= $total_top && $count < $start )1074 $this->unset_children( $e, $children_elements );1075 1076 if ( $count < $start )1077 continue;1078 1079 if ( $count >= $end )1080 break;1081 1082 $this->display_element( $e, $children_elements, $max_depth, 0, $args, $output );1083 }1084 1085 if ( $end >= $total_top && count( $children_elements ) > 0 ) {1086 $empty_array = array();1087 foreach ( $children_elements as $orphans )1088 foreach( $orphans as $op )1089 $this->display_element( $op, $empty_array, 1, 0, $args, $output );1090 }1091 1092 return $output;1093 }1094 1095 function get_number_of_root_elements( $elements ){1096 1097 $num = 0;1098 $parent_field = $this->db_fields['parent'];1099 1100 foreach ( $elements as $e) {1101 if ( 0 == $e->$parent_field )1102 $num++;1103 }1104 return $num;1105 }1106 1107 // unset all the children for a given top level element1108 function unset_children( $e, &$children_elements ){1109 1110 if ( !$e || !$children_elements )1111 return;1112 1113 $id_field = $this->db_fields['id'];1114 $id = $e->$id_field;1115 1116 if ( !empty($children_elements[$id]) && is_array($children_elements[$id]) )1117 foreach ( (array) $children_elements[$id] as $child )1118 $this->unset_children( $child, $children_elements );1119 1120 if ( isset($children_elements[$id]) )1121 unset( $children_elements[$id] );1122 1123 }1124 }1125 1126 /**1127 * Create HTML list of pages.1128 *1129 * @package WordPress1130 * @since 2.1.01131 * @uses Walker1132 */1133 class Walker_Page extends Walker {1134 /**1135 * @see Walker::$tree_type1136 * @since 2.1.01137 * @var string1138 */1139 var $tree_type = 'page';1140 1141 /**1142 * @see Walker::$db_fields1143 * @since 2.1.01144 * @todo Decouple this.1145 * @var array1146 */1147 var $db_fields = array ('parent' => 'post_parent', 'id' => 'ID');1148 1149 /**1150 * @see Walker::start_lvl()1151 * @since 2.1.01152 *1153 * @param string $output Passed by reference. Used to append additional content.1154 * @param int $depth Depth of page. Used for padding.1155 */1156 function start_lvl(&$output, $depth) {1157 $indent = str_repeat("\t", $depth);1158 $output .= "\n$indent<ul class='children'>\n";1159 }1160 1161 /**1162 * @see Walker::end_lvl()1163 * @since 2.1.01164 *1165 * @param string $output Passed by reference. Used to append additional content.1166 * @param int $depth Depth of page. Used for padding.1167 */1168 function end_lvl(&$output, $depth) {1169 $indent = str_repeat("\t", $depth);1170 $output .= "$indent</ul>\n";1171 }1172 1173 /**1174 * @see Walker::start_el()1175 * @since 2.1.01176 *1177 * @param string $output Passed by reference. Used to append additional content.1178 * @param object $page Page data object.1179 * @param int $depth Depth of page. Used for padding.1180 * @param int $current_page Page ID.1181 * @param array $args1182 */1183 function start_el(&$output, $page, $depth, $args, $current_page) {1184 if ( $depth )1185 $indent = str_repeat("\t", $depth);1186 else1187 $indent = '';1188 1189 extract($args, EXTR_SKIP);1190 $css_class = array('page_item', 'page-item-'.$page->ID);1191 if ( !empty($current_page) ) {1192 $_current_page = get_page( $current_page );1193 if ( isset($_current_page->ancestors) && in_array($page->ID, (array) $_current_page->ancestors) )1194 $css_class[] = 'current_page_ancestor';1195 if ( $page->ID == $current_page )1196 $css_class[] = 'current_page_item';1197 elseif ( $_current_page && $page->ID == $_current_page->post_parent )1198 $css_class[] = 'current_page_parent';1199 } elseif ( $page->ID == get_option('page_for_posts') ) {1200 $css_class[] = 'current_page_parent';1201 }1202 1203 $css_class = implode(' ', apply_filters('page_css_class', $css_class, $page));1204 1205 $output .= $indent . '<li class="' . $css_class . '"><a href="' . get_permalink($page->ID) . '" title="' . esc_attr( wp_strip_all_tags( apply_filters( 'the_title', $page->post_title, $page->ID ) ) ) . '">' . $link_before . apply_filters( 'the_title', $page->post_title, $page->ID ) . $link_after . '</a>';1206 1207 if ( !empty($show_date) ) {1208 if ( 'modified' == $show_date )1209 $time = $page->post_modified;1210 else1211 $time = $page->post_date;1212 1213 $output .= " " . mysql2date($date_format, $time);1214 }1215 }1216 1217 /**1218 * @see Walker::end_el()1219 * @since 2.1.01220 *1221 * @param string $output Passed by reference. Used to append additional content.1222 * @param object $page Page data object. Not used.1223 * @param int $depth Depth of page. Not Used.1224 */1225 function end_el(&$output, $page, $depth) {1226 $output .= "</li>\n";1227 }1228 1229 }1230 1231 /**1232 * Create HTML dropdown list of pages.1233 *1234 * @package WordPress1235 * @since 2.1.01236 * @uses Walker1237 */1238 class Walker_PageDropdown extends Walker {1239 /**1240 * @see Walker::$tree_type1241 * @since 2.1.01242 * @var string1243 */1244 var $tree_type = 'page';1245 1246 /**1247 * @see Walker::$db_fields1248 * @since 2.1.01249 * @todo Decouple this1250 * @var array1251 */1252 var $db_fields = array ('parent' => 'post_parent', 'id' => 'ID');1253 1254 /**1255 * @see Walker::start_el()1256 * @since 2.1.01257 *1258 * @param string $output Passed by reference. Used to append additional content.1259 * @param object $page Page data object.1260 * @param int $depth Depth of page in reference to parent pages. Used for padding.1261 * @param array $args Uses 'selected' argument for selected page to set selected HTML attribute for option element.1262 */1263 function start_el(&$output, $page, $depth, $args) {1264 $pad = str_repeat(' ', $depth * 3);1265 1266 $output .= "\t<option class=\"level-$depth\" value=\"$page->ID\"";1267 if ( $page->ID == $args['selected'] )1268 $output .= ' selected="selected"';1269 $output .= '>';1270 $title = esc_html($page->post_title);1271 $output .= "$pad$title";1272 $output .= "</option>\n";1273 }1274 }1275 1276 /**1277 * Create HTML list of categories.1278 *1279 * @package WordPress1280 * @since 2.1.01281 * @uses Walker1282 */1283 class Walker_Category extends Walker {1284 /**1285 * @see Walker::$tree_type1286 * @since 2.1.01287 * @var string1288 */1289 var $tree_type = 'category';1290 1291 /**1292 * @see Walker::$db_fields1293 * @since 2.1.01294 * @todo Decouple this1295 * @var array1296 */1297 var $db_fields = array ('parent' => 'parent', 'id' => 'term_id');1298 1299 /**1300 * @see Walker::start_lvl()1301 * @since 2.1.01302 *1303 * @param string $output Passed by reference. Used to append additional content.1304 * @param int $depth Depth of category. Used for tab indentation.1305 * @param array $args Will only append content if style argument value is 'list'.1306 */1307 function start_lvl(&$output, $depth, $args) {1308 if ( 'list' != $args['style'] )1309 return;1310 1311 $indent = str_repeat("\t", $depth);1312 $output .= "$indent<ul class='children'>\n";1313 }1314 1315 /**1316 * @see Walker::end_lvl()1317 * @since 2.1.01318 *1319 * @param string $output Passed by reference. Used to append additional content.1320 * @param int $depth Depth of category. Used for tab indentation.1321 * @param array $args Will only append content if style argument value is 'list'.1322 */1323 function end_lvl(&$output, $depth, $args) {1324 if ( 'list' != $args['style'] )1325 return;1326 1327 $indent = str_repeat("\t", $depth);1328 $output .= "$indent</ul>\n";1329 }1330 1331 /**1332 * @see Walker::start_el()1333 * @since 2.1.01334 *1335 * @param string $output Passed by reference. Used to append additional content.1336 * @param object $category Category data object.1337 * @param int $depth Depth of category in reference to parents.1338 * @param array $args1339 */1340 function start_el(&$output, $category, $depth, $args) {1341 extract($args);1342 1343 $cat_name = esc_attr( $category->name );1344 $cat_name = apply_filters( 'list_cats', $cat_name, $category );1345 $link = '<a href="' . esc_attr( get_term_link($category) ) . '" ';1346 if ( $use_desc_for_title == 0 || empty($category->description) )1347 $link .= 'title="' . sprintf(__( 'View all posts filed under %s' ), $cat_name) . '"';1348 else1349 $link .= 'title="' . esc_attr( strip_tags( apply_filters( 'category_description', $category->description, $category ) ) ) . '"';1350 $link .= '>';1351 $link .= $cat_name . '</a>';1352 1353 if ( !empty($feed_image) || !empty($feed) ) {1354 $link .= ' ';1355 1356 if ( empty($feed_image) )1357 $link .= '(';1358 1359 $link .= '<a href="' . get_term_feed_link( $category->term_id, $category->taxonomy, $feed_type ) . '"';1360 1361 if ( empty($feed) ) {1362 $alt = ' alt="' . sprintf(__( 'Feed for all posts filed under %s' ), $cat_name ) . '"';1363 } else {1364 $title = ' title="' . $feed . '"';1365 $alt = ' alt="' . $feed . '"';1366 $name = $feed;1367 $link .= $title;1368 }1369 1370 $link .= '>';1371 1372 if ( empty($feed_image) )1373 $link .= $name;1374 else1375 $link .= "<img src='$feed_image'$alt$title" . ' />';1376 1377 $link .= '</a>';1378 1379 if ( empty($feed_image) )1380 $link .= ')';1381 }1382 1383 if ( !empty($show_count) )1384 $link .= ' (' . intval($category->count) . ')';1385 1386 if ( !empty($show_date) )1387 $link .= ' ' . gmdate('Y-m-d', $category->last_update_timestamp);1388 1389 if ( 'list' == $args['style'] ) {1390 $output .= "\t<li";1391 $class = 'cat-item cat-item-' . $category->term_id;1392 if ( !empty($current_category) ) {1393 $_current_category = get_term( $current_category, $category->taxonomy );1394 if ( $category->term_id == $current_category )1395 $class .= ' current-cat';1396 elseif ( $category->term_id == $_current_category->parent )1397 $class .= ' current-cat-parent';1398 }1399 $output .= ' class="' . $class . '"';1400 $output .= ">$link\n";1401 } else {1402 $output .= "\t$link<br />\n";1403 }1404 }1405 1406 /**1407 * @see Walker::end_el()1408 * @since 2.1.01409 *1410 * @param string $output Passed by reference. Used to append additional content.1411 * @param object $page Not used.1412 * @param int $depth Depth of category. Not used.1413 * @param array $args Only uses 'list' for whether should append to output.1414 */1415 function end_el(&$output, $page, $depth, $args) {1416 if ( 'list' != $args['style'] )1417 return;1418 1419 $output .= "</li>\n";1420 }1421 1422 }1423 1424 /**1425 * Create HTML dropdown list of Categories.1426 *1427 * @package WordPress1428 * @since 2.1.01429 * @uses Walker1430 */1431 class Walker_CategoryDropdown extends Walker {1432 /**1433 * @see Walker::$tree_type1434 * @since 2.1.01435 * @var string1436 */1437 var $tree_type = 'category';1438 1439 /**1440 * @see Walker::$db_fields1441 * @since 2.1.01442 * @todo Decouple this1443 * @var array1444 */1445 var $db_fields = array ('parent' => 'parent', 'id' => 'term_id');1446 1447 /**1448 * @see Walker::start_el()1449 * @since 2.1.01450 *1451 * @param string $output Passed by reference. Used to append additional content.1452 * @param object $category Category data object.1453 * @param int $depth Depth of category. Used for padding.1454 * @param array $args Uses 'selected', 'show_count', and 'show_last_update' keys, if they exist.1455 */1456 function start_el(&$output, $category, $depth, $args) {1457 $pad = str_repeat(' ', $depth * 3);1458 1459 $cat_name = apply_filters('list_cats', $category->name, $category);1460 $output .= "\t<option class=\"level-$depth\" value=\"".$category->term_id."\"";1461 if ( $category->term_id == $args['selected'] )1462 $output .= ' selected="selected"';1463 $output .= '>';1464 $output .= $pad.$cat_name;1465 if ( $args['show_count'] )1466 $output .= ' ('. $category->count .')';1467 if ( $args['show_last_update'] ) {1468 $format = 'Y-m-d';1469 $output .= ' ' . gmdate($format, $category->last_update_timestamp);1470 }1471 $output .= "</option>\n";1472 }1473 }1474 1475 /**1476 * Send XML response back to AJAX request.1477 *1478 * @package WordPress1479 * @since 2.1.01480 */1481 class WP_Ajax_Response {1482 /**1483 * Store XML responses to send.1484 *1485 * @since 2.1.01486 * @var array1487 * @access private1488 */1489 var $responses = array();1490 1491 /**1492 * PHP4 Constructor - Passes args to {@link WP_Ajax_Response::add()}.1493 *1494 * @since 2.1.01495 * @see WP_Ajax_Response::add()1496 *1497 * @param string|array $args Optional. Will be passed to add() method.1498 * @return WP_Ajax_Response1499 */1500 function WP_Ajax_Response( $args = '' ) {1501 if ( !empty($args) )1502 $this->add($args);1503 }1504 1505 /**1506 * Append to XML response based on given arguments.1507 *1508 * The arguments that can be passed in the $args parameter are below. It is1509 * also possible to pass a WP_Error object in either the 'id' or 'data'1510 * argument. The parameter isn't actually optional, content should be given1511 * in order to send the correct response.1512 *1513 * 'what' argument is a string that is the XMLRPC response type.1514 * 'action' argument is a boolean or string that acts like a nonce.1515 * 'id' argument can be WP_Error or an integer.1516 * 'old_id' argument is false by default or an integer of the previous ID.1517 * 'position' argument is an integer or a string with -1 = top, 1 = bottom,1518 * html ID = after, -html ID = before.1519 * 'data' argument is a string with the content or message.1520 * 'supplemental' argument is an array of strings that will be children of1521 * the supplemental element.1522 *1523 * @since 2.1.01524 *1525 * @param string|array $args Override defaults.1526 * @return string XML response.1527 */1528 function add( $args = '' ) {1529 $defaults = array(1530 'what' => 'object', 'action' => false,1531 'id' => '0', 'old_id' => false,1532 'position' => 1,1533 'data' => '', 'supplemental' => array()1534 );1535 1536 $r = wp_parse_args( $args, $defaults );1537 extract( $r, EXTR_SKIP );1538 $position = preg_replace( '/[^a-z0-9:_-]/i', '', $position );1539 1540 if ( is_wp_error($id) ) {1541 $data = $id;1542 $id = 0;1543 }1544 1545 $response = '';1546 if ( is_wp_error($data) ) {1547 foreach ( (array) $data->get_error_codes() as $code ) {1548 $response .= "<wp_error code='$code'><![CDATA[" . $data->get_error_message($code) . "]]></wp_error>";1549 if ( !$error_data = $data->get_error_data($code) )1550 continue;1551 $class = '';1552 if ( is_object($error_data) ) {1553 $class = ' class="' . get_class($error_data) . '"';1554 $error_data = get_object_vars($error_data);1555 }1556 1557 $response .= "<wp_error_data code='$code'$class>";1558 1559 if ( is_scalar($error_data) ) {1560 $response .= "<![CDATA[$error_data]]>";1561 } elseif ( is_array($error_data) ) {1562 foreach ( $error_data as $k => $v )1563 $response .= "<$k><![CDATA[$v]]></$k>";1564 }1565 1566 $response .= "</wp_error_data>";1567 }1568 } else {1569 $response = "<response_data><![CDATA[$data]]></response_data>";1570 }1571 1572 $s = '';1573 if ( is_array($supplemental) ) {1574 foreach ( $supplemental as $k => $v )1575 $s .= "<$k><![CDATA[$v]]></$k>";1576 $s = "<supplemental>$s</supplemental>";1577 }1578 1579 if ( false === $action )1580 $action = $_POST['action'];1581 1582 $x = '';1583 $x .= "<response action='{$action}_$id'>"; // The action attribute in the xml output is formatted like a nonce action1584 $x .= "<$what id='$id' " . ( false === $old_id ? '' : "old_id='$old_id' " ) . "position='$position'>";1585 $x .= $response;1586 $x .= $s;1587 $x .= "</$what>";1588 $x .= "</response>";1589 1590 $this->responses[] = $x;1591 return $x;1592 }1593 1594 /**1595 * Display XML formatted responses.1596 *1597 * Sets the content type header to text/xml.1598 *1599 * @since 2.1.01600 */1601 function send() {1602 header('Content-Type: text/xml');1603 echo "<?xml version='1.0' standalone='yes'?><wp_ajax>";1604 foreach ( (array) $this->responses as $response )1605 echo $response;1606 echo '</wp_ajax>';1607 die();1608 }1609 }1610 1611 /**1612 * Helper class to remove the need to use eval to replace $matches[] in query strings.1613 *1614 * @since 2.9.01615 */1616 class WP_MatchesMapRegex {1617 /**1618 * store for matches1619 *1620 * @access private1621 * @var array1622 */1623 var $_matches;1624 1625 /**1626 * store for mapping result1627 *1628 * @access public1629 * @var string1630 */1631 var $output;1632 1633 /**1634 * subject to perform mapping on (query string containing $matches[] references1635 *1636 * @access private1637 * @var string1638 */1639 var $_subject;1640 1641 /**1642 * regexp pattern to match $matches[] references1643 *1644 * @var string1645 */1646 var $_pattern = '(\$matches\[[1-9]+[0-9]*\])'; // magic number1647 1648 /**1649 * constructor1650 *1651 * @param string $subject subject if regex1652 * @param array $matches data to use in map1653 * @return self1654 */1655 function WP_MatchesMapRegex($subject, $matches) {1656 $this->_subject = $subject;1657 $this->_matches = $matches;1658 $this->output = $this->_map();1659 }1660 1661 /**1662 * Substitute substring matches in subject.1663 *1664 * static helper function to ease use1665 *1666 * @access public1667 * @param string $subject subject1668 * @param array $matches data used for subsitution1669 * @return string1670 */1671 function apply($subject, $matches) {1672 $oSelf =& new WP_MatchesMapRegex($subject, $matches);1673 return $oSelf->output;1674 }1675 1676 /**1677 * do the actual mapping1678 *1679 * @access private1680 * @return string1681 */1682 function _map() {1683 $callback = array(&$this, 'callback');1684 return preg_replace_callback($this->_pattern, $callback, $this->_subject);1685 }1686 1687 /**1688 * preg_replace_callback hook1689 *1690 * @access public1691 * @param array $matches preg_replace regexp matches1692 * @return string1693 */1694 function callback($matches) {1695 $index = intval(substr($matches[0], 9, -1));1696 return ( isset( $this->_matches[$index] ) ? urlencode($this->_matches[$index]) : '' );1697 }1698 1699 }1700 1701 215 ?> -
trunk/wp-includes/class-wp-walker.php
r16091 r16099 10 10 * @package WordPress 11 11 */ 12 13 /**14 * WordPress environment setup class.15 *16 * @package WordPress17 * @since 2.0.018 */19 class WP {20 /**21 * Public query variables.22 *23 * Long list of public query variables.24 *25 * @since 2.0.026 * @access public27 * @var array28 */29 var $public_query_vars = array('m', 'p', 'posts', 'w', 'cat', 'withcomments', 'withoutcomments', 's', 'search', 'exact', 'sentence', 'debug', 'calendar', 'page', 'paged', 'more', 'tb', 'pb', 'author', 'order', 'orderby', 'year', 'monthnum', 'day', 'hour', 'minute', 'second', 'name', 'category_name', 'tag', 'feed', 'author_name', 'static', 'pagename', 'page_id', 'error', 'comments_popup', 'attachment', 'attachment_id', 'subpost', 'subpost_id', 'preview', 'robots', 'taxonomy', 'term', 'cpage', 'post_type');30 31 /**32 * Private query variables.33 *34 * Long list of private query variables.35 *36 * @since 2.0.037 * @var array38 */39 var $private_query_vars = array('offset', 'posts_per_page', 'posts_per_archive_page', 'showposts', 'nopaging', 'post_type', 'post_status', 'category__in', 'category__not_in', 'category__and', 'tag__in', 'tag__not_in', 'tag__and', 'tag_slug__in', 'tag_slug__and', 'tag_id', 'post_mime_type', 'perm', 'comments_per_page', 'post__in', 'post__not_in');40 41 /**42 * Extra query variables set by the user.43 *44 * @since 2.1.045 * @var array46 */47 var $extra_query_vars = array();48 49 /**50 * Query variables for setting up the WordPress Query Loop.51 *52 * @since 2.0.053 * @var array54 */55 var $query_vars;56 57 /**58 * String parsed to set the query variables.59 *60 * @since 2.0.061 * @var string62 */63 var $query_string;64 65 /**66 * Permalink or requested URI.67 *68 * @since 2.0.069 * @var string70 */71 var $request;72 73 /**74 * Rewrite rule the request matched.75 *76 * @since 2.0.077 * @var string78 */79 var $matched_rule;80 81 /**82 * Rewrite query the request matched.83 *84 * @since 2.0.085 * @var string86 */87 var $matched_query;88 89 /**90 * Whether already did the permalink.91 *92 * @since 2.0.093 * @var bool94 */95 var $did_permalink = false;96 97 /**98 * Add name to list of public query variables.99 *100 * @since 2.1.0101 *102 * @param string $qv Query variable name.103 */104 function add_query_var($qv) {105 if ( !in_array($qv, $this->public_query_vars) )106 $this->public_query_vars[] = $qv;107 }108 109 /**110 * Set the value of a query variable.111 *112 * @since 2.3.0113 *114 * @param string $key Query variable name.115 * @param mixed $value Query variable value.116 */117 function set_query_var($key, $value) {118 $this->query_vars[$key] = $value;119 }120 121 /**122 * Parse request to find correct WordPress query.123 *124 * Sets up the query variables based on the request. There are also many125 * filters and actions that can be used to further manipulate the result.126 *127 * @since 2.0.0128 *129 * @param array|string $extra_query_vars Set the extra query variables.130 */131 function parse_request($extra_query_vars = '') {132 global $wp_rewrite;133 134 $this->query_vars = array();135 $post_type_query_vars = array();136 137 if ( is_array($extra_query_vars) )138 $this->extra_query_vars = & $extra_query_vars;139 else if (! empty($extra_query_vars))140 parse_str($extra_query_vars, $this->extra_query_vars);141 142 // Process PATH_INFO, REQUEST_URI, and 404 for permalinks.143 144 // Fetch the rewrite rules.145 $rewrite = $wp_rewrite->wp_rewrite_rules();146 147 if ( ! empty($rewrite) ) {148 // If we match a rewrite rule, this will be cleared.149 $error = '404';150 $this->did_permalink = true;151 152 if ( isset($_SERVER['PATH_INFO']) )153 $pathinfo = $_SERVER['PATH_INFO'];154 else155 $pathinfo = '';156 $pathinfo_array = explode('?', $pathinfo);157 $pathinfo = str_replace("%", "%25", $pathinfo_array[0]);158 $req_uri = $_SERVER['REQUEST_URI'];159 $req_uri_array = explode('?', $req_uri);160 $req_uri = $req_uri_array[0];161 $self = $_SERVER['PHP_SELF'];162 $home_path = parse_url(home_url());163 if ( isset($home_path['path']) )164 $home_path = $home_path['path'];165 else166 $home_path = '';167 $home_path = trim($home_path, '/');168 169 // Trim path info from the end and the leading home path from the170 // front. For path info requests, this leaves us with the requesting171 // filename, if any. For 404 requests, this leaves us with the172 // requested permalink.173 $req_uri = str_replace($pathinfo, '', $req_uri);174 $req_uri = trim($req_uri, '/');175 $req_uri = preg_replace("|^$home_path|", '', $req_uri);176 $req_uri = trim($req_uri, '/');177 $pathinfo = trim($pathinfo, '/');178 $pathinfo = preg_replace("|^$home_path|", '', $pathinfo);179 $pathinfo = trim($pathinfo, '/');180 $self = trim($self, '/');181 $self = preg_replace("|^$home_path|", '', $self);182 $self = trim($self, '/');183 184 // The requested permalink is in $pathinfo for path info requests and185 // $req_uri for other requests.186 if ( ! empty($pathinfo) && !preg_match('|^.*' . $wp_rewrite->index . '$|', $pathinfo) ) {187 $request = $pathinfo;188 } else {189 // If the request uri is the index, blank it out so that we don't try to match it against a rule.190 if ( $req_uri == $wp_rewrite->index )191 $req_uri = '';192 $request = $req_uri;193 }194 195 $this->request = $request;196 197 // Look for matches.198 $request_match = $request;199 foreach ( (array) $rewrite as $match => $query) {200 // Don't try to match against AtomPub calls201 if ( $req_uri == 'wp-app.php' )202 break;203 204 // If the requesting file is the anchor of the match, prepend it205 // to the path info.206 if ( (! empty($req_uri)) && (strpos($match, $req_uri) === 0) && ($req_uri != $request) )207 $request_match = $req_uri . '/' . $request;208 209 if ( preg_match("#^$match#", $request_match, $matches) ||210 preg_match("#^$match#", urldecode($request_match), $matches) ) {211 // Got a match.212 $this->matched_rule = $match;213 214 // Trim the query of everything up to the '?'.215 $query = preg_replace("!^.+\?!", '', $query);216 217 // Substitute the substring matches into the query.218 $query = addslashes(WP_MatchesMapRegex::apply($query, $matches));219 220 $this->matched_query = $query;221 222 // Parse the query.223 parse_str($query, $perma_query_vars);224 225 // If we're processing a 404 request, clear the error var226 // since we found something.227 if ( isset($_GET['error']) )228 unset($_GET['error']);229 230 if ( isset($error) )231 unset($error);232 233 break;234 }235 }236 237 // If req_uri is empty or if it is a request for ourself, unset error.238 if ( empty($request) || $req_uri == $self || strpos($_SERVER['PHP_SELF'], 'wp-admin/') !== false ) {239 if ( isset($_GET['error']) )240 unset($_GET['error']);241 242 if ( isset($error) )243 unset($error);244 245 if ( isset($perma_query_vars) && strpos($_SERVER['PHP_SELF'], 'wp-admin/') !== false )246 unset($perma_query_vars);247 248 $this->did_permalink = false;249 }250 }251 252 $this->public_query_vars = apply_filters('query_vars', $this->public_query_vars);253 254 foreach ( $GLOBALS['wp_post_types'] as $post_type => $t )255 if ( $t->query_var )256 $post_type_query_vars[$t->query_var] = $post_type;257 258 foreach ( $this->public_query_vars as $wpvar ) {259 if ( isset( $this->extra_query_vars[$wpvar] ) )260 $this->query_vars[$wpvar] = $this->extra_query_vars[$wpvar];261 elseif ( isset( $_POST[$wpvar] ) )262 $this->query_vars[$wpvar] = $_POST[$wpvar];263 elseif ( isset( $_GET[$wpvar] ) )264 $this->query_vars[$wpvar] = $_GET[$wpvar];265 elseif ( isset( $perma_query_vars[$wpvar] ) )266 $this->query_vars[$wpvar] = $perma_query_vars[$wpvar];267 268 if ( !empty( $this->query_vars[$wpvar] ) ) {269 if ( ! is_array( $this->query_vars[$wpvar] ) ) {270 $this->query_vars[$wpvar] = (string) $this->query_vars[$wpvar];271 } else {272 foreach ( $this->query_vars[$wpvar] as $vkey => $v ) {273 if ( !is_object( $v ) ) {274 $this->query_vars[$wpvar][$vkey] = (string) $v;275 }276 }277 }278 279 if ( isset($post_type_query_vars[$wpvar] ) ) {280 $this->query_vars['post_type'] = $post_type_query_vars[$wpvar];281 $this->query_vars['name'] = $this->query_vars[$wpvar];282 }283 }284 }285 286 // Limit publicly queried post_types to those that are publicly_queryable287 if ( isset( $this->query_vars['post_type']) ) {288 $queryable_post_types = get_post_types( array('publicly_queryable' => true) );289 if ( ! is_array( $this->query_vars['post_type'] ) ) {290 if ( ! in_array( $this->query_vars['post_type'], $queryable_post_types ) )291 unset( $this->query_vars['post_type'] );292 } else {293 $this->query_vars['post_type'] = array_intersect( $this->query_vars['post_type'], $queryable_post_types );294 }295 }296 297 foreach ( (array) $this->private_query_vars as $var) {298 if ( isset($this->extra_query_vars[$var]) )299 $this->query_vars[$var] = $this->extra_query_vars[$var];300 }301 302 if ( isset($error) )303 $this->query_vars['error'] = $error;304 305 $this->query_vars = apply_filters('request', $this->query_vars);306 307 do_action_ref_array('parse_request', array(&$this));308 }309 310 /**311 * Send additional HTTP headers for caching, content type, etc.312 *313 * Sets the X-Pingback header, 404 status (if 404), Content-type. If showing314 * a feed, it will also send last-modified, etag, and 304 status if needed.315 *316 * @since 2.0.0317 */318 function send_headers() {319 $headers = array('X-Pingback' => get_bloginfo('pingback_url'));320 $status = null;321 $exit_required = false;322 323 if ( is_user_logged_in() )324 $headers = array_merge($headers, wp_get_nocache_headers());325 if ( !empty($this->query_vars['error']) && '404' == $this->query_vars['error'] ) {326 $status = 404;327 if ( !is_user_logged_in() )328 $headers = array_merge($headers, wp_get_nocache_headers());329 $headers['Content-Type'] = get_option('html_type') . '; charset=' . get_option('blog_charset');330 } else if ( empty($this->query_vars['feed']) ) {331 $headers['Content-Type'] = get_option('html_type') . '; charset=' . get_option('blog_charset');332 } else {333 // We're showing a feed, so WP is indeed the only thing that last changed334 if ( !empty($this->query_vars['withcomments'])335 || ( empty($this->query_vars['withoutcomments'])336 && ( !empty($this->query_vars['p'])337 || !empty($this->query_vars['name'])338 || !empty($this->query_vars['page_id'])339 || !empty($this->query_vars['pagename'])340 || !empty($this->query_vars['attachment'])341 || !empty($this->query_vars['attachment_id'])342 )343 )344 )345 $wp_last_modified = mysql2date('D, d M Y H:i:s', get_lastcommentmodified('GMT'), 0).' GMT';346 else347 $wp_last_modified = mysql2date('D, d M Y H:i:s', get_lastpostmodified('GMT'), 0).' GMT';348 $wp_etag = '"' . md5($wp_last_modified) . '"';349 $headers['Last-Modified'] = $wp_last_modified;350 $headers['ETag'] = $wp_etag;351 352 // Support for Conditional GET353 if (isset($_SERVER['HTTP_IF_NONE_MATCH']))354 $client_etag = stripslashes(stripslashes($_SERVER['HTTP_IF_NONE_MATCH']));355 else $client_etag = false;356 357 $client_last_modified = empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? '' : trim($_SERVER['HTTP_IF_MODIFIED_SINCE']);358 // If string is empty, return 0. If not, attempt to parse into a timestamp359 $client_modified_timestamp = $client_last_modified ? strtotime($client_last_modified) : 0;360 361 // Make a timestamp for our most recent modification...362 $wp_modified_timestamp = strtotime($wp_last_modified);363 364 if ( ($client_last_modified && $client_etag) ?365 (($client_modified_timestamp >= $wp_modified_timestamp) && ($client_etag == $wp_etag)) :366 (($client_modified_timestamp >= $wp_modified_timestamp) || ($client_etag == $wp_etag)) ) {367 $status = 304;368 $exit_required = true;369 }370 }371 372 $headers = apply_filters('wp_headers', $headers, $this);373 374 if ( ! empty( $status ) )375 status_header( $status );376 foreach( (array) $headers as $name => $field_value )377 @header("{$name}: {$field_value}");378 379 if ( $exit_required )380 exit();381 382 do_action_ref_array('send_headers', array(&$this));383 }384 385 /**386 * Sets the query string property based off of the query variable property.387 *388 * The 'query_string' filter is deprecated, but still works. Plugins should389 * use the 'request' filter instead.390 *391 * @since 2.0.0392 */393 function build_query_string() {394 $this->query_string = '';395 foreach ( (array) array_keys($this->query_vars) as $wpvar) {396 if ( '' != $this->query_vars[$wpvar] ) {397 $this->query_string .= (strlen($this->query_string) < 1) ? '' : '&';398 if ( !is_scalar($this->query_vars[$wpvar]) ) // Discard non-scalars.399 continue;400 $this->query_string .= $wpvar . '=' . rawurlencode($this->query_vars[$wpvar]);401 }402 }403 404 // query_string filter deprecated. Use request filter instead.405 if ( has_filter('query_string') ) { // Don't bother filtering and parsing if no plugins are hooked in.406 $this->query_string = apply_filters('query_string', $this->query_string);407 parse_str($this->query_string, $this->query_vars);408 }409 }410 411 /**412 * Set up the WordPress Globals.413 *414 * The query_vars property will be extracted to the GLOBALS. So care should415 * be taken when naming global variables that might interfere with the416 * WordPress environment.417 *418 * @global string $query_string Query string for the loop.419 * @global int $more Only set, if single page or post.420 * @global int $single If single page or post. Only set, if single page or post.421 *422 * @since 2.0.0423 */424 function register_globals() {425 global $wp_query;426 // Extract updated query vars back into global namespace.427 foreach ( (array) $wp_query->query_vars as $key => $value) {428 $GLOBALS[$key] = $value;429 }430 431 $GLOBALS['query_string'] = $this->query_string;432 $GLOBALS['posts'] = & $wp_query->posts;433 $GLOBALS['post'] = (isset($wp_query->post)) ? $wp_query->post : null;434 $GLOBALS['request'] = $wp_query->request;435 436 if ( is_single() || is_page() ) {437 $GLOBALS['more'] = 1;438 $GLOBALS['single'] = 1;439 }440 }441 442 /**443 * Set up the current user.444 *445 * @since 2.0.0446 */447 function init() {448 wp_get_current_user();449 }450 451 /**452 * Set up the Loop based on the query variables.453 *454 * @uses WP::$query_vars455 * @since 2.0.0456 */457 function query_posts() {458 global $wp_the_query;459 $this->build_query_string();460 $wp_the_query->query($this->query_vars);461 }462 463 /**464 * Set the Headers for 404, if nothing is found for requested URL.465 *466 * Issue a 404 if a request doesn't match any posts and doesn't match467 * any object (e.g. an existing-but-empty category, tag, author) and a 404 was not already468 * issued, and if the request was not a search or the homepage.469 *470 * Otherwise, issue a 200.471 *472 * @since 2.0.0473 */474 function handle_404() {475 global $wp_query;476 477 if ( !is_admin() && ( 0 == count( $wp_query->posts ) ) && !is_404() && !is_robots() && !is_search() && !is_home() ) {478 // Don't 404 for these queries if they matched an object.479 if ( ( is_tag() || is_category() || is_tax() || is_author() ) && $wp_query->get_queried_object() && !is_paged() ) {480 if ( !is_404() )481 status_header( 200 );482 return;483 }484 $wp_query->set_404();485 status_header( 404 );486 nocache_headers();487 } elseif ( !is_404() ) {488 status_header( 200 );489 }490 }491 492 /**493 * Sets up all of the variables required by the WordPress environment.494 *495 * The action 'wp' has one parameter that references the WP object. It496 * allows for accessing the properties and methods to further manipulate the497 * object.498 *499 * @since 2.0.0500 *501 * @param string|array $query_args Passed to {@link parse_request()}502 */503 function main($query_args = '') {504 $this->init();505 $this->parse_request($query_args);506 $this->send_headers();507 $this->query_posts();508 $this->handle_404();509 $this->register_globals();510 do_action_ref_array('wp', array(&$this));511 }512 513 /**514 * PHP4 Constructor - Does nothing.515 *516 * Call main() method when ready to run setup.517 *518 * @since 2.0.0519 *520 * @return WP521 */522 function WP() {523 // Empty.524 }525 }526 527 /**528 * WordPress Error class.529 *530 * Container for checking for WordPress errors and error messages. Return531 * WP_Error and use {@link is_wp_error()} to check if this class is returned.532 * Many core WordPress functions pass this class in the event of an error and533 * if not handled properly will result in code errors.534 *535 * @package WordPress536 * @since 2.1.0537 */538 class WP_Error {539 /**540 * Stores the list of errors.541 *542 * @since 2.1.0543 * @var array544 * @access private545 */546 var $errors = array();547 548 /**549 * Stores the list of data for error codes.550 *551 * @since 2.1.0552 * @var array553 * @access private554 */555 var $error_data = array();556 557 /**558 * PHP4 Constructor - Sets up error message.559 *560 * If code parameter is empty then nothing will be done. It is possible to561 * add multiple messages to the same code, but with other methods in the562 * class.563 *564 * All parameters are optional, but if the code parameter is set, then the565 * data parameter is optional.566 *567 * @since 2.1.0568 *569 * @param string|int $code Error code570 * @param string $message Error message571 * @param mixed $data Optional. Error data.572 * @return WP_Error573 */574 function WP_Error($code = '', $message = '', $data = '') {575 if ( empty($code) )576 return;577 578 $this->errors[$code][] = $message;579 580 if ( ! empty($data) )581 $this->error_data[$code] = $data;582 }583 584 /**585 * Retrieve all error codes.586 *587 * @since 2.1.0588 * @access public589 *590 * @return array List of error codes, if avaiable.591 */592 function get_error_codes() {593 if ( empty($this->errors) )594 return array();595 596 return array_keys($this->errors);597 }598 599 /**600 * Retrieve first error code available.601 *602 * @since 2.1.0603 * @access public604 *605 * @return string|int Empty string, if no error codes.606 */607 function get_error_code() {608 $codes = $this->get_error_codes();609 610 if ( empty($codes) )611 return '';612 613 return $codes[0];614 }615 616 /**617 * Retrieve all error messages or error messages matching code.618 *619 * @since 2.1.0620 *621 * @param string|int $code Optional. Retrieve messages matching code, if exists.622 * @return array Error strings on success, or empty array on failure (if using codee parameter).623 */624 function get_error_messages($code = '') {625 // Return all messages if no code specified.626 if ( empty($code) ) {627 $all_messages = array();628 foreach ( (array) $this->errors as $code => $messages )629 $all_messages = array_merge($all_messages, $messages);630 631 return $all_messages;632 }633 634 if ( isset($this->errors[$code]) )635 return $this->errors[$code];636 else637 return array();638 }639 640 /**641 * Get single error message.642 *643 * This will get the first message available for the code. If no code is644 * given then the first code available will be used.645 *646 * @since 2.1.0647 *648 * @param string|int $code Optional. Error code to retrieve message.649 * @return string650 */651 function get_error_message($code = '') {652 if ( empty($code) )653 $code = $this->get_error_code();654 $messages = $this->get_error_messages($code);655 if ( empty($messages) )656 return '';657 return $messages[0];658 }659 660 /**661 * Retrieve error data for error code.662 *663 * @since 2.1.0664 *665 * @param string|int $code Optional. Error code.666 * @return mixed Null, if no errors.667 */668 function get_error_data($code = '') {669 if ( empty($code) )670 $code = $this->get_error_code();671 672 if ( isset($this->error_data[$code]) )673 return $this->error_data[$code];674 return null;675 }676 677 /**678 * Append more error messages to list of error messages.679 *680 * @since 2.1.0681 * @access public682 *683 * @param string|int $code Error code.684 * @param string $message Error message.685 * @param mixed $data Optional. Error data.686 */687 function add($code, $message, $data = '') {688 $this->errors[$code][] = $message;689 if ( ! empty($data) )690 $this->error_data[$code] = $data;691 }692 693 /**694 * Add data for error code.695 *696 * The error code can only contain one error data.697 *698 * @since 2.1.0699 *700 * @param mixed $data Error data.701 * @param string|int $code Error code.702 */703 function add_data($data, $code = '') {704 if ( empty($code) )705 $code = $this->get_error_code();706 707 $this->error_data[$code] = $data;708 }709 }710 711 /**712 * Check whether variable is a WordPress Error.713 *714 * Looks at the object and if a WP_Error class. Does not check to see if the715 * parent is also WP_Error, so can't inherit WP_Error and still use this716 * function.717 *718 * @since 2.1.0719 *720 * @param mixed $thing Check if unknown variable is WordPress Error object.721 * @return bool True, if WP_Error. False, if not WP_Error.722 */723 function is_wp_error($thing) {724 if ( is_object($thing) && is_a($thing, 'WP_Error') )725 return true;726 return false;727 }728 12 729 13 /** … … 1124 408 } 1125 409 1126 /**1127 * Create HTML list of pages.1128 *1129 * @package WordPress1130 * @since 2.1.01131 * @uses Walker1132 */1133 class Walker_Page extends Walker {1134 /**1135 * @see Walker::$tree_type1136 * @since 2.1.01137 * @var string1138 */1139 var $tree_type = 'page';1140 1141 /**1142 * @see Walker::$db_fields1143 * @since 2.1.01144 * @todo Decouple this.1145 * @var array1146 */1147 var $db_fields = array ('parent' => 'post_parent', 'id' => 'ID');1148 1149 /**1150 * @see Walker::start_lvl()1151 * @since 2.1.01152 *1153 * @param string $output Passed by reference. Used to append additional content.1154 * @param int $depth Depth of page. Used for padding.1155 */1156 function start_lvl(&$output, $depth) {1157 $indent = str_repeat("\t", $depth);1158 $output .= "\n$indent<ul class='children'>\n";1159 }1160 1161 /**1162 * @see Walker::end_lvl()1163 * @since 2.1.01164 *1165 * @param string $output Passed by reference. Used to append additional content.1166 * @param int $depth Depth of page. Used for padding.1167 */1168 function end_lvl(&$output, $depth) {1169 $indent = str_repeat("\t", $depth);1170 $output .= "$indent</ul>\n";1171 }1172 1173 /**1174 * @see Walker::start_el()1175 * @since 2.1.01176 *1177 * @param string $output Passed by reference. Used to append additional content.1178 * @param object $page Page data object.1179 * @param int $depth Depth of page. Used for padding.1180 * @param int $current_page Page ID.1181 * @param array $args1182 */1183 function start_el(&$output, $page, $depth, $args, $current_page) {1184 if ( $depth )1185 $indent = str_repeat("\t", $depth);1186 else1187 $indent = '';1188 1189 extract($args, EXTR_SKIP);1190 $css_class = array('page_item', 'page-item-'.$page->ID);1191 if ( !empty($current_page) ) {1192 $_current_page = get_page( $current_page );1193 if ( isset($_current_page->ancestors) && in_array($page->ID, (array) $_current_page->ancestors) )1194 $css_class[] = 'current_page_ancestor';1195 if ( $page->ID == $current_page )1196 $css_class[] = 'current_page_item';1197 elseif ( $_current_page && $page->ID == $_current_page->post_parent )1198 $css_class[] = 'current_page_parent';1199 } elseif ( $page->ID == get_option('page_for_posts') ) {1200 $css_class[] = 'current_page_parent';1201 }1202 1203 $css_class = implode(' ', apply_filters('page_css_class', $css_class, $page));1204 1205 $output .= $indent . '<li class="' . $css_class . '"><a href="' . get_permalink($page->ID) . '" title="' . esc_attr( wp_strip_all_tags( apply_filters( 'the_title', $page->post_title, $page->ID ) ) ) . '">' . $link_before . apply_filters( 'the_title', $page->post_title, $page->ID ) . $link_after . '</a>';1206 1207 if ( !empty($show_date) ) {1208 if ( 'modified' == $show_date )1209 $time = $page->post_modified;1210 else1211 $time = $page->post_date;1212 1213 $output .= " " . mysql2date($date_format, $time);1214 }1215 }1216 1217 /**1218 * @see Walker::end_el()1219 * @since 2.1.01220 *1221 * @param string $output Passed by reference. Used to append additional content.1222 * @param object $page Page data object. Not used.1223 * @param int $depth Depth of page. Not Used.1224 */1225 function end_el(&$output, $page, $depth) {1226 $output .= "</li>\n";1227 }1228 1229 }1230 1231 /**1232 * Create HTML dropdown list of pages.1233 *1234 * @package WordPress1235 * @since 2.1.01236 * @uses Walker1237 */1238 class Walker_PageDropdown extends Walker {1239 /**1240 * @see Walker::$tree_type1241 * @since 2.1.01242 * @var string1243 */1244 var $tree_type = 'page';1245 1246 /**1247 * @see Walker::$db_fields1248 * @since 2.1.01249 * @todo Decouple this1250 * @var array1251 */1252 var $db_fields = array ('parent' => 'post_parent', 'id' => 'ID');1253 1254 /**1255 * @see Walker::start_el()1256 * @since 2.1.01257 *1258 * @param string $output Passed by reference. Used to append additional content.1259 * @param object $page Page data object.1260 * @param int $depth Depth of page in reference to parent pages. Used for padding.1261 * @param array $args Uses 'selected' argument for selected page to set selected HTML attribute for option element.1262 */1263 function start_el(&$output, $page, $depth, $args) {1264 $pad = str_repeat(' ', $depth * 3);1265 1266 $output .= "\t<option class=\"level-$depth\" value=\"$page->ID\"";1267 if ( $page->ID == $args['selected'] )1268 $output .= ' selected="selected"';1269 $output .= '>';1270 $title = esc_html($page->post_title);1271 $output .= "$pad$title";1272 $output .= "</option>\n";1273 }1274 }1275 1276 /**1277 * Create HTML list of categories.1278 *1279 * @package WordPress1280 * @since 2.1.01281 * @uses Walker1282 */1283 class Walker_Category extends Walker {1284 /**1285 * @see Walker::$tree_type1286 * @since 2.1.01287 * @var string1288 */1289 var $tree_type = 'category';1290 1291 /**1292 * @see Walker::$db_fields1293 * @since 2.1.01294 * @todo Decouple this1295 * @var array1296 */1297 var $db_fields = array ('parent' => 'parent', 'id' => 'term_id');1298 1299 /**1300 * @see Walker::start_lvl()1301 * @since 2.1.01302 *1303 * @param string $output Passed by reference. Used to append additional content.1304 * @param int $depth Depth of category. Used for tab indentation.1305 * @param array $args Will only append content if style argument value is 'list'.1306 */1307 function start_lvl(&$output, $depth, $args) {1308 if ( 'list' != $args['style'] )1309 return;1310 1311 $indent = str_repeat("\t", $depth);1312 $output .= "$indent<ul class='children'>\n";1313 }1314 1315 /**1316 * @see Walker::end_lvl()1317 * @since 2.1.01318 *1319 * @param string $output Passed by reference. Used to append additional content.1320 * @param int $depth Depth of category. Used for tab indentation.1321 * @param array $args Will only append content if style argument value is 'list'.1322 */1323 function end_lvl(&$output, $depth, $args) {1324 if ( 'list' != $args['style'] )1325 return;1326 1327 $indent = str_repeat("\t", $depth);1328 $output .= "$indent</ul>\n";1329 }1330 1331 /**1332 * @see Walker::start_el()1333 * @since 2.1.01334 *1335 * @param string $output Passed by reference. Used to append additional content.1336 * @param object $category Category data object.1337 * @param int $depth Depth of category in reference to parents.1338 * @param array $args1339 */1340 function start_el(&$output, $category, $depth, $args) {1341 extract($args);1342 1343 $cat_name = esc_attr( $category->name );1344 $cat_name = apply_filters( 'list_cats', $cat_name, $category );1345 $link = '<a href="' . esc_attr( get_term_link($category) ) . '" ';1346 if ( $use_desc_for_title == 0 || empty($category->description) )1347 $link .= 'title="' . sprintf(__( 'View all posts filed under %s' ), $cat_name) . '"';1348 else1349 $link .= 'title="' . esc_attr( strip_tags( apply_filters( 'category_description', $category->description, $category ) ) ) . '"';1350 $link .= '>';1351 $link .= $cat_name . '</a>';1352 1353 if ( !empty($feed_image) || !empty($feed) ) {1354 $link .= ' ';1355 1356 if ( empty($feed_image) )1357 $link .= '(';1358 1359 $link .= '<a href="' . get_term_feed_link( $category->term_id, $category->taxonomy, $feed_type ) . '"';1360 1361 if ( empty($feed) ) {1362 $alt = ' alt="' . sprintf(__( 'Feed for all posts filed under %s' ), $cat_name ) . '"';1363 } else {1364 $title = ' title="' . $feed . '"';1365 $alt = ' alt="' . $feed . '"';1366 $name = $feed;1367 $link .= $title;1368 }1369 1370 $link .= '>';1371 1372 if ( empty($feed_image) )1373 $link .= $name;1374 else1375 $link .= "<img src='$feed_image'$alt$title" . ' />';1376 1377 $link .= '</a>';1378 1379 if ( empty($feed_image) )1380 $link .= ')';1381 }1382 1383 if ( !empty($show_count) )1384 $link .= ' (' . intval($category->count) . ')';1385 1386 if ( !empty($show_date) )1387 $link .= ' ' . gmdate('Y-m-d', $category->last_update_timestamp);1388 1389 if ( 'list' == $args['style'] ) {1390 $output .= "\t<li";1391 $class = 'cat-item cat-item-' . $category->term_id;1392 if ( !empty($current_category) ) {1393 $_current_category = get_term( $current_category, $category->taxonomy );1394 if ( $category->term_id == $current_category )1395 $class .= ' current-cat';1396 elseif ( $category->term_id == $_current_category->parent )1397 $class .= ' current-cat-parent';1398 }1399 $output .= ' class="' . $class . '"';1400 $output .= ">$link\n";1401 } else {1402 $output .= "\t$link<br />\n";1403 }1404 }1405 1406 /**1407 * @see Walker::end_el()1408 * @since 2.1.01409 *1410 * @param string $output Passed by reference. Used to append additional content.1411 * @param object $page Not used.1412 * @param int $depth Depth of category. Not used.1413 * @param array $args Only uses 'list' for whether should append to output.1414 */1415 function end_el(&$output, $page, $depth, $args) {1416 if ( 'list' != $args['style'] )1417 return;1418 1419 $output .= "</li>\n";1420 }1421 1422 }1423 1424 /**1425 * Create HTML dropdown list of Categories.1426 *1427 * @package WordPress1428 * @since 2.1.01429 * @uses Walker1430 */1431 class Walker_CategoryDropdown extends Walker {1432 /**1433 * @see Walker::$tree_type1434 * @since 2.1.01435 * @var string1436 */1437 var $tree_type = 'category';1438 1439 /**1440 * @see Walker::$db_fields1441 * @since 2.1.01442 * @todo Decouple this1443 * @var array1444 */1445 var $db_fields = array ('parent' => 'parent', 'id' => 'term_id');1446 1447 /**1448 * @see Walker::start_el()1449 * @since 2.1.01450 *1451 * @param string $output Passed by reference. Used to append additional content.1452 * @param object $category Category data object.1453 * @param int $depth Depth of category. Used for padding.1454 * @param array $args Uses 'selected', 'show_count', and 'show_last_update' keys, if they exist.1455 */1456 function start_el(&$output, $category, $depth, $args) {1457 $pad = str_repeat(' ', $depth * 3);1458 1459 $cat_name = apply_filters('list_cats', $category->name, $category);1460 $output .= "\t<option class=\"level-$depth\" value=\"".$category->term_id."\"";1461 if ( $category->term_id == $args['selected'] )1462 $output .= ' selected="selected"';1463 $output .= '>';1464 $output .= $pad.$cat_name;1465 if ( $args['show_count'] )1466 $output .= ' ('. $category->count .')';1467 if ( $args['show_last_update'] ) {1468 $format = 'Y-m-d';1469 $output .= ' ' . gmdate($format, $category->last_update_timestamp);1470 }1471 $output .= "</option>\n";1472 }1473 }1474 1475 /**1476 * Send XML response back to AJAX request.1477 *1478 * @package WordPress1479 * @since 2.1.01480 */1481 class WP_Ajax_Response {1482 /**1483 * Store XML responses to send.1484 *1485 * @since 2.1.01486 * @var array1487 * @access private1488 */1489 var $responses = array();1490 1491 /**1492 * PHP4 Constructor - Passes args to {@link WP_Ajax_Response::add()}.1493 *1494 * @since 2.1.01495 * @see WP_Ajax_Response::add()1496 *1497 * @param string|array $args Optional. Will be passed to add() method.1498 * @return WP_Ajax_Response1499 */1500 function WP_Ajax_Response( $args = '' ) {1501 if ( !empty($args) )1502 $this->add($args);1503 }1504 1505 /**1506 * Append to XML response based on given arguments.1507 *1508 * The arguments that can be passed in the $args parameter are below. It is1509 * also possible to pass a WP_Error object in either the 'id' or 'data'1510 * argument. The parameter isn't actually optional, content should be given1511 * in order to send the correct response.1512 *1513 * 'what' argument is a string that is the XMLRPC response type.1514 * 'action' argument is a boolean or string that acts like a nonce.1515 * 'id' argument can be WP_Error or an integer.1516 * 'old_id' argument is false by default or an integer of the previous ID.1517 * 'position' argument is an integer or a string with -1 = top, 1 = bottom,1518 * html ID = after, -html ID = before.1519 * 'data' argument is a string with the content or message.1520 * 'supplemental' argument is an array of strings that will be children of1521 * the supplemental element.1522 *1523 * @since 2.1.01524 *1525 * @param string|array $args Override defaults.1526 * @return string XML response.1527 */1528 function add( $args = '' ) {1529 $defaults = array(1530 'what' => 'object', 'action' => false,1531 'id' => '0', 'old_id' => false,1532 'position' => 1,1533 'data' => '', 'supplemental' => array()1534 );1535 1536 $r = wp_parse_args( $args, $defaults );1537 extract( $r, EXTR_SKIP );1538 $position = preg_replace( '/[^a-z0-9:_-]/i', '', $position );1539 1540 if ( is_wp_error($id) ) {1541 $data = $id;1542 $id = 0;1543 }1544 1545 $response = '';1546 if ( is_wp_error($data) ) {1547 foreach ( (array) $data->get_error_codes() as $code ) {1548 $response .= "<wp_error code='$code'><![CDATA[" . $data->get_error_message($code) . "]]></wp_error>";1549 if ( !$error_data = $data->get_error_data($code) )1550 continue;1551 $class = '';1552 if ( is_object($error_data) ) {1553 $class = ' class="' . get_class($error_data) . '"';1554 $error_data = get_object_vars($error_data);1555 }1556 1557 $response .= "<wp_error_data code='$code'$class>";1558 1559 if ( is_scalar($error_data) ) {1560 $response .= "<![CDATA[$error_data]]>";1561 } elseif ( is_array($error_data) ) {1562 foreach ( $error_data as $k => $v )1563 $response .= "<$k><![CDATA[$v]]></$k>";1564 }1565 1566 $response .= "</wp_error_data>";1567 }1568 } else {1569 $response = "<response_data><![CDATA[$data]]></response_data>";1570 }1571 1572 $s = '';1573 if ( is_array($supplemental) ) {1574 foreach ( $supplemental as $k => $v )1575 $s .= "<$k><![CDATA[$v]]></$k>";1576 $s = "<supplemental>$s</supplemental>";1577 }1578 1579 if ( false === $action )1580 $action = $_POST['action'];1581 1582 $x = '';1583 $x .= "<response action='{$action}_$id'>"; // The action attribute in the xml output is formatted like a nonce action1584 $x .= "<$what id='$id' " . ( false === $old_id ? '' : "old_id='$old_id' " ) . "position='$position'>";1585 $x .= $response;1586 $x .= $s;1587 $x .= "</$what>";1588 $x .= "</response>";1589 1590 $this->responses[] = $x;1591 return $x;1592 }1593 1594 /**1595 * Display XML formatted responses.1596 *1597 * Sets the content type header to text/xml.1598 *1599 * @since 2.1.01600 */1601 function send() {1602 header('Content-Type: text/xml');1603 echo "<?xml version='1.0' standalone='yes'?><wp_ajax>";1604 foreach ( (array) $this->responses as $response )1605 echo $response;1606 echo '</wp_ajax>';1607 die();1608 }1609 }1610 1611 /**1612 * Helper class to remove the need to use eval to replace $matches[] in query strings.1613 *1614 * @since 2.9.01615 */1616 class WP_MatchesMapRegex {1617 /**1618 * store for matches1619 *1620 * @access private1621 * @var array1622 */1623 var $_matches;1624 1625 /**1626 * store for mapping result1627 *1628 * @access public1629 * @var string1630 */1631 var $output;1632 1633 /**1634 * subject to perform mapping on (query string containing $matches[] references1635 *1636 * @access private1637 * @var string1638 */1639 var $_subject;1640 1641 /**1642 * regexp pattern to match $matches[] references1643 *1644 * @var string1645 */1646 var $_pattern = '(\$matches\[[1-9]+[0-9]*\])'; // magic number1647 1648 /**1649 * constructor1650 *1651 * @param string $subject subject if regex1652 * @param array $matches data to use in map1653 * @return self1654 */1655 function WP_MatchesMapRegex($subject, $matches) {1656 $this->_subject = $subject;1657 $this->_matches = $matches;1658 $this->output = $this->_map();1659 }1660 1661 /**1662 * Substitute substring matches in subject.1663 *1664 * static helper function to ease use1665 *1666 * @access public1667 * @param string $subject subject1668 * @param array $matches data used for subsitution1669 * @return string1670 */1671 function apply($subject, $matches) {1672 $oSelf =& new WP_MatchesMapRegex($subject, $matches);1673 return $oSelf->output;1674 }1675 1676 /**1677 * do the actual mapping1678 *1679 * @access private1680 * @return string1681 */1682 function _map() {1683 $callback = array(&$this, 'callback');1684 return preg_replace_callback($this->_pattern, $callback, $this->_subject);1685 }1686 1687 /**1688 * preg_replace_callback hook1689 *1690 * @access public1691 * @param array $matches preg_replace regexp matches1692 * @return string1693 */1694 function callback($matches) {1695 $index = intval(substr($matches[0], 9, -1));1696 return ( isset( $this->_matches[$index] ) ? urlencode($this->_matches[$index]) : '' );1697 }1698 1699 }1700 1701 410 ?> -
trunk/wp-includes/class-wp.php
r16091 r16099 526 526 527 527 /** 528 * WordPress Error class.529 *530 * Container for checking for WordPress errors and error messages. Return531 * WP_Error and use {@link is_wp_error()} to check if this class is returned.532 * Many core WordPress functions pass this class in the event of an error and533 * if not handled properly will result in code errors.534 *535 * @package WordPress536 * @since 2.1.0537 */538 class WP_Error {539 /**540 * Stores the list of errors.541 *542 * @since 2.1.0543 * @var array544 * @access private545 */546 var $errors = array();547 548 /**549 * Stores the list of data for error codes.550 *551 * @since 2.1.0552 * @var array553 * @access private554 */555 var $error_data = array();556 557 /**558 * PHP4 Constructor - Sets up error message.559 *560 * If code parameter is empty then nothing will be done. It is possible to561 * add multiple messages to the same code, but with other methods in the562 * class.563 *564 * All parameters are optional, but if the code parameter is set, then the565 * data parameter is optional.566 *567 * @since 2.1.0568 *569 * @param string|int $code Error code570 * @param string $message Error message571 * @param mixed $data Optional. Error data.572 * @return WP_Error573 */574 function WP_Error($code = '', $message = '', $data = '') {575 if ( empty($code) )576 return;577 578 $this->errors[$code][] = $message;579 580 if ( ! empty($data) )581 $this->error_data[$code] = $data;582 }583 584 /**585 * Retrieve all error codes.586 *587 * @since 2.1.0588 * @access public589 *590 * @return array List of error codes, if avaiable.591 */592 function get_error_codes() {593 if ( empty($this->errors) )594 return array();595 596 return array_keys($this->errors);597 }598 599 /**600 * Retrieve first error code available.601 *602 * @since 2.1.0603 * @access public604 *605 * @return string|int Empty string, if no error codes.606 */607 function get_error_code() {608 $codes = $this->get_error_codes();609 610 if ( empty($codes) )611 return '';612 613 return $codes[0];614 }615 616 /**617 * Retrieve all error messages or error messages matching code.618 *619 * @since 2.1.0620 *621 * @param string|int $code Optional. Retrieve messages matching code, if exists.622 * @return array Error strings on success, or empty array on failure (if using codee parameter).623 */624 function get_error_messages($code = '') {625 // Return all messages if no code specified.626 if ( empty($code) ) {627 $all_messages = array();628 foreach ( (array) $this->errors as $code => $messages )629 $all_messages = array_merge($all_messages, $messages);630 631 return $all_messages;632 }633 634 if ( isset($this->errors[$code]) )635 return $this->errors[$code];636 else637 return array();638 }639 640 /**641 * Get single error message.642 *643 * This will get the first message available for the code. If no code is644 * given then the first code available will be used.645 *646 * @since 2.1.0647 *648 * @param string|int $code Optional. Error code to retrieve message.649 * @return string650 */651 function get_error_message($code = '') {652 if ( empty($code) )653 $code = $this->get_error_code();654 $messages = $this->get_error_messages($code);655 if ( empty($messages) )656 return '';657 return $messages[0];658 }659 660 /**661 * Retrieve error data for error code.662 *663 * @since 2.1.0664 *665 * @param string|int $code Optional. Error code.666 * @return mixed Null, if no errors.667 */668 function get_error_data($code = '') {669 if ( empty($code) )670 $code = $this->get_error_code();671 672 if ( isset($this->error_data[$code]) )673 return $this->error_data[$code];674 return null;675 }676 677 /**678 * Append more error messages to list of error messages.679 *680 * @since 2.1.0681 * @access public682 *683 * @param string|int $code Error code.684 * @param string $message Error message.685 * @param mixed $data Optional. Error data.686 */687 function add($code, $message, $data = '') {688 $this->errors[$code][] = $message;689 if ( ! empty($data) )690 $this->error_data[$code] = $data;691 }692 693 /**694 * Add data for error code.695 *696 * The error code can only contain one error data.697 *698 * @since 2.1.0699 *700 * @param mixed $data Error data.701 * @param string|int $code Error code.702 */703 function add_data($data, $code = '') {704 if ( empty($code) )705 $code = $this->get_error_code();706 707 $this->error_data[$code] = $data;708 }709 }710 711 /**712 * Check whether variable is a WordPress Error.713 *714 * Looks at the object and if a WP_Error class. Does not check to see if the715 * parent is also WP_Error, so can't inherit WP_Error and still use this716 * function.717 *718 * @since 2.1.0719 *720 * @param mixed $thing Check if unknown variable is WordPress Error object.721 * @return bool True, if WP_Error. False, if not WP_Error.722 */723 function is_wp_error($thing) {724 if ( is_object($thing) && is_a($thing, 'WP_Error') )725 return true;726 return false;727 }728 729 /**730 * A class for displaying various tree-like structures.731 *732 * Extend the Walker class to use it, see examples at the below. Child classes733 * do not need to implement all of the abstract methods in the class. The child734 * only needs to implement the methods that are needed. Also, the methods are735 * not strictly abstract in that the parameter definition needs to be followed.736 * The child classes can have additional parameters.737 *738 * @package WordPress739 * @since 2.1.0740 * @abstract741 */742 class Walker {743 /**744 * What the class handles.745 *746 * @since 2.1.0747 * @var string748 * @access public749 */750 var $tree_type;751 752 /**753 * DB fields to use.754 *755 * @since 2.1.0756 * @var array757 * @access protected758 */759 var $db_fields;760 761 /**762 * Max number of pages walked by the paged walker763 *764 * @since 2.7.0765 * @var int766 * @access protected767 */768 var $max_pages = 1;769 770 /**771 * Starts the list before the elements are added.772 *773 * Additional parameters are used in child classes. The args parameter holds774 * additional values that may be used with the child class methods. This775 * method is called at the start of the output list.776 *777 * @since 2.1.0778 * @abstract779 *780 * @param string $output Passed by reference. Used to append additional content.781 */782 function start_lvl(&$output) {}783 784 /**785 * Ends the list of after the elements are added.786 *787 * Additional parameters are used in child classes. The args parameter holds788 * additional values that may be used with the child class methods. This789 * method finishes the list at the end of output of the elements.790 *791 * @since 2.1.0792 * @abstract793 *794 * @param string $output Passed by reference. Used to append additional content.795 */796 function end_lvl(&$output) {}797 798 /**799 * Start the element output.800 *801 * Additional parameters are used in child classes. The args parameter holds802 * additional values that may be used with the child class methods. Includes803 * the element output also.804 *805 * @since 2.1.0806 * @abstract807 *808 * @param string $output Passed by reference. Used to append additional content.809 */810 function start_el(&$output) {}811 812 /**813 * Ends the element output, if needed.814 *815 * Additional parameters are used in child classes. The args parameter holds816 * additional values that may be used with the child class methods.817 *818 * @since 2.1.0819 * @abstract820 *821 * @param string $output Passed by reference. Used to append additional content.822 */823 function end_el(&$output) {}824 825 /**826 * Traverse elements to create list from elements.827 *828 * Display one element if the element doesn't have any children otherwise,829 * display the element and its children. Will only traverse up to the max830 * depth and no ignore elements under that depth. It is possible to set the831 * max depth to include all depths, see walk() method.832 *833 * This method shouldn't be called directly, use the walk() method instead.834 *835 * @since 2.5.0836 *837 * @param object $element Data object838 * @param array $children_elements List of elements to continue traversing.839 * @param int $max_depth Max depth to traverse.840 * @param int $depth Depth of current element.841 * @param array $args842 * @param string $output Passed by reference. Used to append additional content.843 * @return null Null on failure with no changes to parameters.844 */845 function display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output ) {846 847 if ( !$element )848 return;849 850 $id_field = $this->db_fields['id'];851 852 //display this element853 if ( is_array( $args[0] ) )854 $args[0]['has_children'] = ! empty( $children_elements[$element->$id_field] );855 $cb_args = array_merge( array(&$output, $element, $depth), $args);856 call_user_func_array(array(&$this, 'start_el'), $cb_args);857 858 $id = $element->$id_field;859 860 // descend only when the depth is right and there are childrens for this element861 if ( ($max_depth == 0 || $max_depth > $depth+1 ) && isset( $children_elements[$id]) ) {862 863 foreach( $children_elements[ $id ] as $child ){864 865 if ( !isset($newlevel) ) {866 $newlevel = true;867 //start the child delimiter868 $cb_args = array_merge( array(&$output, $depth), $args);869 call_user_func_array(array(&$this, 'start_lvl'), $cb_args);870 }871 $this->display_element( $child, $children_elements, $max_depth, $depth + 1, $args, $output );872 }873 unset( $children_elements[ $id ] );874 }875 876 if ( isset($newlevel) && $newlevel ){877 //end the child delimiter878 $cb_args = array_merge( array(&$output, $depth), $args);879 call_user_func_array(array(&$this, 'end_lvl'), $cb_args);880 }881 882 //end this element883 $cb_args = array_merge( array(&$output, $element, $depth), $args);884 call_user_func_array(array(&$this, 'end_el'), $cb_args);885 }886 887 /**888 * Display array of elements hierarchically.889 *890 * It is a generic function which does not assume any existing order of891 * elements. max_depth = -1 means flatly display every element. max_depth =892 * 0 means display all levels. max_depth > 0 specifies the number of893 * display levels.894 *895 * @since 2.1.0896 *897 * @param array $elements898 * @param int $max_depth899 * @return string900 */901 function walk( $elements, $max_depth) {902 903 $args = array_slice(func_get_args(), 2);904 $output = '';905 906 if ($max_depth < -1) //invalid parameter907 return $output;908 909 if (empty($elements)) //nothing to walk910 return $output;911 912 $id_field = $this->db_fields['id'];913 $parent_field = $this->db_fields['parent'];914 915 // flat display916 if ( -1 == $max_depth ) {917 $empty_array = array();918 foreach ( $elements as $e )919 $this->display_element( $e, $empty_array, 1, 0, $args, $output );920 return $output;921 }922 923 /*924 * need to display in hierarchical order925 * separate elements into two buckets: top level and children elements926 * children_elements is two dimensional array, eg.927 * children_elements[10][] contains all sub-elements whose parent is 10.928 */929 $top_level_elements = array();930 $children_elements = array();931 foreach ( $elements as $e) {932 if ( 0 == $e->$parent_field )933 $top_level_elements[] = $e;934 else935 $children_elements[ $e->$parent_field ][] = $e;936 }937 938 /*939 * when none of the elements is top level940 * assume the first one must be root of the sub elements941 */942 if ( empty($top_level_elements) ) {943 944 $first = array_slice( $elements, 0, 1 );945 $root = $first[0];946 947 $top_level_elements = array();948 $children_elements = array();949 foreach ( $elements as $e) {950 if ( $root->$parent_field == $e->$parent_field )951 $top_level_elements[] = $e;952 else953 $children_elements[ $e->$parent_field ][] = $e;954 }955 }956 957 foreach ( $top_level_elements as $e )958 $this->display_element( $e, $children_elements, $max_depth, 0, $args, $output );959 960 /*961 * if we are displaying all levels, and remaining children_elements is not empty,962 * then we got orphans, which should be displayed regardless963 */964 if ( ( $max_depth == 0 ) && count( $children_elements ) > 0 ) {965 $empty_array = array();966 foreach ( $children_elements as $orphans )967 foreach( $orphans as $op )968 $this->display_element( $op, $empty_array, 1, 0, $args, $output );969 }970 971 return $output;972 }973 974 /**975 * paged_walk() - produce a page of nested elements976 *977 * Given an array of hierarchical elements, the maximum depth, a specific page number,978 * and number of elements per page, this function first determines all top level root elements979 * belonging to that page, then lists them and all of their children in hierarchical order.980 *981 * @package WordPress982 * @since 2.7983 * @param int $max_depth = 0 means display all levels; $max_depth > 0 specifies the number of display levels.984 * @param int $page_num the specific page number, beginning with 1.985 * @return XHTML of the specified page of elements986 */987 function paged_walk( $elements, $max_depth, $page_num, $per_page ) {988 989 /* sanity check */990 if ( empty($elements) || $max_depth < -1 )991 return '';992 993 $args = array_slice( func_get_args(), 4 );994 $output = '';995 996 $id_field = $this->db_fields['id'];997 $parent_field = $this->db_fields['parent'];998 999 $count = -1;1000 if ( -1 == $max_depth )1001 $total_top = count( $elements );1002 if ( $page_num < 1 || $per_page < 0 ) {1003 // No paging1004 $paging = false;1005 $start = 0;1006 if ( -1 == $max_depth )1007 $end = $total_top;1008 $this->max_pages = 1;1009 } else {1010 $paging = true;1011 $start = ( (int)$page_num - 1 ) * (int)$per_page;1012 $end = $start + $per_page;1013 if ( -1 == $max_depth )1014 $this->max_pages = ceil($total_top / $per_page);1015 }1016 1017 // flat display1018 if ( -1 == $max_depth ) {1019 if ( !empty($args[0]['reverse_top_level']) ) {1020 $elements = array_reverse( $elements );1021 $oldstart = $start;1022 $start = $total_top - $end;1023 $end = $total_top - $oldstart;1024 }1025 1026 $empty_array = array();1027 foreach ( $elements as $e ) {1028 $count++;1029 if ( $count < $start )1030 continue;1031 if ( $count >= $end )1032 break;1033 $this->display_element( $e, $empty_array, 1, 0, $args, $output );1034 }1035 return $output;1036 }1037 1038 /*1039 * separate elements into two buckets: top level and children elements1040 * children_elements is two dimensional array, eg.1041 * children_elements[10][] contains all sub-elements whose parent is 10.1042 */1043 $top_level_elements = array();1044 $children_elements = array();1045 foreach ( $elements as $e) {1046 if ( 0 == $e->$parent_field )1047 $top_level_elements[] = $e;1048 else1049 $children_elements[ $e->$parent_field ][] = $e;1050 }1051 1052 $total_top = count( $top_level_elements );1053 if ( $paging )1054 $this->max_pages = ceil($total_top / $per_page);1055 else1056 $end = $total_top;1057 1058 if ( !empty($args[0]['reverse_top_level']) ) {1059 $top_level_elements = array_reverse( $top_level_elements );1060 $oldstart = $start;1061 $start = $total_top - $end;1062 $end = $total_top - $oldstart;1063 }1064 if ( !empty($args[0]['reverse_children']) ) {1065 foreach ( $children_elements as $parent => $children )1066 $children_elements[$parent] = array_reverse( $children );1067 }1068 1069 foreach ( $top_level_elements as $e ) {1070 $count++;1071 1072 //for the last page, need to unset earlier children in order to keep track of orphans1073 if ( $end >= $total_top && $count < $start )1074 $this->unset_children( $e, $children_elements );1075 1076 if ( $count < $start )1077 continue;1078 1079 if ( $count >= $end )1080 break;1081 1082 $this->display_element( $e, $children_elements, $max_depth, 0, $args, $output );1083 }1084 1085 if ( $end >= $total_top && count( $children_elements ) > 0 ) {1086 $empty_array = array();1087 foreach ( $children_elements as $orphans )1088 foreach( $orphans as $op )1089 $this->display_element( $op, $empty_array, 1, 0, $args, $output );1090 }1091 1092 return $output;1093 }1094 1095 function get_number_of_root_elements( $elements ){1096 1097 $num = 0;1098 $parent_field = $this->db_fields['parent'];1099 1100 foreach ( $elements as $e) {1101 if ( 0 == $e->$parent_field )1102 $num++;1103 }1104 return $num;1105 }1106 1107 // unset all the children for a given top level element1108 function unset_children( $e, &$children_elements ){1109 1110 if ( !$e || !$children_elements )1111 return;1112 1113 $id_field = $this->db_fields['id'];1114 $id = $e->$id_field;1115 1116 if ( !empty($children_elements[$id]) && is_array($children_elements[$id]) )1117 foreach ( (array) $children_elements[$id] as $child )1118 $this->unset_children( $child, $children_elements );1119 1120 if ( isset($children_elements[$id]) )1121 unset( $children_elements[$id] );1122 1123 }1124 }1125 1126 /**1127 * Create HTML list of pages.1128 *1129 * @package WordPress1130 * @since 2.1.01131 * @uses Walker1132 */1133 class Walker_Page extends Walker {1134 /**1135 * @see Walker::$tree_type1136 * @since 2.1.01137 * @var string1138 */1139 var $tree_type = 'page';1140 1141 /**1142 * @see Walker::$db_fields1143 * @since 2.1.01144 * @todo Decouple this.1145 * @var array1146 */1147 var $db_fields = array ('parent' => 'post_parent', 'id' => 'ID');1148 1149 /**1150 * @see Walker::start_lvl()1151 * @since 2.1.01152 *1153 * @param string $output Passed by reference. Used to append additional content.1154 * @param int $depth Depth of page. Used for padding.1155 */1156 function start_lvl(&$output, $depth) {1157 $indent = str_repeat("\t", $depth);1158 $output .= "\n$indent<ul class='children'>\n";1159 }1160 1161 /**1162 * @see Walker::end_lvl()1163 * @since 2.1.01164 *1165 * @param string $output Passed by reference. Used to append additional content.1166 * @param int $depth Depth of page. Used for padding.1167 */1168 function end_lvl(&$output, $depth) {1169 $indent = str_repeat("\t", $depth);1170 $output .= "$indent</ul>\n";1171 }1172 1173 /**1174 * @see Walker::start_el()1175 * @since 2.1.01176 *1177 * @param string $output Passed by reference. Used to append additional content.1178 * @param object $page Page data object.1179 * @param int $depth Depth of page. Used for padding.1180 * @param int $current_page Page ID.1181 * @param array $args1182 */1183 function start_el(&$output, $page, $depth, $args, $current_page) {1184 if ( $depth )1185 $indent = str_repeat("\t", $depth);1186 else1187 $indent = '';1188 1189 extract($args, EXTR_SKIP);1190 $css_class = array('page_item', 'page-item-'.$page->ID);1191 if ( !empty($current_page) ) {1192 $_current_page = get_page( $current_page );1193 if ( isset($_current_page->ancestors) && in_array($page->ID, (array) $_current_page->ancestors) )1194 $css_class[] = 'current_page_ancestor';1195 if ( $page->ID == $current_page )1196 $css_class[] = 'current_page_item';1197 elseif ( $_current_page && $page->ID == $_current_page->post_parent )1198 $css_class[] = 'current_page_parent';1199 } elseif ( $page->ID == get_option('page_for_posts') ) {1200 $css_class[] = 'current_page_parent';1201 }1202 1203 $css_class = implode(' ', apply_filters('page_css_class', $css_class, $page));1204 1205 $output .= $indent . '<li class="' . $css_class . '"><a href="' . get_permalink($page->ID) . '" title="' . esc_attr( wp_strip_all_tags( apply_filters( 'the_title', $page->post_title, $page->ID ) ) ) . '">' . $link_before . apply_filters( 'the_title', $page->post_title, $page->ID ) . $link_after . '</a>';1206 1207 if ( !empty($show_date) ) {1208 if ( 'modified' == $show_date )1209 $time = $page->post_modified;1210 else1211 $time = $page->post_date;1212 1213 $output .= " " . mysql2date($date_format, $time);1214 }1215 }1216 1217 /**1218 * @see Walker::end_el()1219 * @since 2.1.01220 *1221 * @param string $output Passed by reference. Used to append additional content.1222 * @param object $page Page data object. Not used.1223 * @param int $depth Depth of page. Not Used.1224 */1225 function end_el(&$output, $page, $depth) {1226 $output .= "</li>\n";1227 }1228 1229 }1230 1231 /**1232 * Create HTML dropdown list of pages.1233 *1234 * @package WordPress1235 * @since 2.1.01236 * @uses Walker1237 */1238 class Walker_PageDropdown extends Walker {1239 /**1240 * @see Walker::$tree_type1241 * @since 2.1.01242 * @var string1243 */1244 var $tree_type = 'page';1245 1246 /**1247 * @see Walker::$db_fields1248 * @since 2.1.01249 * @todo Decouple this1250 * @var array1251 */1252 var $db_fields = array ('parent' => 'post_parent', 'id' => 'ID');1253 1254 /**1255 * @see Walker::start_el()1256 * @since 2.1.01257 *1258 * @param string $output Passed by reference. Used to append additional content.1259 * @param object $page Page data object.1260 * @param int $depth Depth of page in reference to parent pages. Used for padding.1261 * @param array $args Uses 'selected' argument for selected page to set selected HTML attribute for option element.1262 */1263 function start_el(&$output, $page, $depth, $args) {1264 $pad = str_repeat(' ', $depth * 3);1265 1266 $output .= "\t<option class=\"level-$depth\" value=\"$page->ID\"";1267 if ( $page->ID == $args['selected'] )1268 $output .= ' selected="selected"';1269 $output .= '>';1270 $title = esc_html($page->post_title);1271 $output .= "$pad$title";1272 $output .= "</option>\n";1273 }1274 }1275 1276 /**1277 * Create HTML list of categories.1278 *1279 * @package WordPress1280 * @since 2.1.01281 * @uses Walker1282 */1283 class Walker_Category extends Walker {1284 /**1285 * @see Walker::$tree_type1286 * @since 2.1.01287 * @var string1288 */1289 var $tree_type = 'category';1290 1291 /**1292 * @see Walker::$db_fields1293 * @since 2.1.01294 * @todo Decouple this1295 * @var array1296 */1297 var $db_fields = array ('parent' => 'parent', 'id' => 'term_id');1298 1299 /**1300 * @see Walker::start_lvl()1301 * @since 2.1.01302 *1303 * @param string $output Passed by reference. Used to append additional content.1304 * @param int $depth Depth of category. Used for tab indentation.1305 * @param array $args Will only append content if style argument value is 'list'.1306 */1307 function start_lvl(&$output, $depth, $args) {1308 if ( 'list' != $args['style'] )1309 return;1310 1311 $indent = str_repeat("\t", $depth);1312 $output .= "$indent<ul class='children'>\n";1313 }1314 1315 /**1316 * @see Walker::end_lvl()1317 * @since 2.1.01318 *1319 * @param string $output Passed by reference. Used to append additional content.1320 * @param int $depth Depth of category. Used for tab indentation.1321 * @param array $args Will only append content if style argument value is 'list'.1322 */1323 function end_lvl(&$output, $depth, $args) {1324 if ( 'list' != $args['style'] )1325 return;1326 1327 $indent = str_repeat("\t", $depth);1328 $output .= "$indent</ul>\n";1329 }1330 1331 /**1332 * @see Walker::start_el()1333 * @since 2.1.01334 *1335 * @param string $output Passed by reference. Used to append additional content.1336 * @param object $category Category data object.1337 * @param int $depth Depth of category in reference to parents.1338 * @param array $args1339 */1340 function start_el(&$output, $category, $depth, $args) {1341 extract($args);1342 1343 $cat_name = esc_attr( $category->name );1344 $cat_name = apply_filters( 'list_cats', $cat_name, $category );1345 $link = '<a href="' . esc_attr( get_term_link($category) ) . '" ';1346 if ( $use_desc_for_title == 0 || empty($category->description) )1347 $link .= 'title="' . sprintf(__( 'View all posts filed under %s' ), $cat_name) . '"';1348 else1349 $link .= 'title="' . esc_attr( strip_tags( apply_filters( 'category_description', $category->description, $category ) ) ) . '"';1350 $link .= '>';1351 $link .= $cat_name . '</a>';1352 1353 if ( !empty($feed_image) || !empty($feed) ) {1354 $link .= ' ';1355 1356 if ( empty($feed_image) )1357 $link .= '(';1358 1359 $link .= '<a href="' . get_term_feed_link( $category->term_id, $category->taxonomy, $feed_type ) . '"';1360 1361 if ( empty($feed) ) {1362 $alt = ' alt="' . sprintf(__( 'Feed for all posts filed under %s' ), $cat_name ) . '"';1363 } else {1364 $title = ' title="' . $feed . '"';1365 $alt = ' alt="' . $feed . '"';1366 $name = $feed;1367 $link .= $title;1368 }1369 1370 $link .= '>';1371 1372 if ( empty($feed_image) )1373 $link .= $name;1374 else1375 $link .= "<img src='$feed_image'$alt$title" . ' />';1376 1377 $link .= '</a>';1378 1379 if ( empty($feed_image) )1380 $link .= ')';1381 }1382 1383 if ( !empty($show_count) )1384 $link .= ' (' . intval($category->count) . ')';1385 1386 if ( !empty($show_date) )1387 $link .= ' ' . gmdate('Y-m-d', $category->last_update_timestamp);1388 1389 if ( 'list' == $args['style'] ) {1390 $output .= "\t<li";1391 $class = 'cat-item cat-item-' . $category->term_id;1392 if ( !empty($current_category) ) {1393 $_current_category = get_term( $current_category, $category->taxonomy );1394 if ( $category->term_id == $current_category )1395 $class .= ' current-cat';1396 elseif ( $category->term_id == $_current_category->parent )1397 $class .= ' current-cat-parent';1398 }1399 $output .= ' class="' . $class . '"';1400 $output .= ">$link\n";1401 } else {1402 $output .= "\t$link<br />\n";1403 }1404 }1405 1406 /**1407 * @see Walker::end_el()1408 * @since 2.1.01409 *1410 * @param string $output Passed by reference. Used to append additional content.1411 * @param object $page Not used.1412 * @param int $depth Depth of category. Not used.1413 * @param array $args Only uses 'list' for whether should append to output.1414 */1415 function end_el(&$output, $page, $depth, $args) {1416 if ( 'list' != $args['style'] )1417 return;1418 1419 $output .= "</li>\n";1420 }1421 1422 }1423 1424 /**1425 * Create HTML dropdown list of Categories.1426 *1427 * @package WordPress1428 * @since 2.1.01429 * @uses Walker1430 */1431 class Walker_CategoryDropdown extends Walker {1432 /**1433 * @see Walker::$tree_type1434 * @since 2.1.01435 * @var string1436 */1437 var $tree_type = 'category';1438 1439 /**1440 * @see Walker::$db_fields1441 * @since 2.1.01442 * @todo Decouple this1443 * @var array1444 */1445 var $db_fields = array ('parent' => 'parent', 'id' => 'term_id');1446 1447 /**1448 * @see Walker::start_el()1449 * @since 2.1.01450 *1451 * @param string $output Passed by reference. Used to append additional content.1452 * @param object $category Category data object.1453 * @param int $depth Depth of category. Used for padding.1454 * @param array $args Uses 'selected', 'show_count', and 'show_last_update' keys, if they exist.1455 */1456 function start_el(&$output, $category, $depth, $args) {1457 $pad = str_repeat(' ', $depth * 3);1458 1459 $cat_name = apply_filters('list_cats', $category->name, $category);1460 $output .= "\t<option class=\"level-$depth\" value=\"".$category->term_id."\"";1461 if ( $category->term_id == $args['selected'] )1462 $output .= ' selected="selected"';1463 $output .= '>';1464 $output .= $pad.$cat_name;1465 if ( $args['show_count'] )1466 $output .= ' ('. $category->count .')';1467 if ( $args['show_last_update'] ) {1468 $format = 'Y-m-d';1469 $output .= ' ' . gmdate($format, $category->last_update_timestamp);1470 }1471 $output .= "</option>\n";1472 }1473 }1474 1475 /**1476 * Send XML response back to AJAX request.1477 *1478 * @package WordPress1479 * @since 2.1.01480 */1481 class WP_Ajax_Response {1482 /**1483 * Store XML responses to send.1484 *1485 * @since 2.1.01486 * @var array1487 * @access private1488 */1489 var $responses = array();1490 1491 /**1492 * PHP4 Constructor - Passes args to {@link WP_Ajax_Response::add()}.1493 *1494 * @since 2.1.01495 * @see WP_Ajax_Response::add()1496 *1497 * @param string|array $args Optional. Will be passed to add() method.1498 * @return WP_Ajax_Response1499 */1500 function WP_Ajax_Response( $args = '' ) {1501 if ( !empty($args) )1502 $this->add($args);1503 }1504 1505 /**1506 * Append to XML response based on given arguments.1507 *1508 * The arguments that can be passed in the $args parameter are below. It is1509 * also possible to pass a WP_Error object in either the 'id' or 'data'1510 * argument. The parameter isn't actually optional, content should be given1511 * in order to send the correct response.1512 *1513 * 'what' argument is a string that is the XMLRPC response type.1514 * 'action' argument is a boolean or string that acts like a nonce.1515 * 'id' argument can be WP_Error or an integer.1516 * 'old_id' argument is false by default or an integer of the previous ID.1517 * 'position' argument is an integer or a string with -1 = top, 1 = bottom,1518 * html ID = after, -html ID = before.1519 * 'data' argument is a string with the content or message.1520 * 'supplemental' argument is an array of strings that will be children of1521 * the supplemental element.1522 *1523 * @since 2.1.01524 *1525 * @param string|array $args Override defaults.1526 * @return string XML response.1527 */1528 function add( $args = '' ) {1529 $defaults = array(1530 'what' => 'object', 'action' => false,1531 'id' => '0', 'old_id' => false,1532 'position' => 1,1533 'data' => '', 'supplemental' => array()1534 );1535 1536 $r = wp_parse_args( $args, $defaults );1537 extract( $r, EXTR_SKIP );1538 $position = preg_replace( '/[^a-z0-9:_-]/i', '', $position );1539 1540 if ( is_wp_error($id) ) {1541 $data = $id;1542 $id = 0;1543 }1544 1545 $response = '';1546 if ( is_wp_error($data) ) {1547 foreach ( (array) $data->get_error_codes() as $code ) {1548 $response .= "<wp_error code='$code'><![CDATA[" . $data->get_error_message($code) . "]]></wp_error>";1549 if ( !$error_data = $data->get_error_data($code) )1550 continue;1551 $class = '';1552 if ( is_object($error_data) ) {1553 $class = ' class="' . get_class($error_data) . '"';1554 $error_data = get_object_vars($error_data);1555 }1556 1557 $response .= "<wp_error_data code='$code'$class>";1558 1559 if ( is_scalar($error_data) ) {1560 $response .= "<![CDATA[$error_data]]>";1561 } elseif ( is_array($error_data) ) {1562 foreach ( $error_data as $k => $v )1563 $response .= "<$k><![CDATA[$v]]></$k>";1564 }1565 1566 $response .= "</wp_error_data>";1567 }1568 } else {1569 $response = "<response_data><![CDATA[$data]]></response_data>";1570 }1571 1572 $s = '';1573 if ( is_array($supplemental) ) {1574 foreach ( $supplemental as $k => $v )1575 $s .= "<$k><![CDATA[$v]]></$k>";1576 $s = "<supplemental>$s</supplemental>";1577 }1578 1579 if ( false === $action )1580 $action = $_POST['action'];1581 1582 $x = '';1583 $x .= "<response action='{$action}_$id'>"; // The action attribute in the xml output is formatted like a nonce action1584 $x .= "<$what id='$id' " . ( false === $old_id ? '' : "old_id='$old_id' " ) . "position='$position'>";1585 $x .= $response;1586 $x .= $s;1587 $x .= "</$what>";1588 $x .= "</response>";1589 1590 $this->responses[] = $x;1591 return $x;1592 }1593 1594 /**1595 * Display XML formatted responses.1596 *1597 * Sets the content type header to text/xml.1598 *1599 * @since 2.1.01600 */1601 function send() {1602 header('Content-Type: text/xml');1603 echo "<?xml version='1.0' standalone='yes'?><wp_ajax>";1604 foreach ( (array) $this->responses as $response )1605 echo $response;1606 echo '</wp_ajax>';1607 die();1608 }1609 }1610 1611 /**1612 528 * Helper class to remove the need to use eval to replace $matches[] in query strings. 1613 529 * -
trunk/wp-includes/classes.php
r16051 r16099 10 10 * @package WordPress 11 11 */ 12 13 /**14 * WordPress environment setup class.15 *16 * @package WordPress17 * @since 2.0.018 */19 class WP {20 /**21 * Public query variables.22 *23 * Long list of public query variables.24 *25 * @since 2.0.026 * @access public27 * @var array28 */29 var $public_query_vars = array('m', 'p', 'posts', 'w', 'cat', 'withcomments', 'withoutcomments', 's', 'search', 'exact', 'sentence', 'debug', 'calendar', 'page', 'paged', 'more', 'tb', 'pb', 'author', 'order', 'orderby', 'year', 'monthnum', 'day', 'hour', 'minute', 'second', 'name', 'category_name', 'tag', 'feed', 'author_name', 'static', 'pagename', 'page_id', 'error', 'comments_popup', 'attachment', 'attachment_id', 'subpost', 'subpost_id', 'preview', 'robots', 'taxonomy', 'term', 'cpage', 'post_type');30 31 /**32 * Private query variables.33 *34 * Long list of private query variables.35 *36 * @since 2.0.037 * @var array38 */39 var $private_query_vars = array('offset', 'posts_per_page', 'posts_per_archive_page', 'showposts', 'nopaging', 'post_type', 'post_status', 'category__in', 'category__not_in', 'category__and', 'tag__in', 'tag__not_in', 'tag__and', 'tag_slug__in', 'tag_slug__and', 'tag_id', 'post_mime_type', 'perm', 'comments_per_page', 'post__in', 'post__not_in');40 41 /**42 * Extra query variables set by the user.43 *44 * @since 2.1.045 * @var array46 */47 var $extra_query_vars = array();48 49 /**50 * Query variables for setting up the WordPress Query Loop.51 *52 * @since 2.0.053 * @var array54 */55 var $query_vars;56 57 /**58 * String parsed to set the query variables.59 *60 * @since 2.0.061 * @var string62 */63 var $query_string;64 65 /**66 * Permalink or requested URI.67 *68 * @since 2.0.069 * @var string70 */71 var $request;72 73 /**74 * Rewrite rule the request matched.75 *76 * @since 2.0.077 * @var string78 */79 var $matched_rule;80 81 /**82 * Rewrite query the request matched.83 *84 * @since 2.0.085 * @var string86 */87 var $matched_query;88 89 /**90 * Whether already did the permalink.91 *92 * @since 2.0.093 * @var bool94 */95 var $did_permalink = false;96 97 /**98 * Add name to list of public query variables.99 *100 * @since 2.1.0101 *102 * @param string $qv Query variable name.103 */104 function add_query_var($qv) {105 if ( !in_array($qv, $this->public_query_vars) )106 $this->public_query_vars[] = $qv;107 }108 109 /**110 * Set the value of a query variable.111 *112 * @since 2.3.0113 *114 * @param string $key Query variable name.115 * @param mixed $value Query variable value.116 */117 function set_query_var($key, $value) {118 $this->query_vars[$key] = $value;119 }120 121 /**122 * Parse request to find correct WordPress query.123 *124 * Sets up the query variables based on the request. There are also many125 * filters and actions that can be used to further manipulate the result.126 *127 * @since 2.0.0128 *129 * @param array|string $extra_query_vars Set the extra query variables.130 */131 function parse_request($extra_query_vars = '') {132 global $wp_rewrite;133 134 $this->query_vars = array();135 $post_type_query_vars = array();136 137 if ( is_array($extra_query_vars) )138 $this->extra_query_vars = & $extra_query_vars;139 else if (! empty($extra_query_vars))140 parse_str($extra_query_vars, $this->extra_query_vars);141 142 // Process PATH_INFO, REQUEST_URI, and 404 for permalinks.143 144 // Fetch the rewrite rules.145 $rewrite = $wp_rewrite->wp_rewrite_rules();146 147 if ( ! empty($rewrite) ) {148 // If we match a rewrite rule, this will be cleared.149 $error = '404';150 $this->did_permalink = true;151 152 if ( isset($_SERVER['PATH_INFO']) )153 $pathinfo = $_SERVER['PATH_INFO'];154 else155 $pathinfo = '';156 $pathinfo_array = explode('?', $pathinfo);157 $pathinfo = str_replace("%", "%25", $pathinfo_array[0]);158 $req_uri = $_SERVER['REQUEST_URI'];159 $req_uri_array = explode('?', $req_uri);160 $req_uri = $req_uri_array[0];161 $self = $_SERVER['PHP_SELF'];162 $home_path = parse_url(home_url());163 if ( isset($home_path['path']) )164 $home_path = $home_path['path'];165 else166 $home_path = '';167 $home_path = trim($home_path, '/');168 169 // Trim path info from the end and the leading home path from the170 // front. For path info requests, this leaves us with the requesting171 // filename, if any. For 404 requests, this leaves us with the172 // requested permalink.173 $req_uri = str_replace($pathinfo, '', $req_uri);174 $req_uri = trim($req_uri, '/');175 $req_uri = preg_replace("|^$home_path|", '', $req_uri);176 $req_uri = trim($req_uri, '/');177 $pathinfo = trim($pathinfo, '/');178 $pathinfo = preg_replace("|^$home_path|", '', $pathinfo);179 $pathinfo = trim($pathinfo, '/');180 $self = trim($self, '/');181 $self = preg_replace("|^$home_path|", '', $self);182 $self = trim($self, '/');183 184 // The requested permalink is in $pathinfo for path info requests and185 // $req_uri for other requests.186 if ( ! empty($pathinfo) && !preg_match('|^.*' . $wp_rewrite->index . '$|', $pathinfo) ) {187 $request = $pathinfo;188 } else {189 // If the request uri is the index, blank it out so that we don't try to match it against a rule.190 if ( $req_uri == $wp_rewrite->index )191 $req_uri = '';192 $request = $req_uri;193 }194 195 $this->request = $request;196 197 // Look for matches.198 $request_match = $request;199 foreach ( (array) $rewrite as $match => $query) {200 // Don't try to match against AtomPub calls201 if ( $req_uri == 'wp-app.php' )202 break;203 204 // If the requesting file is the anchor of the match, prepend it205 // to the path info.206 if ( (! empty($req_uri)) && (strpos($match, $req_uri) === 0) && ($req_uri != $request) )207 $request_match = $req_uri . '/' . $request;208 209 if ( preg_match("#^$match#", $request_match, $matches) ||210 preg_match("#^$match#", urldecode($request_match), $matches) ) {211 // Got a match.212 $this->matched_rule = $match;213 214 // Trim the query of everything up to the '?'.215 $query = preg_replace("!^.+\?!", '', $query);216 217 // Substitute the substring matches into the query.218 $query = addslashes(WP_MatchesMapRegex::apply($query, $matches));219 220 $this->matched_query = $query;221 222 // Parse the query.223 parse_str($query, $perma_query_vars);224 225 // If we're processing a 404 request, clear the error var226 // since we found something.227 if ( isset($_GET['error']) )228 unset($_GET['error']);229 230 if ( isset($error) )231 unset($error);232 233 break;234 }235 }236 237 // If req_uri is empty or if it is a request for ourself, unset error.238 if ( empty($request) || $req_uri == $self || strpos($_SERVER['PHP_SELF'], 'wp-admin/') !== false ) {239 if ( isset($_GET['error']) )240 unset($_GET['error']);241 242 if ( isset($error) )243 unset($error);244 245 if ( isset($perma_query_vars) && strpos($_SERVER['PHP_SELF'], 'wp-admin/') !== false )246 unset($perma_query_vars);247 248 $this->did_permalink = false;249 }250 }251 252 $this->public_query_vars = apply_filters('query_vars', $this->public_query_vars);253 254 foreach ( $GLOBALS['wp_post_types'] as $post_type => $t )255 if ( $t->query_var )256 $post_type_query_vars[$t->query_var] = $post_type;257 258 foreach ( $this->public_query_vars as $wpvar ) {259 if ( isset( $this->extra_query_vars[$wpvar] ) )260 $this->query_vars[$wpvar] = $this->extra_query_vars[$wpvar];261 elseif ( isset( $_POST[$wpvar] ) )262 $this->query_vars[$wpvar] = $_POST[$wpvar];263 elseif ( isset( $_GET[$wpvar] ) )264 $this->query_vars[$wpvar] = $_GET[$wpvar];265 elseif ( isset( $perma_query_vars[$wpvar] ) )266 $this->query_vars[$wpvar] = $perma_query_vars[$wpvar];267 268 if ( !empty( $this->query_vars[$wpvar] ) ) {269 if ( ! is_array( $this->query_vars[$wpvar] ) ) {270 $this->query_vars[$wpvar] = (string) $this->query_vars[$wpvar];271 } else {272 foreach ( $this->query_vars[$wpvar] as $vkey => $v ) {273 if ( !is_object( $v ) ) {274 $this->query_vars[$wpvar][$vkey] = (string) $v;275 }276 }277 }278 279 if ( isset($post_type_query_vars[$wpvar] ) ) {280 $this->query_vars['post_type'] = $post_type_query_vars[$wpvar];281 $this->query_vars['name'] = $this->query_vars[$wpvar];282 }283 }284 }285 286 // Limit publicly queried post_types to those that are publicly_queryable287 if ( isset( $this->query_vars['post_type']) ) {288 $queryable_post_types = get_post_types( array('publicly_queryable' => true) );289 if ( ! is_array( $this->query_vars['post_type'] ) ) {290 if ( ! in_array( $this->query_vars['post_type'], $queryable_post_types ) )291 unset( $this->query_vars['post_type'] );292 } else {293 $this->query_vars['post_type'] = array_intersect( $this->query_vars['post_type'], $queryable_post_types );294 }295 }296 297 foreach ( (array) $this->private_query_vars as $var) {298 if ( isset($this->extra_query_vars[$var]) )299 $this->query_vars[$var] = $this->extra_query_vars[$var];300 }301 302 if ( isset($error) )303 $this->query_vars['error'] = $error;304 305 $this->query_vars = apply_filters('request', $this->query_vars);306 307 do_action_ref_array('parse_request', array(&$this));308 }309 310 /**311 * Send additional HTTP headers for caching, content type, etc.312 *313 * Sets the X-Pingback header, 404 status (if 404), Content-type. If showing314 * a feed, it will also send last-modified, etag, and 304 status if needed.315 *316 * @since 2.0.0317 */318 function send_headers() {319 $headers = array('X-Pingback' => get_bloginfo('pingback_url'));320 $status = null;321 $exit_required = false;322 323 if ( is_user_logged_in() )324 $headers = array_merge($headers, wp_get_nocache_headers());325 if ( !empty($this->query_vars['error']) && '404' == $this->query_vars['error'] ) {326 $status = 404;327 if ( !is_user_logged_in() )328 $headers = array_merge($headers, wp_get_nocache_headers());329 $headers['Content-Type'] = get_option('html_type') . '; charset=' . get_option('blog_charset');330 } else if ( empty($this->query_vars['feed']) ) {331 $headers['Content-Type'] = get_option('html_type') . '; charset=' . get_option('blog_charset');332 } else {333 // We're showing a feed, so WP is indeed the only thing that last changed334 if ( !empty($this->query_vars['withcomments'])335 || ( empty($this->query_vars['withoutcomments'])336 && ( !empty($this->query_vars['p'])337 || !empty($this->query_vars['name'])338 || !empty($this->query_vars['page_id'])339 || !empty($this->query_vars['pagename'])340 || !empty($this->query_vars['attachment'])341 || !empty($this->query_vars['attachment_id'])342 )343 )344 )345 $wp_last_modified = mysql2date('D, d M Y H:i:s', get_lastcommentmodified('GMT'), 0).' GMT';346 else347 $wp_last_modified = mysql2date('D, d M Y H:i:s', get_lastpostmodified('GMT'), 0).' GMT';348 $wp_etag = '"' . md5($wp_last_modified) . '"';349 $headers['Last-Modified'] = $wp_last_modified;350 $headers['ETag'] = $wp_etag;351 352 // Support for Conditional GET353 if (isset($_SERVER['HTTP_IF_NONE_MATCH']))354 $client_etag = stripslashes(stripslashes($_SERVER['HTTP_IF_NONE_MATCH']));355 else $client_etag = false;356 357 $client_last_modified = empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? '' : trim($_SERVER['HTTP_IF_MODIFIED_SINCE']);358 // If string is empty, return 0. If not, attempt to parse into a timestamp359 $client_modified_timestamp = $client_last_modified ? strtotime($client_last_modified) : 0;360 361 // Make a timestamp for our most recent modification...362 $wp_modified_timestamp = strtotime($wp_last_modified);363 364 if ( ($client_last_modified && $client_etag) ?365 (($client_modified_timestamp >= $wp_modified_timestamp) && ($client_etag == $wp_etag)) :366 (($client_modified_timestamp >= $wp_modified_timestamp) || ($client_etag == $wp_etag)) ) {367 $status = 304;368 $exit_required = true;369 }370 }371 372 $headers = apply_filters('wp_headers', $headers, $this);373 374 if ( ! empty( $status ) )375 status_header( $status );376 foreach( (array) $headers as $name => $field_value )377 @header("{$name}: {$field_value}");378 379 if ( $exit_required )380 exit();381 382 do_action_ref_array('send_headers', array(&$this));383 }384 385 /**386 * Sets the query string property based off of the query variable property.387 *388 * The 'query_string' filter is deprecated, but still works. Plugins should389 * use the 'request' filter instead.390 *391 * @since 2.0.0392 */393 function build_query_string() {394 $this->query_string = '';395 foreach ( (array) array_keys($this->query_vars) as $wpvar) {396 if ( '' != $this->query_vars[$wpvar] ) {397 $this->query_string .= (strlen($this->query_string) < 1) ? '' : '&';398 if ( !is_scalar($this->query_vars[$wpvar]) ) // Discard non-scalars.399 continue;400 $this->query_string .= $wpvar . '=' . rawurlencode($this->query_vars[$wpvar]);401 }402 }403 404 // query_string filter deprecated. Use request filter instead.405 if ( has_filter('query_string') ) { // Don't bother filtering and parsing if no plugins are hooked in.406 $this->query_string = apply_filters('query_string', $this->query_string);407 parse_str($this->query_string, $this->query_vars);408 }409 }410 411 /**412 * Set up the WordPress Globals.413 *414 * The query_vars property will be extracted to the GLOBALS. So care should415 * be taken when naming global variables that might interfere with the416 * WordPress environment.417 *418 * @global string $query_string Query string for the loop.419 * @global int $more Only set, if single page or post.420 * @global int $single If single page or post. Only set, if single page or post.421 *422 * @since 2.0.0423 */424 function register_globals() {425 global $wp_query;426 // Extract updated query vars back into global namespace.427 foreach ( (array) $wp_query->query_vars as $key => $value) {428 $GLOBALS[$key] = $value;429 }430 431 $GLOBALS['query_string'] = $this->query_string;432 $GLOBALS['posts'] = & $wp_query->posts;433 $GLOBALS['post'] = (isset($wp_query->post)) ? $wp_query->post : null;434 $GLOBALS['request'] = $wp_query->request;435 436 if ( is_single() || is_page() ) {437 $GLOBALS['more'] = 1;438 $GLOBALS['single'] = 1;439 }440 }441 442 /**443 * Set up the current user.444 *445 * @since 2.0.0446 */447 function init() {448 wp_get_current_user();449 }450 451 /**452 * Set up the Loop based on the query variables.453 *454 * @uses WP::$query_vars455 * @since 2.0.0456 */457 function query_posts() {458 global $wp_the_query;459 $this->build_query_string();460 $wp_the_query->query($this->query_vars);461 }462 463 /**464 * Set the Headers for 404, if nothing is found for requested URL.465 *466 * Issue a 404 if a request doesn't match any posts and doesn't match467