WordPress.org

Make WordPress Core

Changeset 16099


Ignore:
Timestamp:
10/30/2010 07:02:06 AM (11 years ago)
Author:
nacin
Message:

Begin the dissolution of classes.php. see #10287.

Location:
trunk
Files:
4 edited
4 copied

Legend:

Unmodified
Added
Removed
  • trunk/wp-admin/setup-config.php

    r16066 r16099  
    4343require_once(ABSPATH . WPINC . '/compat.php');
    4444require_once(ABSPATH . WPINC . '/functions.php');
    45 require_once(ABSPATH . WPINC . '/classes.php');
     45require_once(ABSPATH . WPINC . '/class-wp-error.php');
    4646require_once(ABSPATH . WPINC . '/version.php');
    4747
  • trunk/wp-includes/class-wp-ajax-response.php

    r16091 r16099  
    1010 * @package WordPress
    1111 */
    12 
    13 /**
    14  * WordPress environment setup class.
    15  *
    16  * @package WordPress
    17  * @since 2.0.0
    18  */
    19 class WP {
    20     /**
    21      * Public query variables.
    22      *
    23      * Long list of public query variables.
    24      *
    25      * @since 2.0.0
    26      * @access public
    27      * @var array
    28      */
    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.0
    37      * @var array
    38      */
    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.0
    45      * @var array
    46      */
    47     var $extra_query_vars = array();
    48 
    49     /**
    50      * Query variables for setting up the WordPress Query Loop.
    51      *
    52      * @since 2.0.0
    53      * @var array
    54      */
    55     var $query_vars;
    56 
    57     /**
    58      * String parsed to set the query variables.
    59      *
    60      * @since 2.0.0
    61      * @var string
    62      */
    63     var $query_string;
    64 
    65     /**
    66      * Permalink or requested URI.
    67      *
    68      * @since 2.0.0
    69      * @var string
    70      */
    71     var $request;
    72 
    73     /**
    74      * Rewrite rule the request matched.
    75      *
    76      * @since 2.0.0
    77      * @var string
    78      */
    79     var $matched_rule;
    80 
    81     /**
    82      * Rewrite query the request matched.
    83      *
    84      * @since 2.0.0
    85      * @var string
    86      */
    87     var $matched_query;
    88 
    89     /**
    90      * Whether already did the permalink.
    91      *
    92      * @since 2.0.0
    93      * @var bool
    94      */
    95     var $did_permalink = false;
    96 
    97     /**
    98      * Add name to list of public query variables.
    99      *
    100      * @since 2.1.0
    101      *
    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.0
    113      *
    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 many
    125      * filters and actions that can be used to further manipulate the result.
    126      *
    127      * @since 2.0.0
    128      *
    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             else
    155                 $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             else
    166                 $home_path = '';
    167             $home_path = trim($home_path, '/');
    168 
    169             // Trim path info from the end and the leading home path from the
    170             // front.  For path info requests, this leaves us with the requesting
    171             // filename, if any.  For 404 requests, this leaves us with the
    172             // 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 and
    185             //  $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 calls
    201                 if ( $req_uri == 'wp-app.php' )
    202                     break;
    203 
    204                 // If the requesting file is the anchor of the match, prepend it
    205                 // 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 var
    226                     // 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_queryable
    287         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 showing
    314      * a feed, it will also send last-modified, etag, and 304 status if needed.
    315      *
    316      * @since 2.0.0
    317      */
    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 changed
    334             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             else
    347                 $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 GET
    353             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 timestamp
    359             $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 should
    389      * use the 'request' filter instead.
    390      *
    391      * @since 2.0.0
    392      */
    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 should
    415      * be taken when naming global variables that might interfere with the
    416      * 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.0
    423      */
    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.0
    446      */
    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_vars
    455      * @since 2.0.0
    456      */
    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 match
    467      * any object (e.g. an existing-but-empty category, tag, author) and a 404 was not already
    468      * issued, and if the request was not a search or the homepage.
    469      *
    470      * Otherwise, issue a 200.
    471      *
    472      * @since 2.0.0
    473      */
    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. It
    496      * allows for accessing the properties and methods to further manipulate the
    497      * object.
    498      *
    499      * @since 2.0.0
    500      *
    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.0
    519      *
    520      * @return WP
    521      */
    522     function WP() {
    523         // Empty.
    524     }
    525 }
    526 
    527 /**
    528  * WordPress Error class.
    529  *
    530  * Container for checking for WordPress errors and error messages. Return
    531  * 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 and
    533  * if not handled properly will result in code errors.
    534  *
    535  * @package WordPress
    536  * @since 2.1.0
    537  */
    538 class WP_Error {
    539     /**
    540      * Stores the list of errors.
    541      *
    542      * @since 2.1.0
    543      * @var array
    544      * @access private
    545      */
    546     var $errors = array();
    547 
    548     /**
    549      * Stores the list of data for error codes.
    550      *
    551      * @since 2.1.0
    552      * @var array
    553      * @access private
    554      */
    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 to
    561      * add multiple messages to the same code, but with other methods in the
    562      * class.
    563      *
    564      * All parameters are optional, but if the code parameter is set, then the
    565      * data parameter is optional.
    566      *
    567      * @since 2.1.0
    568      *
    569      * @param string|int $code Error code
    570      * @param string $message Error message
    571      * @param mixed $data Optional. Error data.
    572      * @return WP_Error
    573      */
    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.0
    588      * @access public
    589      *
    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.0
    603      * @access public
    604      *
    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.0
    620      *
    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         else
    637             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 is
    644      * given then the first code available will be used.
    645      *
    646      * @since 2.1.0
    647      *
    648      * @param string|int $code Optional. Error code to retrieve message.
    649      * @return string
    650      */
    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.0
    664      *
    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.0
    681      * @access public
    682      *
    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.0
    699      *
    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 the
    715  * parent is also WP_Error, so can't inherit WP_Error and still use this
    716  * function.
    717  *
    718  * @since 2.1.0
    719  *
    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 classes
    733  * do not need to implement all of the abstract methods in the class. The child
    734  * only needs to implement the methods that are needed. Also, the methods are
    735  * not strictly abstract in that the parameter definition needs to be followed.
    736  * The child classes can have additional parameters.
    737  *
    738  * @package WordPress
    739  * @since 2.1.0
    740  * @abstract
    741  */
    742 class Walker {
    743     /**
    744      * What the class handles.
    745      *
    746      * @since 2.1.0
    747      * @var string
    748      * @access public
    749      */
    750     var $tree_type;
    751 
    752     /**
    753      * DB fields to use.
    754      *
    755      * @since 2.1.0
    756      * @var array
    757      * @access protected
    758      */
    759     var $db_fields;
    760 
    761     /**
    762      * Max number of pages walked by the paged walker
    763      *
    764      * @since 2.7.0
    765      * @var int
    766      * @access protected
    767      */
    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 holds
    774      * additional values that may be used with the child class methods. This
    775      * method is called at the start of the output list.
    776      *
    777      * @since 2.1.0
    778      * @abstract
    779      *
    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 holds
    788      * additional values that may be used with the child class methods. This
    789      * method finishes the list at the end of output of the elements.
    790      *
    791      * @since 2.1.0
    792      * @abstract
    793      *
    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 holds
    802      * additional values that may be used with the child class methods. Includes
    803      * the element output also.
    804      *
    805      * @since 2.1.0
    806      * @abstract
    807      *
    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 holds
    816      * additional values that may be used with the child class methods.
    817      *
    818      * @since 2.1.0
    819      * @abstract
    820      *
    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 max
    830      * depth and no ignore elements under that depth. It is possible to set the
    831      * 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.0
    836      *
    837      * @param object $element Data object
    838      * @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 $args
    842      * @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 element
    853         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 element
    861         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 delimiter
    868                     $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 delimiter
    878             $cb_args = array_merge( array(&$output, $depth), $args);
    879             call_user_func_array(array(&$this, 'end_lvl'), $cb_args);
    880         }
    881 
    882         //end this element
    883         $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 of
    891      * elements. max_depth = -1 means flatly display every element. max_depth =
    892      * 0 means display all levels. max_depth > 0  specifies the number of
    893      * display levels.
    894      *
    895      * @since 2.1.0
    896      *
    897      * @param array $elements
    898      * @param int $max_depth
    899      * @return string
    900      */
    901     function walk( $elements, $max_depth) {
    902 
    903         $args = array_slice(func_get_args(), 2);
    904         $output = '';
    905 
    906         if ($max_depth < -1) //invalid parameter
    907             return $output;
    908 
    909         if (empty($elements)) //nothing to walk
    910             return $output;
    911 
    912         $id_field = $this->db_fields['id'];
    913         $parent_field = $this->db_fields['parent'];
    914 
    915         // flat display
    916         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 order
    925          * separate elements into two buckets: top level and children elements
    926          * 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             else
    935                 $children_elements[ $e->$parent_field ][] = $e;
    936         }
    937 
    938         /*
    939          * when none of the elements is top level
    940          * assume the first one must be root of the sub elements
    941          */
    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                 else
    953                     $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 regardless
    963          */
    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 elements
    976      *
    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 elements
    979      * belonging to that page, then lists them and all of their children in hierarchical order.
    980      *
    981      * @package WordPress
    982      * @since 2.7
    983      * @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 elements
    986      */
    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 paging
    1004             $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 display
    1018         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 elements
    1040          * 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             else
    1049                 $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         else
    1056             $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 orphans
    1073             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 element
    1108     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 WordPress
    1130  * @since 2.1.0
    1131  * @uses Walker
    1132  */
    1133 class Walker_Page extends Walker {
    1134     /**
    1135      * @see Walker::$tree_type
    1136      * @since 2.1.0
    1137      * @var string
    1138      */
    1139     var $tree_type = 'page';
    1140 
    1141     /**
    1142      * @see Walker::$db_fields
    1143      * @since 2.1.0
    1144      * @todo Decouple this.
    1145      * @var array
    1146      */
    1147     var $db_fields = array ('parent' => 'post_parent', 'id' => 'ID');
    1148 
    1149     /**
    1150      * @see Walker::start_lvl()
    1151      * @since 2.1.0
    1152      *
    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.0
    1164      *
    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.0
    1176      *
    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 $args
    1182      */
    1183     function start_el(&$output, $page, $depth, $args, $current_page) {
    1184         if ( $depth )
    1185             $indent = str_repeat("\t", $depth);
    1186         else
    1187             $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             else
    1211                 $time = $page->post_date;
    1212 
    1213             $output .= " " . mysql2date($date_format, $time);
    1214         }
    1215     }
    1216 
    1217     /**
    1218      * @see Walker::end_el()
    1219      * @since 2.1.0
    1220      *
    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 WordPress
    1235  * @since 2.1.0
    1236  * @uses Walker
    1237  */
    1238 class Walker_PageDropdown extends Walker {
    1239     /**
    1240      * @see Walker::$tree_type
    1241      * @since 2.1.0
    1242      * @var string
    1243      */
    1244     var $tree_type = 'page';
    1245 
    1246     /**
    1247      * @see Walker::$db_fields
    1248      * @since 2.1.0
    1249      * @todo Decouple this
    1250      * @var array
    1251      */
    1252     var $db_fields = array ('parent' => 'post_parent', 'id' => 'ID');
    1253 
    1254     /**
    1255      * @see Walker::start_el()
    1256      * @since 2.1.0
    1257      *
    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('&nbsp;', $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 WordPress
    1280  * @since 2.1.0
    1281  * @uses Walker
    1282  */
    1283 class Walker_Category extends Walker {
    1284     /**
    1285      * @see Walker::$tree_type
    1286      * @since 2.1.0
    1287      * @var string
    1288      */
    1289     var $tree_type = 'category';
    1290 
    1291     /**
    1292      * @see Walker::$db_fields
    1293      * @since 2.1.0
    1294      * @todo Decouple this
    1295      * @var array
    1296      */
    1297     var $db_fields = array ('parent' => 'parent', 'id' => 'term_id');
    1298 
    1299     /**
    1300      * @see Walker::start_lvl()
    1301      * @since 2.1.0
    1302      *
    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.0
    1318      *
    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.0
    1334      *
    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 $args
    1339      */
    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         else
    1349             $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             else
    1375                 $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.0
    1409      *
    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 WordPress
    1428  * @since 2.1.0
    1429  * @uses Walker
    1430  */
    1431 class Walker_CategoryDropdown extends Walker {
    1432     /**
    1433      * @see Walker::$tree_type
    1434      * @since 2.1.0
    1435      * @var string
    1436      */
    1437     var $tree_type = 'category';
    1438 
    1439     /**
    1440      * @see Walker::$db_fields
    1441      * @since 2.1.0
    1442      * @todo Decouple this
    1443      * @var array
    1444      */
    1445     var $db_fields = array ('parent' => 'parent', 'id' => 'term_id');
    1446 
    1447     /**
    1448      * @see Walker::start_el()
    1449      * @since 2.1.0
    1450      *
    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('&nbsp;', $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 .= '&nbsp;&nbsp;('. $category->count .')';
    1467         if ( $args['show_last_update'] ) {
    1468             $format = 'Y-m-d';
    1469             $output .= '&nbsp;&nbsp;' . gmdate($format, $category->last_update_timestamp);
    1470         }
    1471         $output .= "</option>\n";
    1472     }
    1473 }
    147412
    147513/**
     
    1609147}
    1610148
    1611 /**
    1612  * Helper class to remove the need to use eval to replace $matches[] in query strings.
    1613  *
    1614  * @since 2.9.0
    1615  */
    1616 class WP_MatchesMapRegex {
    1617     /**
    1618      * store for matches
    1619      *
    1620      * @access private
    1621      * @var array
    1622      */
    1623     var $_matches;
    1624 
    1625     /**
    1626      * store for mapping result
    1627      *
    1628      * @access public
    1629      * @var string
    1630      */
    1631     var $output;
    1632 
    1633     /**
    1634      * subject to perform mapping on (query string containing $matches[] references
    1635      *
    1636      * @access private
    1637      * @var string
    1638      */
    1639     var $_subject;
    1640 
    1641     /**
    1642      * regexp pattern to match $matches[] references
    1643      *
    1644      * @var string
    1645      */
    1646     var $_pattern = '(\$matches\[[1-9]+[0-9]*\])'; // magic number
    1647 
    1648     /**
    1649      * constructor
    1650      *
    1651      * @param string $subject subject if regex
    1652      * @param array  $matches data to use in map
    1653      * @return self
    1654      */
    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 use
    1665      *
    1666      * @access public
    1667      * @param string $subject subject
    1668      * @param array  $matches data used for subsitution
    1669      * @return string
    1670      */
    1671     function apply($subject, $matches) {
    1672         $oSelf =& new WP_MatchesMapRegex($subject, $matches);
    1673         return $oSelf->output;
    1674     }
    1675 
    1676     /**
    1677      * do the actual mapping
    1678      *
    1679      * @access private
    1680      * @return string
    1681      */
    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 hook
    1689      *
    1690      * @access public
    1691      * @param  array $matches preg_replace regexp matches
    1692      * @return string
    1693      */
    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 
    1701149?>
  • trunk/wp-includes/class-wp-error.php

    r16091 r16099  
    1010 * @package WordPress
    1111 */
    12 
    13 /**
    14  * WordPress environment setup class.
    15  *
    16  * @package WordPress
    17  * @since 2.0.0
    18  */
    19 class WP {
    20     /**
    21      * Public query variables.
    22      *
    23      * Long list of public query variables.
    24      *
    25      * @since 2.0.0
    26      * @access public
    27      * @var array
    28      */
    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.0
    37      * @var array
    38      */
    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.0
    45      * @var array
    46      */
    47     var $extra_query_vars = array();
    48 
    49     /**
    50      * Query variables for setting up the WordPress Query Loop.
    51      *
    52      * @since 2.0.0
    53      * @var array
    54      */
    55     var $query_vars;
    56 
    57     /**
    58      * String parsed to set the query variables.
    59      *
    60      * @since 2.0.0
    61      * @var string
    62      */
    63     var $query_string;
    64 
    65     /**
    66      * Permalink or requested URI.
    67      *
    68      * @since 2.0.0
    69      * @var string
    70      */
    71     var $request;
    72 
    73     /**
    74      * Rewrite rule the request matched.
    75      *
    76      * @since 2.0.0
    77      * @var string
    78      */
    79     var $matched_rule;
    80 
    81     /**
    82      * Rewrite query the request matched.
    83      *
    84      * @since 2.0.0
    85      * @var string
    86      */
    87     var $matched_query;
    88 
    89     /**
    90      * Whether already did the permalink.
    91      *
    92      * @since 2.0.0
    93      * @var bool
    94      */
    95     var $did_permalink = false;
    96 
    97     /**
    98      * Add name to list of public query variables.
    99      *
    100      * @since 2.1.0
    101      *
    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.0
    113      *
    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 many
    125      * filters and actions that can be used to further manipulate the result.
    126      *
    127      * @since 2.0.0
    128      *
    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             else
    155                 $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             else
    166                 $home_path = '';
    167             $home_path = trim($home_path, '/');
    168 
    169             // Trim path info from the end and the leading home path from the
    170             // front.  For path info requests, this leaves us with the requesting
    171             // filename, if any.  For 404 requests, this leaves us with the
    172             // 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 and
    185             //  $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 calls
    201                 if ( $req_uri == 'wp-app.php' )
    202                     break;
    203 
    204                 // If the requesting file is the anchor of the match, prepend it
    205                 // 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 var
    226                     // 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_queryable
    287         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 showing
    314      * a feed, it will also send last-modified, etag, and 304 status if needed.
    315      *
    316      * @since 2.0.0
    317      */
    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 changed
    334             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             else
    347                 $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 GET
    353             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 timestamp
    359             $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 should
    389      * use the 'request' filter instead.
    390      *
    391      * @since 2.0.0
    392      */
    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 should
    415      * be taken when naming global variables that might interfere with the
    416      * 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.0
    423      */
    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.0
    446      */
    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_vars
    455      * @since 2.0.0
    456      */
    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 match
    467      * any object (e.g. an existing-but-empty category, tag, author) and a 404 was not already
    468      * issued, and if the request was not a search or the homepage.
    469      *
    470      * Otherwise, issue a 200.
    471      *
    472      * @since 2.0.0
    473      */
    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. It
    496      * allows for accessing the properties and methods to further manipulate the
    497      * object.
    498      *
    499      * @since 2.0.0
    500      *
    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.0
    519      *
    520      * @return WP
    521      */
    522     function WP() {
    523         // Empty.
    524     }
    525 }
    52612
    52713/**
     
    727213}
    728214
    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 classes
    733  * do not need to implement all of the abstract methods in the class. The child
    734  * only needs to implement the methods that are needed. Also, the methods are
    735  * not strictly abstract in that the parameter definition needs to be followed.
    736  * The child classes can have additional parameters.
    737  *
    738  * @package WordPress
    739  * @since 2.1.0
    740  * @abstract
    741  */
    742 class Walker {
    743     /**
    744      * What the class handles.
    745      *
    746      * @since 2.1.0
    747      * @var string
    748      * @access public
    749      */
    750     var $tree_type;
    751 
    752     /**
    753      * DB fields to use.
    754      *
    755      * @since 2.1.0
    756      * @var array
    757      * @access protected
    758      */
    759     var $db_fields;
    760 
    761     /**
    762      * Max number of pages walked by the paged walker
    763      *
    764      * @since 2.7.0
    765      * @var int
    766      * @access protected
    767      */
    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 holds
    774      * additional values that may be used with the child class methods. This
    775      * method is called at the start of the output list.
    776      *
    777      * @since 2.1.0
    778      * @abstract
    779      *
    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 holds
    788      * additional values that may be used with the child class methods. This
    789      * method finishes the list at the end of output of the elements.
    790      *
    791      * @since 2.1.0
    792      * @abstract
    793      *
    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 holds
    802      * additional values that may be used with the child class methods. Includes
    803      * the element output also.
    804      *
    805      * @since 2.1.0
    806      * @abstract
    807      *
    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 holds
    816      * additional values that may be used with the child class methods.
    817      *
    818      * @since 2.1.0
    819      * @abstract
    820      *
    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 max
    830      * depth and no ignore elements under that depth. It is possible to set the
    831      * 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.0
    836      *
    837      * @param object $element Data object
    838      * @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 $args
    842      * @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 element
    853         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 element
    861         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 delimiter
    868                     $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 delimiter
    878             $cb_args = array_merge( array(&$output, $depth), $args);
    879             call_user_func_array(array(&$this, 'end_lvl'), $cb_args);
    880         }
    881 
    882         //end this element
    883         $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 of
    891      * elements. max_depth = -1 means flatly display every element. max_depth =
    892      * 0 means display all levels. max_depth > 0  specifies the number of
    893      * display levels.
    894      *
    895      * @since 2.1.0
    896      *
    897      * @param array $elements
    898      * @param int $max_depth
    899      * @return string
    900      */
    901     function walk( $elements, $max_depth) {
    902 
    903         $args = array_slice(func_get_args(), 2);
    904         $output = '';
    905 
    906         if ($max_depth < -1) //invalid parameter
    907             return $output;
    908 
    909         if (empty($elements)) //nothing to walk
    910             return $output;
    911 
    912         $id_field = $this->db_fields['id'];
    913         $parent_field = $this->db_fields['parent'];
    914 
    915         // flat display
    916         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 order
    925          * separate elements into two buckets: top level and children elements
    926          * 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             else
    935                 $children_elements[ $e->$parent_field ][] = $e;
    936         }
    937 
    938         /*
    939          * when none of the elements is top level
    940          * assume the first one must be root of the sub elements
    941          */
    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                 else
    953                     $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 regardless
    963          */
    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 elements
    976      *
    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 elements
    979      * belonging to that page, then lists them and all of their children in hierarchical order.
    980      *
    981      * @package WordPress
    982      * @since 2.7
    983      * @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 elements
    986      */
    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 paging
    1004             $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 display
    1018         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 elements
    1040          * 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             else
    1049                 $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         else
    1056             $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 orphans
    1073             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 element
    1108     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 WordPress
    1130  * @since 2.1.0
    1131  * @uses Walker
    1132  */
    1133 class Walker_Page extends Walker {
    1134     /**
    1135      * @see Walker::$tree_type
    1136      * @since 2.1.0
    1137      * @var string
    1138      */
    1139     var $tree_type = 'page';
    1140 
    1141     /**
    1142      * @see Walker::$db_fields
    1143      * @since 2.1.0
    1144      * @todo Decouple this.
    1145      * @var array
    1146      */
    1147     var $db_fields = array ('parent' => 'post_parent', 'id' => 'ID');
    1148 
    1149     /**
    1150      * @see Walker::start_lvl()
    1151      * @since 2.1.0
    1152      *
    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.0
    1164      *
    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.0
    1176      *
    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 $args
    1182      */
    1183     function start_el(&$output, $page, $depth, $args, $current_page) {
    1184         if ( $depth )
    1185             $indent = str_repeat("\t", $depth);
    1186         else
    1187             $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             else
    1211                 $time = $page->post_date;
    1212 
    1213             $output .= " " . mysql2date($date_format, $time);
    1214         }
    1215     }
    1216 
    1217     /**
    1218      * @see Walker::end_el()
    1219      * @since 2.1.0
    1220      *
    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 WordPress
    1235  * @since 2.1.0
    1236  * @uses Walker
    1237  */
    1238 class Walker_PageDropdown extends Walker {
    1239     /**
    1240      * @see Walker::$tree_type
    1241      * @since 2.1.0
    1242      * @var string
    1243      */
    1244     var $tree_type = 'page';
    1245 
    1246     /**
    1247      * @see Walker::$db_fields
    1248      * @since 2.1.0
    1249      * @todo Decouple this
    1250      * @var array
    1251      */
    1252     var $db_fields = array ('parent' => 'post_parent', 'id' => 'ID');
    1253 
    1254     /**
    1255      * @see Walker::start_el()
    1256      * @since 2.1.0
    1257      *
    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('&nbsp;', $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 WordPress
    1280  * @since 2.1.0
    1281  * @uses Walker
    1282  */
    1283 class Walker_Category extends Walker {
    1284     /**
    1285      * @see Walker::$tree_type
    1286      * @since 2.1.0
    1287      * @var string
    1288      */
    1289     var $tree_type = 'category';
    1290 
    1291     /**
    1292      * @see Walker::$db_fields
    1293      * @since 2.1.0
    1294      * @todo Decouple this
    1295      * @var array
    1296      */
    1297     var $db_fields = array ('parent' => 'parent', 'id' => 'term_id');
    1298 
    1299     /**
    1300      * @see Walker::start_lvl()
    1301      * @since 2.1.0
    1302      *
    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.0
    1318      *
    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.0
    1334      *
    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 $args
    1339      */
    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         else
    1349             $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             else
    1375                 $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.0
    1409      *
    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 WordPress
    1428  * @since 2.1.0
    1429  * @uses Walker
    1430  */
    1431 class Walker_CategoryDropdown extends Walker {
    1432     /**
    1433      * @see Walker::$tree_type
    1434      * @since 2.1.0
    1435      * @var string
    1436      */
    1437     var $tree_type = 'category';
    1438 
    1439     /**
    1440      * @see Walker::$db_fields
    1441      * @since 2.1.0
    1442      * @todo Decouple this
    1443      * @var array
    1444      */
    1445     var $db_fields = array ('parent' => 'parent', 'id' => 'term_id');
    1446 
    1447     /**
    1448      * @see Walker::start_el()
    1449      * @since 2.1.0
    1450      *
    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('&nbsp;', $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 .= '&nbsp;&nbsp;('. $category->count .')';
    1467         if ( $args['show_last_update'] ) {
    1468             $format = 'Y-m-d';
    1469             $output .= '&nbsp;&nbsp;' . 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 WordPress
    1479  * @since 2.1.0
    1480  */
    1481 class WP_Ajax_Response {
    1482     /**
    1483      * Store XML responses to send.
    1484      *
    1485      * @since 2.1.0
    1486      * @var array
    1487      * @access private
    1488      */
    1489     var $responses = array();
    1490 
    1491     /**
    1492      * PHP4 Constructor - Passes args to {@link WP_Ajax_Response::add()}.
    1493      *
    1494      * @since 2.1.0
    1495      * @see WP_Ajax_Response::add()
    1496      *
    1497      * @param string|array $args Optional. Will be passed to add() method.
    1498      * @return WP_Ajax_Response
    1499      */
    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 is
    1509      * 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 given
    1511      * 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 of
    1521      * the supplemental element.
    1522      *
    1523      * @since 2.1.0
    1524      *
    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 action
    1584         $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.0
    1600      */
    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.0
    1615  */
    1616 class WP_MatchesMapRegex {
    1617     /**
    1618      * store for matches
    1619      *
    1620      * @access private
    1621      * @var array
    1622      */
    1623     var $_matches;
    1624 
    1625     /**
    1626      * store for mapping result
    1627      *
    1628      * @access public
    1629      * @var string
    1630      */
    1631     var $output;
    1632 
    1633     /**
    1634      * subject to perform mapping on (query string containing $matches[] references
    1635      *
    1636      * @access private
    1637      * @var string
    1638      */
    1639     var $_subject;
    1640 
    1641     /**
    1642      * regexp pattern to match $matches[] references
    1643      *
    1644      * @var string
    1645      */
    1646     var $_pattern = '(\$matches\[[1-9]+[0-9]*\])'; // magic number
    1647 
    1648     /**
    1649      * constructor
    1650      *
    1651      * @param string $subject subject if regex
    1652      * @param array  $matches data to use in map
    1653      * @return self
    1654      */
    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 use
    1665      *
    1666      * @access public
    1667      * @param string $subject subject
    1668      * @param array  $matches data used for subsitution
    1669      * @return string
    1670      */
    1671     function apply($subject, $matches) {
    1672         $oSelf =& new WP_MatchesMapRegex($subject, $matches);
    1673         return $oSelf->output;
    1674     }
    1675 
    1676     /**
    1677      * do the actual mapping
    1678      *
    1679      * @access private
    1680      * @return string
    1681      */
    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 hook
    1689      *
    1690      * @access public
    1691      * @param  array $matches preg_replace regexp matches
    1692      * @return string
    1693      */
    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 
    1701215?>
  • trunk/wp-includes/class-wp-walker.php

    r16091 r16099  
    1010 * @package WordPress
    1111 */
    12 
    13 /**
    14  * WordPress environment setup class.
    15  *
    16  * @package WordPress
    17  * @since 2.0.0
    18  */
    19 class WP {
    20     /**
    21      * Public query variables.
    22      *
    23      * Long list of public query variables.
    24      *
    25      * @since 2.0.0
    26      * @access public
    27      * @var array
    28      */
    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.0
    37      * @var array
    38      */
    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.0
    45      * @var array
    46      */
    47     var $extra_query_vars = array();
    48 
    49     /**
    50      * Query variables for setting up the WordPress Query Loop.
    51      *
    52      * @since 2.0.0
    53      * @var array
    54      */
    55     var $query_vars;
    56 
    57     /**
    58      * String parsed to set the query variables.
    59      *
    60      * @since 2.0.0
    61      * @var string
    62      */
    63     var $query_string;
    64 
    65     /**
    66      * Permalink or requested URI.
    67      *
    68      * @since 2.0.0
    69      * @var string
    70      */
    71     var $request;
    72 
    73     /**
    74      * Rewrite rule the request matched.
    75      *
    76      * @since 2.0.0
    77      * @var string
    78      */
    79     var $matched_rule;
    80 
    81     /**
    82      * Rewrite query the request matched.
    83      *
    84      * @since 2.0.0
    85      * @var string
    86      */
    87     var $matched_query;
    88 
    89     /**
    90      * Whether already did the permalink.
    91      *
    92      * @since 2.0.0
    93      * @var bool
    94      */
    95     var $did_permalink = false;
    96 
    97     /**
    98      * Add name to list of public query variables.
    99      *
    100      * @since 2.1.0
    101      *
    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.0
    113      *
    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 many
    125      * filters and actions that can be used to further manipulate the result.
    126      *
    127      * @since 2.0.0
    128      *
    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             else
    155                 $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             else
    166                 $home_path = '';
    167             $home_path = trim($home_path, '/');
    168 
    169             // Trim path info from the end and the leading home path from the
    170             // front.  For path info requests, this leaves us with the requesting
    171             // filename, if any.  For 404 requests, this leaves us with the
    172             // 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 and
    185             //  $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 calls
    201                 if ( $req_uri == 'wp-app.php' )
    202                     break;
    203 
    204                 // If the requesting file is the anchor of the match, prepend it
    205                 // 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 var
    226                     // 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_queryable
    287         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 showing
    314      * a feed, it will also send last-modified, etag, and 304 status if needed.
    315      *
    316      * @since 2.0.0
    317      */
    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 changed
    334             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             else
    347                 $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 GET
    353             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 timestamp
    359             $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 should
    389      * use the 'request' filter instead.
    390      *
    391      * @since 2.0.0
    392      */
    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 should
    415      * be taken when naming global variables that might interfere with the
    416      * 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.0
    423      */
    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.0
    446      */
    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_vars
    455      * @since 2.0.0
    456      */
    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 match
    467      * any object (e.g. an existing-but-empty category, tag, author) and a 404 was not already
    468      * issued, and if the request was not a search or the homepage.
    469      *
    470      * Otherwise, issue a 200.
    471      *
    472      * @since 2.0.0
    473      */
    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. It
    496      * allows for accessing the properties and methods to further manipulate the
    497      * object.
    498      *
    499      * @since 2.0.0
    500      *
    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.0
    519      *
    520      * @return WP
    521      */
    522     function WP() {
    523         // Empty.
    524     }
    525 }
    526 
    527 /**
    528  * WordPress Error class.
    529  *
    530  * Container for checking for WordPress errors and error messages. Return
    531  * 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 and
    533  * if not handled properly will result in code errors.
    534  *
    535  * @package WordPress
    536  * @since 2.1.0
    537  */
    538 class WP_Error {
    539     /**
    540      * Stores the list of errors.
    541      *
    542      * @since 2.1.0
    543      * @var array
    544      * @access private
    545      */
    546     var $errors = array();
    547 
    548     /**
    549      * Stores the list of data for error codes.
    550      *
    551      * @since 2.1.0
    552      * @var array
    553      * @access private
    554      */
    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 to
    561      * add multiple messages to the same code, but with other methods in the
    562      * class.
    563      *
    564      * All parameters are optional, but if the code parameter is set, then the
    565      * data parameter is optional.
    566      *
    567      * @since 2.1.0
    568      *
    569      * @param string|int $code Error code
    570      * @param string $message Error message
    571      * @param mixed $data Optional. Error data.
    572      * @return WP_Error
    573      */
    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.0
    588      * @access public
    589      *
    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.0
    603      * @access public
    604      *
    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.0
    620      *
    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         else
    637             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 is
    644      * given then the first code available will be used.
    645      *
    646      * @since 2.1.0
    647      *
    648      * @param string|int $code Optional. Error code to retrieve message.
    649      * @return string
    650      */
    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.0
    664      *
    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.0
    681      * @access public
    682      *
    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.0
    699      *
    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 the
    715  * parent is also WP_Error, so can't inherit WP_Error and still use this
    716  * function.
    717  *
    718  * @since 2.1.0
    719  *
    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 }
    72812
    72913/**
     
    1124408}
    1125409
    1126 /**
    1127  * Create HTML list of pages.
    1128  *
    1129  * @package WordPress
    1130  * @since 2.1.0
    1131  * @uses Walker
    1132  */
    1133 class Walker_Page extends Walker {
    1134     /**
    1135      * @see Walker::$tree_type
    1136      * @since 2.1.0
    1137      * @var string
    1138      */
    1139     var $tree_type = 'page';
    1140 
    1141     /**
    1142      * @see Walker::$db_fields
    1143      * @since 2.1.0
    1144      * @todo Decouple this.
    1145      * @var array
    1146      */
    1147     var $db_fields = array ('parent' => 'post_parent', 'id' => 'ID');
    1148 
    1149     /**
    1150      * @see Walker::start_lvl()
    1151      * @since 2.1.0
    1152      *
    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.0
    1164      *
    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.0
    1176      *
    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 $args
    1182      */
    1183     function start_el(&$output, $page, $depth, $args, $current_page) {
    1184         if ( $depth )
    1185             $indent = str_repeat("\t", $depth);
    1186         else
    1187             $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             else
    1211                 $time = $page->post_date;
    1212 
    1213             $output .= " " . mysql2date($date_format, $time);
    1214         }
    1215     }
    1216 
    1217     /**
    1218      * @see Walker::end_el()
    1219      * @since 2.1.0
    1220      *
    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 WordPress
    1235  * @since 2.1.0
    1236  * @uses Walker
    1237  */
    1238 class Walker_PageDropdown extends Walker {
    1239     /**
    1240      * @see Walker::$tree_type
    1241      * @since 2.1.0
    1242      * @var string
    1243      */
    1244     var $tree_type = 'page';
    1245 
    1246     /**
    1247      * @see Walker::$db_fields
    1248      * @since 2.1.0
    1249      * @todo Decouple this
    1250      * @var array
    1251      */
    1252     var $db_fields = array ('parent' => 'post_parent', 'id' => 'ID');
    1253 
    1254     /**
    1255      * @see Walker::start_el()
    1256      * @since 2.1.0
    1257      *
    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('&nbsp;', $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 WordPress
    1280  * @since 2.1.0
    1281  * @uses Walker
    1282  */
    1283 class Walker_Category extends Walker {
    1284     /**
    1285      * @see Walker::$tree_type
    1286      * @since 2.1.0
    1287      * @var string
    1288      */
    1289     var $tree_type = 'category';
    1290 
    1291     /**
    1292      * @see Walker::$db_fields
    1293      * @since 2.1.0
    1294      * @todo Decouple this
    1295      * @var array
    1296      */
    1297     var $db_fields = array ('parent' => 'parent', 'id' => 'term_id');
    1298 
    1299     /**
    1300      * @see Walker::start_lvl()
    1301      * @since 2.1.0
    1302      *
    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.0
    1318      *
    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.0
    1334      *
    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 $args
    1339      */
    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         else
    1349             $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             else
    1375                 $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.0
    1409      *
    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 WordPress
    1428  * @since 2.1.0
    1429  * @uses Walker
    1430  */
    1431 class Walker_CategoryDropdown extends Walker {
    1432     /**
    1433      * @see Walker::$tree_type
    1434      * @since 2.1.0
    1435      * @var string
    1436      */
    1437     var $tree_type = 'category';
    1438 
    1439     /**
    1440      * @see Walker::$db_fields
    1441      * @since 2.1.0
    1442      * @todo Decouple this
    1443      * @var array
    1444      */
    1445     var $db_fields = array ('parent' => 'parent', 'id' => 'term_id');
    1446 
    1447     /**
    1448      * @see Walker::start_el()
    1449      * @since 2.1.0
    1450      *
    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('&nbsp;', $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 .= '&nbsp;&nbsp;('. $category->count .')';
    1467         if ( $args['show_last_update'] ) {
    1468             $format = 'Y-m-d';
    1469             $output .= '&nbsp;&nbsp;' . 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 WordPress
    1479  * @since 2.1.0
    1480  */
    1481 class WP_Ajax_Response {
    1482     /**
    1483      * Store XML responses to send.
    1484      *
    1485      * @since 2.1.0
    1486      * @var array
    1487      * @access private
    1488      */
    1489     var $responses = array();
    1490 
    1491     /**
    1492      * PHP4 Constructor - Passes args to {@link WP_Ajax_Response::add()}.
    1493      *
    1494      * @since 2.1.0
    1495      * @see WP_Ajax_Response::add()
    1496      *
    1497      * @param string|array $args Optional. Will be passed to add() method.
    1498      * @return WP_Ajax_Response
    1499      */
    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 is
    1509      * 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 given
    1511      * 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 of
    1521      * the supplemental element.
    1522      *
    1523      * @since 2.1.0
    1524      *
    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 action
    1584         $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.0
    1600      */
    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.0
    1615  */
    1616 class WP_MatchesMapRegex {
    1617     /**
    1618      * store for matches
    1619      *
    1620      * @access private
    1621      * @var array
    1622      */
    1623     var $_matches;
    1624 
    1625     /**
    1626      * store for mapping result
    1627      *
    1628      * @access public
    1629      * @var string
    1630      */
    1631     var $output;
    1632 
    1633     /**
    1634      * subject to perform mapping on (query string containing $matches[] references
    1635      *
    1636      * @access private
    1637      * @var string
    1638      */
    1639     var $_subject;
    1640 
    1641     /**
    1642      * regexp pattern to match $matches[] references
    1643      *
    1644      * @var string
    1645      */
    1646     var $_pattern = '(\$matches\[[1-9]+[0-9]*\])'; // magic number
    1647 
    1648     /**
    1649      * constructor
    1650      *
    1651      * @param string $subject subject if regex
    1652      * @param array  $matches data to use in map
    1653      * @return self
    1654      */
    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 use
    1665      *
    1666      * @access public
    1667      * @param string $subject subject
    1668      * @param array  $matches data used for subsitution
    1669      * @return string
    1670      */
    1671     function apply($subject, $matches) {
    1672         $oSelf =& new WP_MatchesMapRegex($subject, $matches);
    1673         return $oSelf->output;
    1674     }
    1675 
    1676     /**
    1677      * do the actual mapping
    1678      *
    1679      * @access private
    1680      * @return string
    1681      */
    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 hook
    1689      *
    1690      * @access public
    1691      * @param  array $matches preg_replace regexp matches
    1692      * @return string
    1693      */
    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 
    1701410?>
  • trunk/wp-includes/class-wp.php

    r16091 r16099  
    526526
    527527/**
    528  * WordPress Error class.
    529  *
    530  * Container for checking for WordPress errors and error messages. Return
    531  * 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 and
    533  * if not handled properly will result in code errors.
    534  *
    535  * @package WordPress
    536  * @since 2.1.0
    537  */
    538 class WP_Error {
    539     /**
    540      * Stores the list of errors.
    541      *
    542      * @since 2.1.0
    543      * @var array
    544      * @access private
    545      */
    546     var $errors = array();
    547 
    548     /**
    549      * Stores the list of data for error codes.
    550      *
    551      * @since 2.1.0
    552      * @var array
    553      * @access private
    554      */
    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 to
    561      * add multiple messages to the same code, but with other methods in the
    562      * class.
    563      *
    564      * All parameters are optional, but if the code parameter is set, then the
    565      * data parameter is optional.
    566      *
    567      * @since 2.1.0
    568      *
    569      * @param string|int $code Error code
    570      * @param string $message Error message
    571      * @param mixed $data Optional. Error data.
    572      * @return WP_Error
    573      */
    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.0
    588      * @access public
    589      *
    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.0
    603      * @access public
    604      *
    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.0
    620      *
    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         else
    637             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 is
    644      * given then the first code available will be used.
    645      *
    646      * @since 2.1.0
    647      *
    648      * @param string|int $code Optional. Error code to retrieve message.
    649      * @return string
    650      */
    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.0
    664      *
    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.0
    681      * @access public
    682      *
    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.0
    699      *
    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 the
    715  * parent is also WP_Error, so can't inherit WP_Error and still use this
    716  * function.
    717  *
    718  * @since 2.1.0
    719  *
    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 classes
    733  * do not need to implement all of the abstract methods in the class. The child
    734  * only needs to implement the methods that are needed. Also, the methods are
    735  * not strictly abstract in that the parameter definition needs to be followed.
    736  * The child classes can have additional parameters.
    737  *
    738  * @package WordPress
    739  * @since 2.1.0
    740  * @abstract
    741  */
    742 class Walker {
    743     /**
    744      * What the class handles.
    745      *
    746      * @since 2.1.0
    747      * @var string
    748      * @access public
    749      */
    750     var $tree_type;
    751 
    752     /**
    753      * DB fields to use.
    754      *
    755      * @since 2.1.0
    756      * @var array
    757      * @access protected
    758      */
    759     var $db_fields;
    760 
    761     /**
    762      * Max number of pages walked by the paged walker
    763      *
    764      * @since 2.7.0
    765      * @var int
    766      * @access protected
    767      */
    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 holds
    774      * additional values that may be used with the child class methods. This
    775      * method is called at the start of the output list.
    776      *
    777      * @since 2.1.0
    778      * @abstract
    779      *
    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 holds
    788      * additional values that may be used with the child class methods. This
    789      * method finishes the list at the end of output of the elements.
    790      *
    791      * @since 2.1.0
    792      * @abstract
    793      *
    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 holds
    802      * additional values that may be used with the child class methods. Includes
    803      * the element output also.
    804      *
    805      * @since 2.1.0
    806      * @abstract
    807      *
    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 holds
    816      * additional values that may be used with the child class methods.
    817      *
    818      * @since 2.1.0
    819      * @abstract
    820      *
    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 max
    830      * depth and no ignore elements under that depth. It is possible to set the
    831      * 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.0
    836      *
    837      * @param object $element Data object
    838      * @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 $args
    842      * @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 element
    853         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 element
    861         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 delimiter
    868                     $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 delimiter
    878             $cb_args = array_merge( array(&$output, $depth), $args);
    879             call_user_func_array(array(&$this, 'end_lvl'), $cb_args);
    880         }
    881 
    882         //end this element
    883         $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 of
    891      * elements. max_depth = -1 means flatly display every element. max_depth =
    892      * 0 means display all levels. max_depth > 0  specifies the number of
    893      * display levels.
    894      *
    895      * @since 2.1.0
    896      *
    897      * @param array $elements
    898      * @param int $max_depth
    899      * @return string
    900      */
    901     function walk( $elements, $max_depth) {
    902 
    903         $args = array_slice(func_get_args(), 2);
    904         $output = '';
    905 
    906         if ($max_depth < -1) //invalid parameter
    907             return $output;
    908 
    909         if (empty($elements)) //nothing to walk
    910             return $output;
    911 
    912         $id_field = $this->db_fields['id'];
    913         $parent_field = $this->db_fields['parent'];
    914 
    915         // flat display
    916         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 order
    925          * separate elements into two buckets: top level and children elements
    926          * 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             else
    935                 $children_elements[ $e->$parent_field ][] = $e;
    936         }
    937 
    938         /*
    939          * when none of the elements is top level
    940          * assume the first one must be root of the sub elements
    941          */
    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                 else
    953                     $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 regardless
    963          */
    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 elements
    976      *
    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 elements
    979      * belonging to that page, then lists them and all of their children in hierarchical order.
    980      *
    981      * @package WordPress
    982      * @since 2.7
    983      * @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 elements
    986      */
    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 paging
    1004             $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 display
    1018         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 elements
    1040          * 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             else
    1049                 $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         else
    1056             $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 orphans
    1073             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 element
    1108     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 WordPress
    1130  * @since 2.1.0
    1131  * @uses Walker
    1132  */
    1133 class Walker_Page extends Walker {
    1134     /**
    1135      * @see Walker::$tree_type
    1136      * @since 2.1.0
    1137      * @var string
    1138      */
    1139     var $tree_type = 'page';
    1140 
    1141     /**
    1142      * @see Walker::$db_fields
    1143      * @since 2.1.0
    1144      * @todo Decouple this.
    1145      * @var array
    1146      */
    1147     var $db_fields = array ('parent' => 'post_parent', 'id' => 'ID');
    1148 
    1149     /**
    1150      * @see Walker::start_lvl()
    1151      * @since 2.1.0
    1152      *
    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.0
    1164      *
    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.0
    1176      *
    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 $args
    1182      */
    1183     function start_el(&$output, $page, $depth, $args, $current_page) {
    1184         if ( $depth )
    1185             $indent = str_repeat("\t", $depth);
    1186         else
    1187             $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             else
    1211                 $time = $page->post_date;
    1212 
    1213             $output .= " " . mysql2date($date_format, $time);
    1214         }
    1215     }
    1216 
    1217     /**
    1218      * @see Walker::end_el()
    1219      * @since 2.1.0
    1220      *
    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 WordPress
    1235  * @since 2.1.0
    1236  * @uses Walker
    1237  */
    1238 class Walker_PageDropdown extends Walker {
    1239     /**
    1240      * @see Walker::$tree_type
    1241      * @since 2.1.0
    1242      * @var string
    1243      */
    1244     var $tree_type = 'page';
    1245 
    1246     /**
    1247      * @see Walker::$db_fields
    1248      * @since 2.1.0
    1249      * @todo Decouple this
    1250      * @var array
    1251      */
    1252     var $db_fields = array ('parent' => 'post_parent', 'id' => 'ID');
    1253 
    1254     /**
    1255      * @see Walker::start_el()
    1256      * @since 2.1.0
    1257      *
    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('&nbsp;', $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 WordPress
    1280  * @since 2.1.0
    1281  * @uses Walker
    1282  */
    1283 class Walker_Category extends Walker {
    1284     /**
    1285      * @see Walker::$tree_type
    1286      * @since 2.1.0
    1287      * @var string
    1288      */
    1289     var $tree_type = 'category';
    1290 
    1291     /**
    1292      * @see Walker::$db_fields
    1293      * @since 2.1.0
    1294      * @todo Decouple this
    1295      * @var array
    1296      */
    1297     var $db_fields = array ('parent' => 'parent', 'id' => 'term_id');
    1298 
    1299     /**
    1300      * @see Walker::start_lvl()
    1301      * @since 2.1.0
    1302      *
    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.0
    1318      *
    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.0
    1334      *
    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 $args
    1339      */
    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         else
    1349             $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             else
    1375                 $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.0
    1409      *
    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 WordPress
    1428  * @since 2.1.0
    1429  * @uses Walker
    1430  */
    1431 class Walker_CategoryDropdown extends Walker {
    1432     /**
    1433      * @see Walker::$tree_type
    1434      * @since 2.1.0
    1435      * @var string
    1436      */
    1437     var $tree_type = 'category';
    1438 
    1439     /**
    1440      * @see Walker::$db_fields
    1441      * @since 2.1.0
    1442      * @todo Decouple this
    1443      * @var array
    1444      */
    1445     var $db_fields = array ('parent' => 'parent', 'id' => 'term_id');
    1446 
    1447     /**
    1448      * @see Walker::start_el()
    1449      * @since 2.1.0
    1450      *
    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('&nbsp;', $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 .= '&nbsp;&nbsp;('. $category->count .')';
    1467         if ( $args['show_last_update'] ) {
    1468             $format = 'Y-m-d';
    1469             $output .= '&nbsp;&nbsp;' . 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 WordPress
    1479  * @since 2.1.0
    1480  */
    1481 class WP_Ajax_Response {
    1482     /**
    1483      * Store XML responses to send.
    1484      *
    1485      * @since 2.1.0
    1486      * @var array
    1487      * @access private
    1488      */
    1489     var $responses = array();
    1490 
    1491     /**
    1492      * PHP4 Constructor - Passes args to {@link WP_Ajax_Response::add()}.
    1493      *
    1494      * @since 2.1.0
    1495      * @see WP_Ajax_Response::add()
    1496      *
    1497      * @param string|array $args Optional. Will be passed to add() method.
    1498      * @return WP_Ajax_Response
    1499      */
    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 is
    1509      * 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 given
    1511      * 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 of
    1521      * the supplemental element.
    1522      *
    1523      * @since 2.1.0
    1524      *
    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 action
    1584         $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.0
    1600      */
    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 /**
    1612528 * Helper class to remove the need to use eval to replace $matches[] in query strings.
    1613529 *
  • trunk/wp-includes/classes.php

    r16051 r16099  
    1010 * @package WordPress
    1111 */
    12 
    13 /**
    14  * WordPress environment setup class.
    15  *
    16  * @package WordPress
    17  * @since 2.0.0
    18  */
    19 class WP {
    20     /**
    21      * Public query variables.
    22      *
    23      * Long list of public query variables.
    24      *
    25      * @since 2.0.0
    26      * @access public
    27      * @var array
    28      */
    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.0
    37      * @var array
    38      */
    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.0
    45      * @var array
    46      */
    47     var $extra_query_vars = array();
    48 
    49     /**
    50      * Query variables for setting up the WordPress Query Loop.
    51      *
    52      * @since 2.0.0
    53      * @var array
    54      */
    55     var $query_vars;
    56 
    57     /**
    58      * String parsed to set the query variables.
    59      *
    60      * @since 2.0.0
    61      * @var string
    62      */
    63     var $query_string;
    64 
    65     /**
    66      * Permalink or requested URI.
    67      *
    68      * @since 2.0.0
    69      * @var string
    70      */
    71     var $request;
    72 
    73     /**
    74      * Rewrite rule the request matched.
    75      *
    76      * @since 2.0.0
    77      * @var string
    78      */
    79     var $matched_rule;
    80 
    81     /**
    82      * Rewrite query the request matched.
    83      *
    84      * @since 2.0.0
    85      * @var string
    86      */
    87     var $matched_query;
    88 
    89     /**
    90      * Whether already did the permalink.
    91      *
    92      * @since 2.0.0
    93      * @var bool
    94      */
    95     var $did_permalink = false;
    96 
    97     /**
    98      * Add name to list of public query variables.
    99      *
    100      * @since 2.1.0
    101      *
    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.0
    113      *
    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 many
    125      * filters and actions that can be used to further manipulate the result.
    126      *
    127      * @since 2.0.0
    128      *
    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             else
    155                 $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             else
    166                 $home_path = '';
    167             $home_path = trim($home_path, '/');
    168 
    169             // Trim path info from the end and the leading home path from the
    170             // front.  For path info requests, this leaves us with the requesting
    171             // filename, if any.  For 404 requests, this leaves us with the
    172             // 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 and
    185             //  $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 calls
    201                 if ( $req_uri == 'wp-app.php' )
    202                     break;
    203 
    204                 // If the requesting file is the anchor of the match, prepend it
    205                 // 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 var
    226                     // 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_queryable
    287         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 showing
    314      * a feed, it will also send last-modified, etag, and 304 status if needed.
    315      *
    316      * @since 2.0.0
    317      */
    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 changed
    334             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             else
    347                 $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 GET
    353             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 timestamp
    359             $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 should
    389      * use the 'request' filter instead.
    390      *
    391      * @since 2.0.0
    392      */
    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 should
    415      * be taken when naming global variables that might interfere with the
    416      * 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.0
    423      */
    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.0
    446      */
    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_vars
    455      * @since 2.0.0
    456      */
    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 match
    467      * any object (e.g. an existing-but-empty category, tag, author) and a 404 was not already
    468      * issued, and if the request was not a search or the homepage.
    469      *
    470      * Otherwise, issue a 200.
    471      *
    472      * @since 2.0.0
    473      */
    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. It
    496      * allows for accessing the properties and methods to further manipulate the
    497      * object.
    498      *
    499      * @since 2.0.0
    500      *
    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.0
    519      *
    520      * @return WP
    521      */
    522     function WP() {
    523         // Empty.
    524     }
    525 }
    526 
    527 /**
    528  * WordPress Error class.
    529  *
    530  * Container for checking for WordPress errors and error messages. Return
    531  * 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 and
    533  * if not handled properly will result in code errors.
    534  *
    535  * @package WordPress
    536  * @since 2.1.0
    537  */
    538 class WP_Error {
    539     /**
    540      * Stores the list of errors.
    541      *
    542      * @since 2.1.0
    543      * @var array
    544      * @access private
    545      */
    546     var $errors = array();
    547 
    548     /**
    549      * Stores the list of data for error codes.
    550      *
    551      * @since 2.1.0
    552      * @var array
    553      * @access private
    554      */
    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 to
    561      * add multiple messages to the same code, but with other methods in the
    562      * class.
    563      *
    564      * All parameters are optional, but if the code parameter is set, then the
    565      * data parameter is optional.
    566      *
    567      * @since 2.1.0
    568      *
    569      * @param string|int $code Error code
    570      * @param string $message Error message
    571      * @param mixed $data Optional. Error data.
    572      * @return WP_Error
    573      */
    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.0
    588      * @access public
    589      *
    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.0
    603      * @access public
    604      *
    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.0
    620      *
    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         else
    637             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 is
    644      * given then the first code available will be used.
    645      *
    646      * @since 2.1.0
    647      *
    648      * @param string|int $code Optional. Error code to retrieve message.
    649      * @return string
    650      */
    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.0
    664      *
    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.0
    681      * @access public
    682      *
    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.0
    699      *
    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 the
    715  * parent is also WP_Error, so can't inherit WP_Error and still use this
    716  * function.
    717  *
    718  * @since 2.1.0
    719  *
    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 classes
    733  * do not need to implement all of the abstract methods in the class. The child
    734  * only needs to implement the methods that are needed. Also, the methods are
    735  * not strictly abstract in that the parameter definition needs to be followed.
    736  * The child classes can have additional parameters.
    737  *
    738  * @package WordPress
    739  * @since 2.1.0
    740  * @abstract
    741  */
    742 class Walker {
    743     /**
    744      * What the class handles.
    745      *
    746      * @since 2.1.0
    747      * @var string
    748      * @access public
    749      */
    750     var $tree_type;
    751 
    752     /**
    753      * DB fields to use.
    754      *
    755      * @since 2.1.0
    756      * @var array
    757      * @access protected
    758      */
    759     var $db_fields;
    760 
    761     /**
    762      * Max number of pages walked by the paged walker
    763      *
    764      * @since 2.7.0
    765      * @var int
    766      * @access protected
    767      */
    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 holds
    774      * additional values that may be used with the child class methods. This
    775      * method is called at the start of the output list.
    776      *
    777      * @since 2.1.0
    778      * @abstract
    779      *
    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 holds
    788      * additional values that may be used with the child class methods. This
    789      * method finishes the list at the end of output of the elements.
    790      *
    791      * @since 2.1.0
    792      * @abstract
    793      *
    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 holds
    802      * additional values that may be used with the child class methods. Includes
    803      * the element output also.
    804      *
    805      * @since 2.1.0
    806      * @abstract
    807      *
    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 holds
    816      * additional values that may be used with the child class methods.
    817      *
    818      * @since 2.1.0
    819      * @abstract
    820      *
    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 max
    830      * depth and no ignore elements under that depth. It is possible to set the
    831      * 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.0
    836      *
    837      * @param object $element Data object
    838      * @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 $args
    842      * @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 element
    853         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 element
    861         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 delimiter
    868                     $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 delimiter
    878             $cb_args = array_merge( array(&$output, $depth), $args);
    879             call_user_func_array(array(&$this, 'end_lvl'), $cb_args);
    880         }
    881 
    882         //end this element
    883         $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 of
    891      * elements. max_depth = -1 means flatly display every element. max_depth =
    892      * 0 means display all levels. max_depth > 0  specifies the number of
    893      * display levels.
    894      *
    895      * @since 2.1.0
    896      *
    897      * @param array $elements
    898      * @param int $max_depth
    899      * @return string
    900      */
    901     function walk( $elements, $max_depth) {
    902 
    903         $args = array_slice(func_get_args(), 2);
    904         $output = '';
    905 
    906         if ($max_depth < -1) //invalid parameter
    907             return $output;
    908 
    909         if (empty($elements)) //nothing to walk
    910             return $output;
    911 
    912         $id_field = $this->db_fields['id'];
    913         $parent_field = $this->db_fields['parent'];
    914 
    915         // flat display
    916         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 order
    925          * separate elements into two buckets: top level and children elements
    926          * 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             else
    935                 $children_elements[ $e->$parent_field ][] = $e;
    936         }
    937 
    938         /*
    939          * when none of the elements is top level
    940          * assume the first one must be root of the sub elements
    941          */
    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                 else
    953                     $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 regardless
    963          */
    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 elements
    976      *
    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 elements
    979      * belonging to that page, then lists them and all of their children in hierarchical order.
    980      *
    981      * @package WordPress
    982      * @since 2.7
    983      * @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 elements
    986      */
    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 paging
    1004             $paging = false;
    1005             $start = 0;
    1006             if ( -1 == $max_depth )
    1007                 $end = $total_top;
    1008