4 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');
6 var $private_query_vars = array('offset', 'posts_per_page', 'posts_per_archive_page', 'what_to_show', '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');
7 var $extra_query_vars = array();
14 var $did_permalink = false;
16 function add_query_var($qv) {
17 if ( !in_array($qv, $this->public_query_vars) )
18 $this->public_query_vars[] = $qv;
21 function set_query_var($key, $value) {
22 $this->query_vars[$key] = $value;
25 function parse_request($extra_query_vars = '') {
28 $this->query_vars = array();
30 if ( is_array($extra_query_vars) )
31 $this->extra_query_vars = & $extra_query_vars;
32 else if (! empty($extra_query_vars))
33 parse_str($extra_query_vars, $this->extra_query_vars);
35 // Process PATH_INFO, REQUEST_URI, and 404 for permalinks.
37 // Fetch the rewrite rules.
38 $rewrite = $wp_rewrite->wp_rewrite_rules();
40 if (! empty($rewrite)) {
41 // If we match a rewrite rule, this will be cleared.
43 $this->did_permalink = true;
45 if ( isset($_SERVER['PATH_INFO']) )
46 $pathinfo = $_SERVER['PATH_INFO'];
49 $pathinfo_array = explode('?', $pathinfo);
50 $pathinfo = str_replace("%", "%25", $pathinfo_array[0]);
51 $req_uri = $_SERVER['REQUEST_URI'];
52 $req_uri_array = explode('?', $req_uri);
53 $req_uri = $req_uri_array[0];
54 $self = $_SERVER['PHP_SELF'];
55 $home_path = parse_url(get_option('home'));
56 if ( isset($home_path['path']) )
57 $home_path = $home_path['path'];
60 $home_path = trim($home_path, '/');
62 // Trim path info from the end and the leading home path from the
63 // front. For path info requests, this leaves us with the requesting
64 // filename, if any. For 404 requests, this leaves us with the
65 // requested permalink.
66 $req_uri = str_replace($pathinfo, '', rawurldecode($req_uri));
67 $req_uri = trim($req_uri, '/');
68 $req_uri = preg_replace("|^$home_path|", '', $req_uri);
69 $req_uri = trim($req_uri, '/');
70 $pathinfo = trim($pathinfo, '/');
71 $pathinfo = preg_replace("|^$home_path|", '', $pathinfo);
72 $pathinfo = trim($pathinfo, '/');
73 $self = trim($self, '/');
74 $self = preg_replace("|^$home_path|", '', $self);
75 $self = trim($self, '/');
77 // The requested permalink is in $pathinfo for path info requests and
78 // $req_uri for other requests.
79 if ( ! empty($pathinfo) && !preg_match('|^.*' . $wp_rewrite->index . '$|', $pathinfo) ) {
82 // If the request uri is the index, blank it out so that we don't try to match it against a rule.
83 if ( $req_uri == $wp_rewrite->index )
88 $this->request = $request;
91 $request_match = $request;
92 foreach ($rewrite as $match => $query) {
93 // If the requesting file is the anchor of the match, prepend it
95 if ((! empty($req_uri)) && (strpos($match, $req_uri) === 0) && ($req_uri != $request)) {
96 $request_match = $req_uri . '/' . $request;
99 if (preg_match("!^$match!", $request_match, $matches) ||
100 preg_match("!^$match!", urldecode($request_match), $matches)) {
102 $this->matched_rule = $match;
104 // Trim the query of everything up to the '?'.
105 $query = preg_replace("!^.+\?!", '', $query);
107 // Substitute the substring matches into the query.
108 eval("\$query = \"$query\";");
109 $this->matched_query = $query;
112 parse_str($query, $perma_query_vars);
114 // If we're processing a 404 request, clear the error var
115 // since we found something.
116 if (isset($_GET['error']))
117 unset($_GET['error']);
126 // If req_uri is empty or if it is a request for ourself, unset error.
127 if (empty($request) || $req_uri == $self || strpos($_SERVER['PHP_SELF'], 'wp-admin/') !== false) {
128 if (isset($_GET['error']))
129 unset($_GET['error']);
134 if (isset($perma_query_vars) && strpos($_SERVER['PHP_SELF'], 'wp-admin/') !== false)
135 unset($perma_query_vars);
137 $this->did_permalink = false;
141 $this->public_query_vars = apply_filters('query_vars', $this->public_query_vars);
143 for ($i=0; $i<count($this->public_query_vars); $i += 1) {
144 $wpvar = $this->public_query_vars[$i];
145 if (isset($this->extra_query_vars[$wpvar]))
146 $this->query_vars[$wpvar] = $this->extra_query_vars[$wpvar];
147 elseif (isset($GLOBALS[$wpvar]))
148 $this->query_vars[$wpvar] = $GLOBALS[$wpvar];
149 elseif (!empty($_POST[$wpvar]))
150 $this->query_vars[$wpvar] = $_POST[$wpvar];
151 elseif (!empty($_GET[$wpvar]))
152 $this->query_vars[$wpvar] = $_GET[$wpvar];
153 elseif (!empty($perma_query_vars[$wpvar]))
154 $this->query_vars[$wpvar] = $perma_query_vars[$wpvar];
156 if ( !empty( $this->query_vars[$wpvar] ) )
157 $this->query_vars[$wpvar] = (string) $this->query_vars[$wpvar];
160 foreach ($this->private_query_vars as $var) {
161 if (isset($this->extra_query_vars[$var]))
162 $this->query_vars[$var] = $this->extra_query_vars[$var];
163 elseif (isset($GLOBALS[$var]) && '' != $GLOBALS[$var])
164 $this->query_vars[$var] = $GLOBALS[$var];
168 $this->query_vars['error'] = $error;
170 $this->query_vars = apply_filters('request', $this->query_vars);
172 do_action_ref_array('parse_request', array(&$this));
175 function send_headers() {
176 @header('X-Pingback: '. get_bloginfo('pingback_url'));
177 if ( is_user_logged_in() )
179 if ( !empty($this->query_vars['error']) && '404' == $this->query_vars['error'] ) {
180 status_header( 404 );
181 if ( !is_user_logged_in() )
183 @header('Content-Type: ' . get_option('html_type') . '; charset=' . get_option('blog_charset'));
184 } else if ( empty($this->query_vars['feed']) ) {
185 @header('Content-Type: ' . get_option('html_type') . '; charset=' . get_option('blog_charset'));
187 // We're showing a feed, so WP is indeed the only thing that last changed
188 if ( !empty($this->query_vars['withcomments'])
189 || ( empty($this->query_vars['withoutcomments'])
190 && ( !empty($this->query_vars['p'])
191 || !empty($this->query_vars['name'])
192 || !empty($this->query_vars['page_id'])
193 || !empty($this->query_vars['pagename'])
194 || !empty($this->query_vars['attachment'])
195 || !empty($this->query_vars['attachment_id'])
199 $wp_last_modified = mysql2date('D, d M Y H:i:s', get_lastcommentmodified('GMT'), 0).' GMT';
201 $wp_last_modified = mysql2date('D, d M Y H:i:s', get_lastpostmodified('GMT'), 0).' GMT';
202 $wp_etag = '"' . md5($wp_last_modified) . '"';
203 @header("Last-Modified: $wp_last_modified");
204 @header("ETag: $wp_etag");
206 // Support for Conditional GET
207 if (isset($_SERVER['HTTP_IF_NONE_MATCH']))
208 $client_etag = stripslashes(stripslashes($_SERVER['HTTP_IF_NONE_MATCH']));
209 else $client_etag = false;
211 $client_last_modified = empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? '' : trim($_SERVER['HTTP_IF_MODIFIED_SINCE']);
212 // If string is empty, return 0. If not, attempt to parse into a timestamp
213 $client_modified_timestamp = $client_last_modified ? strtotime($client_last_modified) : 0;
215 // Make a timestamp for our most recent modification...
216 $wp_modified_timestamp = strtotime($wp_last_modified);
218 if ( ($client_last_modified && $client_etag) ?
219 (($client_modified_timestamp >= $wp_modified_timestamp) && ($client_etag == $wp_etag)) :
220 (($client_modified_timestamp >= $wp_modified_timestamp) || ($client_etag == $wp_etag)) ) {
221 status_header( 304 );
226 do_action_ref_array('send_headers', array(&$this));
229 function build_query_string() {
230 $this->query_string = '';
231 foreach (array_keys($this->query_vars) as $wpvar) {
232 if ( '' != $this->query_vars[$wpvar] ) {
233 $this->query_string .= (strlen($this->query_string) < 1) ? '' : '&';
234 if ( !is_scalar($this->query_vars[$wpvar]) ) // Discard non-scalars.
236 $this->query_string .= $wpvar . '=' . rawurlencode($this->query_vars[$wpvar]);
240 // query_string filter deprecated. Use request filter instead.
241 if ( has_filter('query_string') ) { // Don't bother filtering and parsing if no plugins are hooked in.
242 $this->query_string = apply_filters('query_string', $this->query_string);
243 parse_str($this->query_string, $this->query_vars);
247 function register_globals() {
249 // Extract updated query vars back into global namespace.
250 foreach ($wp_query->query_vars as $key => $value) {
251 $GLOBALS[$key] = $value;
254 $GLOBALS['query_string'] = & $this->query_string;
255 $GLOBALS['posts'] = & $wp_query->posts;
256 $GLOBALS['post'] = & $wp_query->post;
257 $GLOBALS['request'] = & $wp_query->request;
259 if ( is_single() || is_page() ) {
260 $GLOBALS['more'] = 1;
261 $GLOBALS['single'] = 1;
266 wp_get_current_user();
269 function query_posts() {
270 global $wp_the_query;
271 $this->build_query_string();
272 $wp_the_query->query($this->query_vars);
275 function handle_404() {
277 // Issue a 404 if a permalink request doesn't match any posts. Don't
278 // issue a 404 if one was already issued, if the request was a search,
279 // or if the request was a regular query string request rather than a
280 // permalink request.
281 if ( (0 == count($wp_query->posts)) && !is_404() && !is_search() && ( $this->did_permalink || (!empty($_SERVER['QUERY_STRING']) && (false === strpos($_SERVER['REQUEST_URI'], '?'))) ) ) {
282 $wp_query->set_404();
283 status_header( 404 );
285 } elseif( is_404() != true ) {
286 status_header( 200 );
290 function main($query_args = '') {
292 $this->parse_request($query_args);
293 $this->send_headers();
294 $this->query_posts();
296 $this->register_globals();
297 do_action_ref_array('wp', array(&$this));
306 var $errors = array();
307 var $error_data = array();
309 function WP_Error($code = '', $message = '', $data = '') {
313 $this->errors[$code][] = $message;
315 if ( ! empty($data) )
316 $this->error_data[$code] = $data;
319 function get_error_codes() {
320 if ( empty($this->errors) )
323 return array_keys($this->errors);
326 function get_error_code() {
327 $codes = $this->get_error_codes();
335 function get_error_messages($code = '') {
336 // Return all messages if no code specified.
337 if ( empty($code) ) {
338 $all_messages = array();
339 foreach ( $this->errors as $code => $messages )
340 $all_messages = array_merge($all_messages, $messages);
342 return $all_messages;
345 if ( isset($this->errors[$code]) )
346 return $this->errors[$code];
351 function get_error_message($code = '') {
353 $code = $this->get_error_code();
354 $messages = $this->get_error_messages($code);
355 if ( empty($messages) )
360 function get_error_data($code = '') {
362 $code = $this->get_error_code();
364 if ( isset($this->error_data[$code]) )
365 return $this->error_data[$code];
369 function add($code, $message, $data = '') {
370 $this->errors[$code][] = $message;
371 if ( ! empty($data) )
372 $this->error_data[$code] = $data;
375 function add_data($data, $code = '') {
377 $code = $this->get_error_code();
379 $this->error_data[$code] = $data;
383 function is_wp_error($thing) {
384 if ( is_object($thing) && is_a($thing, 'WP_Error') )
390 * A class for displaying various tree-like structures.
391 * Extend the Walker class to use it, see examples at the bottom
398 function start_lvl(&$output) {}
399 function end_lvl(&$output) {}
400 function start_el(&$output) {}
401 function end_el(&$output) {}
404 * display one element if the element doesn't have any children
405 * otherwise, display the element and its children
407 function display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output ) {
412 $id_field = $this->db_fields['id'];
413 $parent_field = $this->db_fields['parent'];
415 //display this element
416 $cb_args = array_merge( array(&$output, $element, $depth), $args);
417 call_user_func_array(array(&$this, 'start_el'), $cb_args);
419 if ( $max_depth == 0 ||
420 ($max_depth != 0 && $max_depth > $depth+1 )) { //whether to descend
422 for ( $i = 0; $i < sizeof( $children_elements ); $i++ ) {
424 $child = $children_elements[$i];
425 if ( $child->$parent_field == $element->$id_field ) {
427 if ( !isset($newlevel) ) {
429 //start the child delimiter
430 $cb_args = array_merge( array(&$output, $depth), $args);
431 call_user_func_array(array(&$this, 'start_lvl'), $cb_args);
434 array_splice( $children_elements, $i, 1 );
435 $this->display_element( $child, $children_elements, $max_depth, $depth + 1, $args, $output );
441 if ( isset($newlevel) && $newlevel ){
442 //end the child delimiter
443 $cb_args = array_merge( array(&$output, $depth), $args);
444 call_user_func_array(array(&$this, 'end_lvl'), $cb_args);
448 $cb_args = array_merge( array(&$output, $element, $depth), $args);
449 call_user_func_array(array(&$this, 'end_el'), $cb_args);
453 * displays array of elements hierarchically
454 * it is a generic function which does not assume any existing order of elements
455 * max_depth = -1 means flatly display every element
456 * max_depth = 0 means display all levels
457 * max_depth > 0 specifies the number of display levels.
459 function walk( $elements, $max_depth) {
461 $args = array_slice(func_get_args(), 2);
464 if ($max_depth < -1) //invalid parameter
467 if (empty($elements)) //nothing to walk
470 $id_field = $this->db_fields['id'];
471 $parent_field = $this->db_fields['parent'];
474 if ( -1 == $max_depth ) {
475 $empty_array = array();
476 foreach ( $elements as $e )
477 $this->display_element( $e, $empty_array, 1, 0, $args, $output );
482 * need to display in hierarchical order
483 * splice elements into two buckets: those without parent and those with parent
485 $top_level_elements = array();
486 $children_elements = array();
487 foreach ( $elements as $e) {
488 if ( 0 == $e->$parent_field )
489 $top_level_elements[] = $e;
491 $children_elements[] = $e;
495 * none of the elements is top level
496 * the first one must be root of the sub elements
498 if ( !$top_level_elements ) {
500 $root = $children_elements[0];
501 for ( $i = 0; $i < sizeof( $children_elements ); $i++ ) {
503 $child = $children_elements[$i];
504 if ($root->$parent_field == $child->$parent_field ) {
505 $top_level_elements[] = $child;
506 array_splice( $children_elements, $i, 1 );
512 foreach ( $top_level_elements as $e )
513 $this->display_element( $e, $children_elements, $max_depth, 0, $args, $output );
516 * if we are displaying all levels, and remaining children_elements is not empty,
517 * then we got orphans, which should be displayed regardless
519 if ( ( $max_depth == 0 ) && sizeof( $children_elements ) > 0 ) {
520 $empty_array = array();
521 foreach ( $children_elements as $orphan_e )
522 $this->display_element( $orphan_e, $empty_array, 1, 0, $args, $output );
528 class Walker_Page extends Walker {
529 var $tree_type = 'page';
530 var $db_fields = array ('parent' => 'post_parent', 'id' => 'ID'); //TODO: decouple this
532 function start_lvl(&$output, $depth) {
533 $indent = str_repeat("\t", $depth);
534 $output .= "\n$indent<ul>\n";
537 function end_lvl(&$output, $depth) {
538 $indent = str_repeat("\t", $depth);
539 $output .= "$indent</ul>\n";
542 function start_el(&$output, $page, $depth, $current_page, $args) {
544 $indent = str_repeat("\t", $depth);
548 extract($args, EXTR_SKIP);
549 $css_class = 'page_item page-item-'.$page->ID;
550 if ( !empty($current_page) ) {
551 $_current_page = get_page( $current_page );
552 if ( in_array($page->ID, (array) $_current_page->ancestors) )
553 $css_class .= ' current_page_ancestor';
554 if ( $page->ID == $current_page )
555 $css_class .= ' current_page_item';
556 elseif ( $_current_page && $page->ID == $_current_page->post_parent )
557 $css_class .= ' current_page_parent';
560 $output .= $indent . '<li class="' . $css_class . '"><a href="' . get_page_link($page->ID) . '" title="' . attribute_escape(apply_filters('the_title', $page->post_title)) . '">' . apply_filters('the_title', $page->post_title) . '</a>';
562 if ( !empty($show_date) ) {
563 if ( 'modified' == $show_date )
564 $time = $page->post_modified;
566 $time = $page->post_date;
568 $output .= " " . mysql2date($date_format, $time);
572 function end_el(&$output, $page, $depth) {
573 $output .= "</li>\n";
578 class Walker_PageDropdown extends Walker {
579 var $tree_type = 'page';
580 var $db_fields = array ('parent' => 'post_parent', 'id' => 'ID'); //TODO: decouple this
582 function start_el(&$output, $page, $depth, $args) {
583 $pad = str_repeat(' ', $depth * 3);
585 $output .= "\t<option value=\"$page->ID\"";
586 if ( $page->ID == $args['selected'] )
587 $output .= ' selected="selected"';
589 $title = wp_specialchars($page->post_title);
590 $output .= "$pad$title";
591 $output .= "</option>\n";
595 class Walker_Category extends Walker {
596 var $tree_type = 'category';
597 var $db_fields = array ('parent' => 'parent', 'id' => 'term_id'); //TODO: decouple this
599 function start_lvl(&$output, $depth, $args) {
600 if ( 'list' != $args['style'] )
603 $indent = str_repeat("\t", $depth);
604 $output .= "$indent<ul class='children'>\n";
607 function end_lvl(&$output, $depth, $args) {
608 if ( 'list' != $args['style'] )
611 $indent = str_repeat("\t", $depth);
612 $output .= "$indent</ul>\n";
615 function start_el(&$output, $category, $depth, $args) {
618 $cat_name = attribute_escape( $category->name);
619 $cat_name = apply_filters( 'list_cats', $cat_name, $category );
620 $link = '<a href="' . get_category_link( $category->term_id ) . '" ';
621 if ( $use_desc_for_title == 0 || empty($category->description) )
622 $link .= 'title="' . sprintf(__( 'View all posts filed under %s' ), $cat_name) . '"';
624 $link .= 'title="' . attribute_escape( apply_filters( 'category_description', $category->description, $category )) . '"';
626 $link .= $cat_name . '</a>';
628 if ( (! empty($feed_image)) || (! empty($feed)) ) {
631 if ( empty($feed_image) )
634 $link .= '<a href="' . get_category_feed_link($category->term_id, $feed_type) . '"';
637 $alt = ' alt="' . sprintf(__( 'Feed for all posts filed under %s' ), $cat_name ) . '"';
639 $title = ' title="' . $feed . '"';
640 $alt = ' alt="' . $feed . '"';
647 if ( empty($feed_image) )
650 $link .= "<img src='$feed_image'$alt$title" . ' />';
652 if ( empty($feed_image) )
656 if ( isset($show_count) && $show_count )
657 $link .= ' (' . intval($category->count) . ')';
659 if ( isset($show_date) && $show_date ) {
660 $link .= ' ' . gmdate('Y-m-d', $category->last_update_timestamp);
663 if ( isset($current_category) && $current_category )
664 $_current_category = get_category( $current_category );
666 if ( 'list' == $args['style'] ) {
668 $class = 'cat-item cat-item-'.$category->term_id;
669 if ( isset($current_category) && $current_category && ($category->term_id == $current_category) )
670 $class .= ' current-cat';
671 elseif ( isset($_current_category) && $_current_category && ($category->term_id == $_current_category->parent) )
672 $class .= ' current-cat-parent';
673 $output .= ' class="'.$class.'"';
674 $output .= ">$link\n";
676 $output .= "\t$link<br />\n";
680 function end_el(&$output, $page, $depth, $args) {
681 if ( 'list' != $args['style'] )
684 $output .= "</li>\n";
689 class Walker_CategoryDropdown extends Walker {
690 var $tree_type = 'category';
691 var $db_fields = array ('parent' => 'parent', 'id' => 'term_id'); //TODO: decouple this
693 function start_el(&$output, $category, $depth, $args) {
694 $pad = str_repeat(' ', $depth * 3);
696 $cat_name = apply_filters('list_cats', $category->name, $category);
697 $output .= "\t<option value=\"".$category->term_id."\"";
698 if ( $category->term_id == $args['selected'] )
699 $output .= ' selected="selected"';
701 $output .= $pad.$cat_name;
702 if ( $args['show_count'] )
703 $output .= ' ('. $category->count .')';
704 if ( $args['show_last_update'] ) {
706 $output .= ' ' . gmdate($format, $category->last_update_timestamp);
708 $output .= "</option>\n";
712 class WP_Ajax_Response {
713 var $responses = array();
715 function WP_Ajax_Response( $args = '' ) {
720 // a WP_Error object can be passed in 'id' or 'data'
721 function add( $args = '' ) {
723 'what' => 'object', 'action' => false,
724 'id' => '0', 'old_id' => false,
725 'position' => 1, // -1 = top, 1 = bottom, html ID = after, -html ID = before
726 'data' => '', 'supplemental' => array()
729 $r = wp_parse_args( $args, $defaults );
730 extract( $r, EXTR_SKIP );
731 $position = preg_replace( '/[^a-z0-9:_-]/i', '', $position );
733 if ( is_wp_error($id) ) {
739 if ( is_wp_error($data) ) {
740 foreach ( $data->get_error_codes() as $code ) {
741 $response .= "<wp_error code='$code'><![CDATA[" . $data->get_error_message($code) . "]]></wp_error>";
742 if ( !$error_data = $data->get_error_data($code) )
745 if ( is_object($error_data) ) {
746 $class = ' class="' . get_class($error_data) . '"';
747 $error_data = get_object_vars($error_data);
750 $response .= "<wp_error_data code='$code'$class>";
752 if ( is_scalar($error_data) ) {
753 $response .= "<![CDATA[$error_data]]>";
754 } elseif ( is_array($error_data) ) {
755 foreach ( $error_data as $k => $v )
756 $response .= "<$k><![CDATA[$v]]></$k>";
759 $response .= "</wp_error_data>";
762 $response = "<response_data><![CDATA[$data]]></response_data>";
766 if ( (array) $supplemental ) {
767 foreach ( $supplemental as $k => $v )
768 $s .= "<$k><![CDATA[$v]]></$k>";
769 $s = "<supplemental>$s</supplemental>";
772 if ( false === $action )
773 $action = $_POST['action'];
776 $x .= "<response action='{$action}_$id'>"; // The action attribute in the xml output is formatted like a nonce action
777 $x .= "<$what id='$id' " . ( false === $old_id ? '' : "old_id='$old_id' " ) . "position='$position'>";
783 $this->responses[] = $x;
788 header('Content-Type: text/xml');
789 echo "<?xml version='1.0' standalone='yes'?><wp_ajax>";
790 foreach ( $this->responses as $response )