[ Index ] |
WordPress Cross Reference |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * WordPress environment setup class. 4 * 5 * @package WordPress 6 * @since 2.0.0 7 */ 8 class WP { 9 /** 10 * Public query variables. 11 * 12 * Long list of public query variables. 13 * 14 * @since 2.0.0 15 * @access public 16 * @var array 17 */ 18 var $public_query_vars = array('m', 'p', 'posts', 'w', 'cat', 'withcomments', 'withoutcomments', 's', 'search', 'exact', 'sentence', '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'); 19 20 /** 21 * Private query variables. 22 * 23 * Long list of private query variables. 24 * 25 * @since 2.0.0 26 * @var array 27 */ 28 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', 'post_parent__in', 'post_parent__not_in' ); 29 30 /** 31 * Extra query variables set by the user. 32 * 33 * @since 2.1.0 34 * @var array 35 */ 36 var $extra_query_vars = array(); 37 38 /** 39 * Query variables for setting up the WordPress Query Loop. 40 * 41 * @since 2.0.0 42 * @var array 43 */ 44 var $query_vars; 45 46 /** 47 * String parsed to set the query variables. 48 * 49 * @since 2.0.0 50 * @var string 51 */ 52 var $query_string; 53 54 /** 55 * Permalink or requested URI. 56 * 57 * @since 2.0.0 58 * @var string 59 */ 60 var $request; 61 62 /** 63 * Rewrite rule the request matched. 64 * 65 * @since 2.0.0 66 * @var string 67 */ 68 var $matched_rule; 69 70 /** 71 * Rewrite query the request matched. 72 * 73 * @since 2.0.0 74 * @var string 75 */ 76 var $matched_query; 77 78 /** 79 * Whether already did the permalink. 80 * 81 * @since 2.0.0 82 * @var bool 83 */ 84 var $did_permalink = false; 85 86 /** 87 * Add name to list of public query variables. 88 * 89 * @since 2.1.0 90 * 91 * @param string $qv Query variable name. 92 */ 93 function add_query_var($qv) { 94 if ( !in_array($qv, $this->public_query_vars) ) 95 $this->public_query_vars[] = $qv; 96 } 97 98 /** 99 * Set the value of a query variable. 100 * 101 * @since 2.3.0 102 * 103 * @param string $key Query variable name. 104 * @param mixed $value Query variable value. 105 */ 106 function set_query_var($key, $value) { 107 $this->query_vars[$key] = $value; 108 } 109 110 /** 111 * Parse request to find correct WordPress query. 112 * 113 * Sets up the query variables based on the request. There are also many 114 * filters and actions that can be used to further manipulate the result. 115 * 116 * @since 2.0.0 117 * 118 * @param array|string $extra_query_vars Set the extra query variables. 119 */ 120 function parse_request($extra_query_vars = '') { 121 global $wp_rewrite; 122 123 /** 124 * Filter whether to parse the request. 125 * 126 * @since 3.5.0 127 * 128 * @param bool $bool Whether or not to parse the request. Default true. 129 * @param WP $this Current WordPress environment instance. 130 * @param array|string $extra_query_vars Extra passed query variables. 131 */ 132 if ( ! apply_filters( 'do_parse_request', true, $this, $extra_query_vars ) ) 133 return; 134 135 $this->query_vars = array(); 136 $post_type_query_vars = array(); 137 138 if ( is_array($extra_query_vars) ) 139 $this->extra_query_vars = & $extra_query_vars; 140 else if (! empty($extra_query_vars)) 141 parse_str($extra_query_vars, $this->extra_query_vars); 142 143 // Process PATH_INFO, REQUEST_URI, and 404 for permalinks. 144 145 // Fetch the rewrite rules. 146 $rewrite = $wp_rewrite->wp_rewrite_rules(); 147 148 if ( ! empty($rewrite) ) { 149 // If we match a rewrite rule, this will be cleared. 150 $error = '404'; 151 $this->did_permalink = true; 152 153 $pathinfo = isset( $_SERVER['PATH_INFO'] ) ? $_SERVER['PATH_INFO'] : ''; 154 list( $pathinfo ) = explode( '?', $pathinfo ); 155 $pathinfo = str_replace( "%", "%25", $pathinfo ); 156 157 list( $req_uri ) = explode( '?', $_SERVER['REQUEST_URI'] ); 158 $self = $_SERVER['PHP_SELF']; 159 $home_path = trim( parse_url( home_url(), PHP_URL_PATH ), '/' ); 160 161 // Trim path info from the end and the leading home path from the 162 // front. For path info requests, this leaves us with the requesting 163 // filename, if any. For 404 requests, this leaves us with the 164 // requested permalink. 165 $req_uri = str_replace($pathinfo, '', $req_uri); 166 $req_uri = trim($req_uri, '/'); 167 $req_uri = preg_replace("|^$home_path|i", '', $req_uri); 168 $req_uri = trim($req_uri, '/'); 169 $pathinfo = trim($pathinfo, '/'); 170 $pathinfo = preg_replace("|^$home_path|i", '', $pathinfo); 171 $pathinfo = trim($pathinfo, '/'); 172 $self = trim($self, '/'); 173 $self = preg_replace("|^$home_path|i", '', $self); 174 $self = trim($self, '/'); 175 176 // The requested permalink is in $pathinfo for path info requests and 177 // $req_uri for other requests. 178 if ( ! empty($pathinfo) && !preg_match('|^.*' . $wp_rewrite->index . '$|', $pathinfo) ) { 179 $request = $pathinfo; 180 } else { 181 // If the request uri is the index, blank it out so that we don't try to match it against a rule. 182 if ( $req_uri == $wp_rewrite->index ) 183 $req_uri = ''; 184 $request = $req_uri; 185 } 186 187 $this->request = $request; 188 189 // Look for matches. 190 $request_match = $request; 191 if ( empty( $request_match ) ) { 192 // An empty request could only match against ^$ regex 193 if ( isset( $rewrite['$'] ) ) { 194 $this->matched_rule = '$'; 195 $query = $rewrite['$']; 196 $matches = array(''); 197 } 198 } else { 199 foreach ( (array) $rewrite as $match => $query ) { 200 // If the requesting file is the anchor of the match, prepend it to the path info. 201 if ( ! empty($req_uri) && strpos($match, $req_uri) === 0 && $req_uri != $request ) 202 $request_match = $req_uri . '/' . $request; 203 204 if ( preg_match("#^$match#", $request_match, $matches) || 205 preg_match("#^$match#", urldecode($request_match), $matches) ) { 206 207 if ( $wp_rewrite->use_verbose_page_rules && preg_match( '/pagename=\$matches\[([0-9]+)\]/', $query, $varmatch ) ) { 208 // this is a verbose page match, lets check to be sure about it 209 if ( ! get_page_by_path( $matches[ $varmatch[1] ] ) ) 210 continue; 211 } 212 213 // Got a match. 214 $this->matched_rule = $match; 215 break; 216 } 217 } 218 } 219 220 if ( isset( $this->matched_rule ) ) { 221 // Trim the query of everything up to the '?'. 222 $query = preg_replace("!^.+\?!", '', $query); 223 224 // Substitute the substring matches into the query. 225 $query = addslashes(WP_MatchesMapRegex::apply($query, $matches)); 226 227 $this->matched_query = $query; 228 229 // Parse the query. 230 parse_str($query, $perma_query_vars); 231 232 // If we're processing a 404 request, clear the error var since we found something. 233 if ( '404' == $error ) 234 unset( $error, $_GET['error'] ); 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 unset( $error, $_GET['error'] ); 240 241 if ( isset($perma_query_vars) && strpos($_SERVER['PHP_SELF'], 'wp-admin/') !== false ) 242 unset( $perma_query_vars ); 243 244 $this->did_permalink = false; 245 } 246 } 247 248 /** 249 * Filter the query variables whitelist before processing. 250 * 251 * Allows (publicly allowed) query vars to be added, removed, or changed prior 252 * to executing the query. Needed to allow custom rewrite rules using your own arguments 253 * to work, or any other custom query variables you want to be publicly available. 254 * 255 * @since 1.5.0 256 * 257 * @param array $public_query_vars The array of whitelisted query variables. 258 */ 259 $this->public_query_vars = apply_filters( 'query_vars', $this->public_query_vars ); 260 261 foreach ( get_post_types( array(), 'objects' ) as $post_type => $t ) 262 if ( $t->query_var ) 263 $post_type_query_vars[$t->query_var] = $post_type; 264 265 foreach ( $this->public_query_vars as $wpvar ) { 266 if ( isset( $this->extra_query_vars[$wpvar] ) ) 267 $this->query_vars[$wpvar] = $this->extra_query_vars[$wpvar]; 268 elseif ( isset( $_POST[$wpvar] ) ) 269 $this->query_vars[$wpvar] = $_POST[$wpvar]; 270 elseif ( isset( $_GET[$wpvar] ) ) 271 $this->query_vars[$wpvar] = $_GET[$wpvar]; 272 elseif ( isset( $perma_query_vars[$wpvar] ) ) 273 $this->query_vars[$wpvar] = $perma_query_vars[$wpvar]; 274 275 if ( !empty( $this->query_vars[$wpvar] ) ) { 276 if ( ! is_array( $this->query_vars[$wpvar] ) ) { 277 $this->query_vars[$wpvar] = (string) $this->query_vars[$wpvar]; 278 } else { 279 foreach ( $this->query_vars[$wpvar] as $vkey => $v ) { 280 if ( !is_object( $v ) ) { 281 $this->query_vars[$wpvar][$vkey] = (string) $v; 282 } 283 } 284 } 285 286 if ( isset($post_type_query_vars[$wpvar] ) ) { 287 $this->query_vars['post_type'] = $post_type_query_vars[$wpvar]; 288 $this->query_vars['name'] = $this->query_vars[$wpvar]; 289 } 290 } 291 } 292 293 // Convert urldecoded spaces back into + 294 foreach ( get_taxonomies( array() , 'objects' ) as $taxonomy => $t ) 295 if ( $t->query_var && isset( $this->query_vars[$t->query_var] ) ) 296 $this->query_vars[$t->query_var] = str_replace( ' ', '+', $this->query_vars[$t->query_var] ); 297 298 // Limit publicly queried post_types to those that are publicly_queryable 299 if ( isset( $this->query_vars['post_type']) ) { 300 $queryable_post_types = get_post_types( array('publicly_queryable' => true) ); 301 if ( ! is_array( $this->query_vars['post_type'] ) ) { 302 if ( ! in_array( $this->query_vars['post_type'], $queryable_post_types ) ) 303 unset( $this->query_vars['post_type'] ); 304 } else { 305 $this->query_vars['post_type'] = array_intersect( $this->query_vars['post_type'], $queryable_post_types ); 306 } 307 } 308 309 foreach ( (array) $this->private_query_vars as $var) { 310 if ( isset($this->extra_query_vars[$var]) ) 311 $this->query_vars[$var] = $this->extra_query_vars[$var]; 312 } 313 314 if ( isset($error) ) 315 $this->query_vars['error'] = $error; 316 317 /** 318 * Filter the array of parsed query variables. 319 * 320 * @since 2.1.0 321 * 322 * @param array $query_vars The array of requested query variables. 323 */ 324 $this->query_vars = apply_filters( 'request', $this->query_vars ); 325 326 /** 327 * Fires once all query variables for the current request have been parsed. 328 * 329 * @since 2.1.0 330 * 331 * @param WP &$this Current WordPress environment instance (passed by reference). 332 */ 333 do_action_ref_array( 'parse_request', array( &$this ) ); 334 } 335 336 /** 337 * Send additional HTTP headers for caching, content type, etc. 338 * 339 * Sets the X-Pingback header, 404 status (if 404), Content-type. If showing 340 * a feed, it will also send last-modified, etag, and 304 status if needed. 341 * 342 * @since 2.0.0 343 */ 344 function send_headers() { 345 $headers = array('X-Pingback' => get_bloginfo('pingback_url')); 346 $status = null; 347 $exit_required = false; 348 349 if ( is_user_logged_in() ) 350 $headers = array_merge($headers, wp_get_nocache_headers()); 351 if ( ! empty( $this->query_vars['error'] ) ) { 352 $status = (int) $this->query_vars['error']; 353 if ( 404 === $status ) { 354 if ( ! is_user_logged_in() ) 355 $headers = array_merge($headers, wp_get_nocache_headers()); 356 $headers['Content-Type'] = get_option('html_type') . '; charset=' . get_option('blog_charset'); 357 } elseif ( in_array( $status, array( 403, 500, 502, 503 ) ) ) { 358 $exit_required = true; 359 } 360 } else if ( empty($this->query_vars['feed']) ) { 361 $headers['Content-Type'] = get_option('html_type') . '; charset=' . get_option('blog_charset'); 362 } else { 363 // We're showing a feed, so WP is indeed the only thing that last changed 364 if ( !empty($this->query_vars['withcomments']) 365 || false !== strpos( $this->query_vars['feed'], 'comments-' ) 366 || ( empty($this->query_vars['withoutcomments']) 367 && ( !empty($this->query_vars['p']) 368 || !empty($this->query_vars['name']) 369 || !empty($this->query_vars['page_id']) 370 || !empty($this->query_vars['pagename']) 371 || !empty($this->query_vars['attachment']) 372 || !empty($this->query_vars['attachment_id']) 373 ) 374 ) 375 ) 376 $wp_last_modified = mysql2date('D, d M Y H:i:s', get_lastcommentmodified('GMT'), 0).' GMT'; 377 else 378 $wp_last_modified = mysql2date('D, d M Y H:i:s', get_lastpostmodified('GMT'), 0).' GMT'; 379 $wp_etag = '"' . md5($wp_last_modified) . '"'; 380 $headers['Last-Modified'] = $wp_last_modified; 381 $headers['ETag'] = $wp_etag; 382 383 // Support for Conditional GET 384 if (isset($_SERVER['HTTP_IF_NONE_MATCH'])) 385 $client_etag = wp_unslash( $_SERVER['HTTP_IF_NONE_MATCH'] ); 386 else $client_etag = false; 387 388 $client_last_modified = empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? '' : trim($_SERVER['HTTP_IF_MODIFIED_SINCE']); 389 // If string is empty, return 0. If not, attempt to parse into a timestamp 390 $client_modified_timestamp = $client_last_modified ? strtotime($client_last_modified) : 0; 391 392 // Make a timestamp for our most recent modification... 393 $wp_modified_timestamp = strtotime($wp_last_modified); 394 395 if ( ($client_last_modified && $client_etag) ? 396 (($client_modified_timestamp >= $wp_modified_timestamp) && ($client_etag == $wp_etag)) : 397 (($client_modified_timestamp >= $wp_modified_timestamp) || ($client_etag == $wp_etag)) ) { 398 $status = 304; 399 $exit_required = true; 400 } 401 } 402 403 /** 404 * Filter the HTTP headers before they're sent to the browser. 405 * 406 * @since 2.8.0 407 * 408 * @param array $headers The list of headers to be sent. 409 * @param WP $this Current WordPress environment instance. 410 */ 411 $headers = apply_filters( 'wp_headers', $headers, $this ); 412 413 if ( ! empty( $status ) ) 414 status_header( $status ); 415 416 // If Last-Modified is set to false, it should not be sent (no-cache situation). 417 if ( isset( $headers['Last-Modified'] ) && false === $headers['Last-Modified'] ) { 418 unset( $headers['Last-Modified'] ); 419 420 // In PHP 5.3+, make sure we are not sending a Last-Modified header. 421 if ( function_exists( 'header_remove' ) ) { 422 @header_remove( 'Last-Modified' ); 423 } else { 424 // In PHP 5.2, send an empty Last-Modified header, but only as a 425 // last resort to override a header already sent. #WP23021 426 foreach ( headers_list() as $header ) { 427 if ( 0 === stripos( $header, 'Last-Modified' ) ) { 428 $headers['Last-Modified'] = ''; 429 break; 430 } 431 } 432 } 433 } 434 435 foreach( (array) $headers as $name => $field_value ) 436 @header("{$name}: {$field_value}"); 437 438 if ( $exit_required ) 439 exit(); 440 441 /** 442 * Fires once the requested HTTP headers for caching, content type, etc. have been sent. 443 * 444 * @since 2.1.0 445 * 446 * @param WP &$this Current WordPress environment instance (passed by reference). 447 */ 448 do_action_ref_array( 'send_headers', array( &$this ) ); 449 } 450 451 /** 452 * Sets the query string property based off of the query variable property. 453 * 454 * The 'query_string' filter is deprecated, but still works. Plugins should 455 * use the 'request' filter instead. 456 * 457 * @since 2.0.0 458 */ 459 function build_query_string() { 460 $this->query_string = ''; 461 foreach ( (array) array_keys($this->query_vars) as $wpvar) { 462 if ( '' != $this->query_vars[$wpvar] ) { 463 $this->query_string .= (strlen($this->query_string) < 1) ? '' : '&'; 464 if ( !is_scalar($this->query_vars[$wpvar]) ) // Discard non-scalars. 465 continue; 466 $this->query_string .= $wpvar . '=' . rawurlencode($this->query_vars[$wpvar]); 467 } 468 } 469 470 if ( has_filter( 'query_string' ) ) { // Don't bother filtering and parsing if no plugins are hooked in. 471 /** 472 * Filter the query string before parsing. 473 * 474 * @since 1.5.0 475 * @deprecated 2.1.0 Use 'query_vars' or 'request' filters instead. 476 * 477 * @param string $query_string The query string to modify. 478 */ 479 $this->query_string = apply_filters( 'query_string', $this->query_string ); 480 parse_str($this->query_string, $this->query_vars); 481 } 482 } 483 484 /** 485 * Set up the WordPress Globals. 486 * 487 * The query_vars property will be extracted to the GLOBALS. So care should 488 * be taken when naming global variables that might interfere with the 489 * WordPress environment. 490 * 491 * @global string $query_string Query string for the loop. 492 * @global array $posts The found posts. 493 * @global WP_Post|null $post The current post, if available. 494 * @global string $request The SQL statement for the request. 495 * @global int $more Only set, if single page or post. 496 * @global int $single If single page or post. Only set, if single page or post. 497 * @global WP_User $authordata Only set, if author archive. 498 * 499 * @since 2.0.0 500 */ 501 function register_globals() { 502 global $wp_query; 503 504 // Extract updated query vars back into global namespace. 505 foreach ( (array) $wp_query->query_vars as $key => $value ) { 506 $GLOBALS[ $key ] = $value; 507 } 508 509 $GLOBALS['query_string'] = $this->query_string; 510 $GLOBALS['posts'] = & $wp_query->posts; 511 $GLOBALS['post'] = isset( $wp_query->post ) ? $wp_query->post : null; 512 $GLOBALS['request'] = $wp_query->request; 513 514 if ( $wp_query->is_single() || $wp_query->is_page() ) { 515 $GLOBALS['more'] = 1; 516 $GLOBALS['single'] = 1; 517 } 518 519 if ( $wp_query->is_author() && isset( $wp_query->post ) ) 520 $GLOBALS['authordata'] = get_userdata( $wp_query->post->post_author ); 521 } 522 523 /** 524 * Set up the current user. 525 * 526 * @since 2.0.0 527 */ 528 function init() { 529 wp_get_current_user(); 530 } 531 532 /** 533 * Set up the Loop based on the query variables. 534 * 535 * @uses WP::$query_vars 536 * @since 2.0.0 537 */ 538 function query_posts() { 539 global $wp_the_query; 540 $this->build_query_string(); 541 $wp_the_query->query($this->query_vars); 542 } 543 544 /** 545 * Set the Headers for 404, if nothing is found for requested URL. 546 * 547 * Issue a 404 if a request doesn't match any posts and doesn't match 548 * any object (e.g. an existing-but-empty category, tag, author) and a 404 was not already 549 * issued, and if the request was not a search or the homepage. 550 * 551 * Otherwise, issue a 200. 552 * 553 * @since 2.0.0 554 */ 555 function handle_404() { 556 global $wp_query; 557 558 // If we've already issued a 404, bail. 559 if ( is_404() ) 560 return; 561 562 // Never 404 for the admin, robots, or if we found posts. 563 if ( is_admin() || is_robots() || $wp_query->posts ) { 564 status_header( 200 ); 565 return; 566 } 567 568 // We will 404 for paged queries, as no posts were found. 569 if ( ! is_paged() ) { 570 571 // Don't 404 for these queries if they matched an object. 572 if ( ( is_tag() || is_category() || is_tax() || is_author() || is_post_type_archive() ) && $wp_query->get_queried_object() ) { 573 status_header( 200 ); 574 return; 575 } 576 577 // Don't 404 for these queries either. 578 if ( is_home() || is_search() ) { 579 status_header( 200 ); 580 return; 581 } 582 } 583 584 // Guess it's time to 404. 585 $wp_query->set_404(); 586 status_header( 404 ); 587 nocache_headers(); 588 } 589 590 /** 591 * Sets up all of the variables required by the WordPress environment. 592 * 593 * The action 'wp' has one parameter that references the WP object. It 594 * allows for accessing the properties and methods to further manipulate the 595 * object. 596 * 597 * @since 2.0.0 598 * 599 * @param string|array $query_args Passed to {@link parse_request()} 600 */ 601 function main($query_args = '') { 602 $this->init(); 603 $this->parse_request($query_args); 604 $this->send_headers(); 605 $this->query_posts(); 606 $this->handle_404(); 607 $this->register_globals(); 608 609 /** 610 * Fires once the WordPress environment has been set up. 611 * 612 * @since 2.1.0 613 * 614 * @param WP &$this Current WordPress environment instance (passed by reference). 615 */ 616 do_action_ref_array( 'wp', array( &$this ) ); 617 } 618 619 } 620 621 /** 622 * Helper class to remove the need to use eval to replace $matches[] in query strings. 623 * 624 * @since 2.9.0 625 */ 626 class WP_MatchesMapRegex { 627 /** 628 * store for matches 629 * 630 * @access private 631 * @var array 632 */ 633 var $_matches; 634 635 /** 636 * store for mapping result 637 * 638 * @access public 639 * @var string 640 */ 641 var $output; 642 643 /** 644 * subject to perform mapping on (query string containing $matches[] references 645 * 646 * @access private 647 * @var string 648 */ 649 var $_subject; 650 651 /** 652 * regexp pattern to match $matches[] references 653 * 654 * @var string 655 */ 656 var $_pattern = '(\$matches\[[1-9]+[0-9]*\])'; // magic number 657 658 /** 659 * constructor 660 * 661 * @param string $subject subject if regex 662 * @param array $matches data to use in map 663 * @return self 664 */ 665 function WP_MatchesMapRegex($subject, $matches) { 666 $this->_subject = $subject; 667 $this->_matches = $matches; 668 $this->output = $this->_map(); 669 } 670 671 /** 672 * Substitute substring matches in subject. 673 * 674 * static helper function to ease use 675 * 676 * @access public 677 * @param string $subject subject 678 * @param array $matches data used for substitution 679 * @return string 680 */ 681 public static function apply($subject, $matches) { 682 $oSelf = new WP_MatchesMapRegex($subject, $matches); 683 return $oSelf->output; 684 } 685 686 /** 687 * do the actual mapping 688 * 689 * @access private 690 * @return string 691 */ 692 function _map() { 693 $callback = array($this, 'callback'); 694 return preg_replace_callback($this->_pattern, $callback, $this->_subject); 695 } 696 697 /** 698 * preg_replace_callback hook 699 * 700 * @access public 701 * @param array $matches preg_replace regexp matches 702 * @return string 703 */ 704 function callback($matches) { 705 $index = intval(substr($matches[0], 9, -1)); 706 return ( isset( $this->_matches[$index] ) ? urlencode($this->_matches[$index]) : '' ); 707 } 708 709 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Tue Mar 25 01:41:18 2014 | WordPress honlapkészítés: online1.hu |