Textpattern | PHP Cross Reference | Content Management Systems |
Description: Site hostname.
1 <?php 2 3 /* 4 * Textpattern Content Management System 5 * http://textpattern.com 6 * 7 * Copyright (C) 2005 Dean Allen 8 * Copyright (C) 2016 The Textpattern Development Team 9 * 10 * This file is part of Textpattern. 11 * 12 * Textpattern is free software; you can redistribute it and/or 13 * modify it under the terms of the GNU General Public License 14 * as published by the Free Software Foundation, version 2. 15 * 16 * Textpattern is distributed in the hope that it will be useful, 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 * GNU General Public License for more details. 20 * 21 * You should have received a copy of the GNU General Public License 22 * along with Textpattern. If not, see <http://www.gnu.org/licenses/>. 23 */ 24 25 if (!defined('txpath')) { 26 define("txpath", dirname(__FILE__)); 27 } 28 29 if (!defined("txpinterface")) { 30 die('If you just updated and expect to see your site here, please also update the files in your main installation directory.'. 31 ' (Otherwise note that publish.php cannot be called directly.)'); 32 } 33 34 global $trace; 35 36 $trace->start('[PHP includes, stage 2]'); 37 include_once txpath.'/vendors/Textpattern/Loader.php'; 38 39 $loader = new \Textpattern\Loader(txpath.'/vendors'); 40 $loader->register(); 41 42 $loader = new \Textpattern\Loader(txpath.'/lib'); 43 $loader->register(); 44 45 include_once txpath.'/lib/txplib_publish.php'; 46 include_once txpath.'/lib/txplib_db.php'; 47 include_once txpath.'/lib/txplib_html.php'; 48 include_once txpath.'/lib/txplib_forms.php'; 49 include_once txpath.'/lib/admin_config.php'; 50 51 include_once txpath.'/publish/taghandlers.php'; 52 include_once txpath.'/publish/log.php'; 53 include_once txpath.'/publish/comment.php'; 54 $trace->stop(); 55 56 set_error_handler('publicErrorHandler', error_reporting()); 57 58 ob_start(); 59 60 $txp_current_tag = ''; 61 $txp_parsed = array(); 62 63 // Get all prefs as an array. 64 $prefs = get_prefs(); 65 66 // Add prefs to globals. 67 extract($prefs); 68 69 // Check the size of the URL request. 70 bombShelter(); 71 72 // Set a higher error level during initialisation. 73 set_error_level(@$production_status == 'live' ? 'testing' : @$production_status); 74 75 // disable tracing in live environment. 76 if ($production_status == 'live') { 77 Trace::setQuiet(true); 78 } 79 80 // Use the current URL path if $siteurl is unknown. 81 if (empty($siteurl)) { 82 $httphost = preg_replace('/[^-_a-zA-Z0-9.:]/', '', $_SERVER['HTTP_HOST']); 83 $prefs['siteurl'] = $siteurl = $httphost.rtrim(dirname($_SERVER['SCRIPT_NAME']), DS); 84 } 85 86 if (empty($path_to_site)) { 87 updateSitePath(dirname(dirname(__FILE__))); 88 } 89 90 if (!defined('PROTOCOL')) { 91 switch (serverSet('HTTPS')) { 92 case '': 93 case 'off': // ISAPI with IIS. 94 define('PROTOCOL', 'http://'); 95 break; 96 default: 97 define('PROTOCOL', 'https://'); 98 break; 99 } 100 } 101 102 // Definitive HTTP address of the site. 103 if (!defined('hu')) { 104 define('hu', PROTOCOL.$siteurl.'/'); 105 } 106 107 // Relative URL global. 108 if (!defined('rhu')) { 109 define('rhu', preg_replace('|^https?://[^/]+|', '', hu)); 110 } 111 112 // HTTP address of the site serving images. 113 if (!defined('ihu')) { 114 define('ihu', hu); 115 } 116 117 if (!defined('SITE_HOST')) { 118 /** 119 * Site hostname. 120 * 121 * @package Network 122 * @since 4.6.0 123 */ 124 125 define('SITE_HOST', (string) @parse_url(hu, PHP_URL_HOST)); 126 } 127 128 if (!defined('IMPATH')) { 129 /** 130 * Path to image directory. 131 * 132 * @package Image 133 */ 134 135 define('IMPATH', $path_to_site.DS.$img_dir.DS); 136 } 137 138 // 1.0: a new $here variable in the top-level index.php should let us know the 139 // server path to the live site let's save it to prefs. 140 if (isset($here) and $path_to_site != $here) { 141 updateSitePath($here); 142 } 143 144 if (!defined('LANG')) { 145 /** 146 * Currently active language. 147 * 148 * @package L10n 149 */ 150 151 define('LANG', $language); 152 } 153 154 if (!empty($locale)) { 155 setlocale(LC_ALL, $locale); 156 } 157 158 // Initialise the current user. 159 $txp_user = null; 160 161 // i18n. 162 $textarray = (txpinterface == 'css') ? array() : load_lang(LANG); 163 164 // Tidy up the site. 165 janitor(); 166 167 // Here come the plugins. 168 if ($use_plugins) { 169 load_plugins(); 170 } 171 172 // This step deprecated as of 1.0 - really only useful with old-style section 173 // placeholders, which passed $s='section_name'. 174 $s = (empty($s)) ? '' : $s; 175 176 $pretext = !isset($pretext) ? array() : $pretext; 177 $pretext = array_merge($pretext, pretext($s, $prefs)); 178 callback_event('pretext_end'); 179 extract($pretext); 180 181 // Now that everything is initialised, we can crank down error reporting. 182 set_error_level($production_status); 183 184 if (!empty($feed) && in_array($feed, array('atom', 'rss'), true)) { 185 include txpath."/publish/{$feed}.php"; 186 echo $feed(); 187 188 if ($production_status !== 'live') { 189 echo $trace->summary(); 190 191 if ($production_status === 'debug') { 192 echo $trace->result(); 193 } 194 } 195 196 exit; 197 } 198 199 if (gps('parentid')) { 200 if (ps('submit')) { 201 saveComment(); 202 } elseif (ps('preview')) { 203 checkCommentRequired(getComment()); 204 } elseif ($comments_mode == 1) { 205 // Popup comments? 206 header("Content-type: text/html; charset=utf-8"); 207 exit(parse_form('popup_comments')); 208 } 209 } 210 211 // We are dealing with a download. 212 if (@$s == 'file_download' && !empty($filename)) { 213 output_file_download($filename); 214 exit(0); 215 } 216 217 // Send 304 Not Modified if appropriate. 218 handle_lastmod(); 219 220 // Log the page view. 221 log_hit($status); 222 223 // ------------------------------------------------------------- 224 225 function preText($s, $prefs) 226 { 227 extract($prefs); 228 229 callback_event('pretext'); 230 231 // Set messy variables. 232 $out = makeOut('id', 's', 'c', 'context', 'q', 'm', 'pg', 'p', 'month', 'author'); 233 234 if (gps('rss')) { 235 $out['feed'] = 'rss'; 236 } 237 238 if (gps('atom')) { 239 $out['feed'] = 'atom'; 240 } 241 242 // Some useful vars for taghandlers, plugins. 243 $out['request_uri'] = preg_replace("|^https?://[^/]+|i", "", serverSet('REQUEST_URI')); 244 $out['qs'] = serverSet('QUERY_STRING'); 245 246 // IIS fix. 247 if (!$out['request_uri'] and serverSet('SCRIPT_NAME')) { 248 $out['request_uri'] = serverSet('SCRIPT_NAME').((serverSet('QUERY_STRING')) ? '?'.serverSet('QUERY_STRING') : ''); 249 } 250 251 // Another IIS fix. 252 if (!$out['request_uri'] and serverSet('argv')) { 253 $argv = serverSet('argv'); 254 $out['request_uri'] = @substr($argv[0], strpos($argv[0], ';') + 1); 255 } 256 257 // Define the useable url, minus any subdirectories. 258 // This is pretty ugly, if anyone wants to have a go at it. 259 $out['subpath'] = $subpath = preg_quote(preg_replace("/https?:\/\/.*(\/.*)/Ui", "$1", hu), "/"); 260 $out['req'] = $req = preg_replace("/^$subpath/i", "/", $out['request_uri']); 261 262 $is_404 = ($out['status'] == '404'); 263 264 // If messy vars exist, bypass URL parsing. 265 if (!$out['id'] && !$out['s'] && !(txpinterface == 'css') && ! (txpinterface == 'admin')) { 266 // Return clean URL test results for diagnostics. 267 if (gps('txpcleantest')) { 268 exit(show_clean_test($out)); 269 } 270 271 extract(chopUrl($req)); 272 273 // First we sniff out some of the preset URL schemes. 274 if (strlen($u1)) { 275 switch ($u1) { 276 case 'atom': 277 $out['feed'] = 'atom'; 278 break; 279 280 case 'rss': 281 $out['feed'] = 'rss'; 282 break; 283 284 // urldecode(strtolower(urlencode())) looks ugly but is the 285 // only way to make it multibyte-safe without breaking 286 // backwards-compatibility. 287 case urldecode(strtolower(urlencode(gTxt('section')))): 288 $out['s'] = (ckEx('section', $u2)) ? $u2 : ''; $is_404 = empty($out['s']); 289 break; 290 291 case urldecode(strtolower(urlencode(gTxt('category')))): 292 if ($u3) { 293 $out['context'] = validContext($u2); 294 $out['c'] = $u3; 295 } else { 296 $out['context'] = 'article'; 297 $out['c'] = $u2; 298 } 299 $out['c'] = (ckCat($out['context'], $out['c'])) ? $out['c'] : ''; 300 $is_404 = empty($out['c']); 301 break; 302 303 case urldecode(strtolower(urlencode(gTxt('author')))): 304 if ($u3) { 305 $out['context'] = validContext($u2); 306 $out['author'] = $u3; 307 } else { 308 $out['context'] = 'article'; 309 $out['author'] = $u2; 310 } 311 312 $out['author'] = (!empty($out['author'])) ? $out['author'] : ''; 313 break; 314 // AuthorID gets resolved from Name further down. 315 316 case urldecode(strtolower(urlencode(gTxt('file_download')))): 317 $out['s'] = 'file_download'; 318 $out['id'] = (!empty($u2)) ? $u2 : ''; 319 $out['filename'] = (!empty($u3)) ? $u3 : ''; 320 break; 321 322 default: 323 // Then see if the prefs-defined permlink scheme is usable. 324 switch ($permlink_mode) { 325 326 case 'section_id_title': 327 if (empty($u2)) { 328 $out['s'] = (ckEx('section', $u1)) ? $u1 : ''; 329 $is_404 = empty($out['s']); 330 } else { 331 $rs = lookupByIDSection($u2, $u1); 332 $out['s'] = @$rs['Section']; 333 $out['id'] = @$rs['ID']; 334 $is_404 = (empty($out['s']) or empty($out['id'])); 335 } 336 337 break; 338 339 case 'year_month_day_title': 340 if (empty($u2)) { 341 $out['s'] = (ckEx('section', $u1)) ? $u1 : ''; 342 $is_404 = empty($out['s']); 343 } elseif (empty($u4)) { 344 $month = "$u1-$u2"; 345 346 if (!empty($u3)) { 347 $month .= "-$u3"; 348 } 349 350 if (preg_match('/\d+-\d+(?:-\d+)?/', $month)) { 351 $out['month'] = $month; 352 $out['s'] = 'default'; 353 } else { 354 $is_404 = 1; 355 } 356 } else { 357 $when = "$u1-$u2-$u3"; 358 $rs = lookupByDateTitle($when, $u4); 359 $out['id'] = (!empty($rs['ID'])) ? $rs['ID'] : ''; 360 $out['s'] = (!empty($rs['Section'])) ? $rs['Section'] : ''; 361 $is_404 = (empty($out['s']) or empty($out['id'])); 362 } 363 364 break; 365 366 case 'section_title': 367 if (empty($u2)) { 368 $out['s'] = (ckEx('section', $u1)) ? $u1 : ''; 369 $is_404 = empty($out['s']); 370 } else { 371 $rs = lookupByTitleSection($u2, $u1); 372 $out['id'] = isset($rs['ID']) ? $rs['ID'] : ''; 373 $out['s'] = isset($rs['Section']) ? $rs['Section'] : ''; 374 $is_404 = (empty($out['s']) or empty($out['id'])); 375 } 376 377 break; 378 379 case 'title_only': 380 $rs = lookupByTitle($u1); 381 $out['id'] = @$rs['ID']; 382 $out['s'] = (empty($rs['Section']) ? ckEx('section', $u1) : 383 $rs['Section']); 384 $is_404 = empty($out['s']); 385 386 break; 387 388 case 'id_title': 389 if (is_numeric($u1) && ckExID($u1)) { 390 $rs = lookupByID($u1); 391 $out['id'] = (!empty($rs['ID'])) ? $rs['ID'] : ''; 392 $out['s'] = (!empty($rs['Section'])) ? $rs['Section'] : ''; 393 $is_404 = (empty($out['s']) or empty($out['id'])); 394 } else { 395 // We don't want to miss the /section/ pages. 396 $out['s'] = ckEx('section', $u1) ? $u1 : ''; 397 $is_404 = empty($out['s']); 398 } 399 400 break; 401 } 402 403 if (!$is_404) { 404 $out['context'] = validContext($out['context']); 405 } 406 407 break; // Prefs-defined permlink scheme case. 408 } 409 } else { 410 $out['s'] = 'default'; 411 $out['context'] = validContext($out['context']); 412 } 413 } else { 414 // Messy mode, but prevent to get the id for file_downloads. 415 $out['context'] = validContext($out['context']); 416 417 if ($out['context'] == 'article' && $out['id'] && $out['s'] != 'file_download') { 418 $rs = lookupByID($out['id']); 419 $out['id'] = (!empty($rs['ID'])) ? $rs['ID'] : ''; 420 $out['s'] = (!empty($rs['Section'])) ? $rs['Section'] : ''; 421 $is_404 = (empty($out['s']) or empty($out['id'])); 422 } 423 } 424 425 // Existing category in messy or clean URL? 426 if (!empty($out['c'])) { 427 if (!ckCat($out['context'], $out['c'])) { 428 $is_404 = true; 429 $out['c'] = ''; 430 } 431 } 432 433 // Resolve AuthorID from Authorname. 434 if ($out['author']) { 435 $name = urldecode(strtolower(urlencode($out['author']))); 436 437 $name = safe_field('name', 'txp_users', "RealName LIKE '".doSlash($out['author'])."'"); 438 439 if ($name) { 440 $out['author'] = $name; 441 } else { 442 $out['author'] = ''; 443 $is_404 = true; 444 } 445 } 446 447 // Allow article preview. 448 if (gps('txpreview')) { 449 doAuth(); 450 451 if (!has_privs('article.preview')) { 452 txp_status_header('401 Unauthorized'); 453 exit(hed('401 Unauthorized', 1).graf(gTxt('restricted_area'))); 454 } 455 456 global $nolog; 457 458 $nolog = true; 459 $rs = safe_row("ID AS id, Section AS s", 'textpattern', "ID = ".intval(gps('txpreview'))." LIMIT 1"); 460 461 if ($rs) { 462 $is_404 = false; 463 $out = array_merge($out, $rs); 464 } 465 } 466 467 // Stats: found or not. 468 $out['status'] = ($is_404 ? '404' : '200'); 469 470 $out['pg'] = is_numeric($out['pg']) ? intval($out['pg']) : ''; 471 $out['id'] = is_numeric($out['id']) ? intval($out['id']) : ''; 472 473 if ($out['s'] == 'file_download') { 474 if (is_numeric($out['id'])) { 475 // Undo the double-encoding workaround for .gz files; 476 // @see filedownloadurl(). 477 if (!empty($out['filename'])) { 478 $out['filename'] = preg_replace('/gz&$/i', 'gz', $out['filename']); 479 } 480 481 $fn = empty($out['filename']) ? '' : " AND filename = '".doSlash($out['filename'])."'"; 482 $rs = safe_row('*', 'txp_file', "id = ".intval($out['id'])." AND status = ".STATUS_LIVE." AND created <= ".now('created').$fn); 483 } 484 485 return (!empty($rs)) ? array_merge($out, $rs) : array('s' => 'file_download', 'file_error' => 404); 486 } 487 488 if (!$is_404) { 489 $out['s'] = (empty($out['s'])) ? 'default' : $out['s']; 490 } 491 $s = $out['s']; 492 $id = $out['id']; 493 494 // Hackish. 495 global $is_article_list; 496 if (empty($id)) { 497 $is_article_list = true; 498 } 499 500 // By this point we should know the section, so grab its page and CSS. 501 if (txpinterface != 'css') { 502 $rs = safe_row("page, css", "txp_section", "name = '".doSlash($s)."' LIMIT 1"); 503 $out['page'] = isset($rs['page']) ? $rs['page'] : ''; 504 $out['css'] = isset($rs['css']) ? $rs['css'] : ''; 505 } 506 507 if (is_numeric($id) and !$is_404) { 508 $a = safe_row("*, UNIX_TIMESTAMP(Posted) AS uPosted, UNIX_TIMESTAMP(Expires) AS uExpires, UNIX_TIMESTAMP(LastMod) AS uLastMod", 509 'textpattern', 510 "ID = ".intval($id).(gps('txpreview') ? '' : " AND Status IN (".STATUS_LIVE.",".STATUS_STICKY.")")); 511 512 if ($a) { 513 $out['id_keywords'] = $a['Keywords']; 514 $out['id_author'] = $a['AuthorID']; 515 populateArticleData($a); 516 517 $uExpires = $a['uExpires']; 518 519 if ($uExpires and time() > $uExpires and !$publish_expired_articles) { 520 $out['status'] = '410'; 521 } 522 } 523 } 524 525 // These are deprecated as of Textpattern v1.0 - leaving them here for 526 // plugin compatibility. 527 $out['path_from_root'] = rhu; 528 $out['pfr'] = rhu; 529 530 $out['path_to_site'] = $path_to_site; 531 $out['permlink_mode'] = $permlink_mode; 532 $out['sitename'] = $sitename; 533 534 return $out; 535 } 536 537 // textpattern() is the function that assembles a page, based on 538 // the variables passed to it by pretext(); 539 540 // ------------------------------------------------------------- 541 542 function textpattern() 543 { 544 global $pretext, $prefs, $production_status, $siteurl, $has_article_tag; 545 546 $has_article_tag = false; 547 548 callback_event('textpattern'); 549 550 if ($pretext['status'] == '404') { 551 txp_die(gTxt('404_not_found'), '404'); 552 } 553 554 if ($pretext['status'] == '410') { 555 txp_die(gTxt('410_gone'), '410'); 556 } 557 558 // Useful for clean URLs with error-handlers. 559 txp_status_header('200 OK'); 560 561 set_error_handler('tagErrorHandler'); 562 $html = parse_page($pretext['page']); 563 564 if ($html === false) { 565 txp_die(gTxt('unknown_section'), '404'); 566 } 567 568 // Make sure the page has an article tag if necessary. 569 if (!$has_article_tag and $production_status != 'live' and $pretext['context'] == 'article' and (!empty($pretext['id']) or !empty($pretext['c']) or !empty($pretext['q']) or !empty($pretext['pg']))) { 570 trigger_error(gTxt('missing_article_tag', array('{page}' => $pretext['page']))); 571 } 572 573 restore_error_handler(); 574 575 header("Content-type: text/html; charset=utf-8"); 576 echo $html; 577 578 callback_event('textpattern_end'); 579 } 580 581 // ------------------------------------------------------------- 582 function output_css($s = '', $n = '') 583 { 584 $order = ''; 585 586 if ($n) { 587 if (!is_scalar($n)) { 588 txp_die('Not Found', 404); 589 } 590 591 $n = do_list_unique($n); 592 $cssname = join("','", doSlash($n)); 593 594 if (count($n) > 1) { 595 $order = " ORDER BY FIELD(name, '$cssname')"; 596 } 597 } elseif ($s) { 598 if (!is_scalar($s)) { 599 txp_die('Not Found', 404); 600 } 601 602 $cssname = safe_field('css', 'txp_section', "name = '".doSlash($s)."'"); 603 } 604 605 if (!empty($cssname)) { 606 $css = join(n, safe_column_num('css', 'txp_css', "name IN ('$cssname')".$order)); 607 echo $css; 608 } 609 } 610 611 // ------------------------------------------------------------- 612 function output_file_download($filename) 613 { 614 global $file_error, $file_base_path, $pretext; 615 616 callback_event('file_download'); 617 618 if (!isset($file_error)) { 619 $filename = sanitizeForFile($filename); 620 $fullpath = build_file_path($file_base_path, $filename); 621 622 if (is_file($fullpath)) { 623 // Discard any error PHP messages. 624 ob_clean(); 625 $filesize = filesize($fullpath); 626 $sent = 0; 627 header('Content-Description: File Download'); 628 header('Content-Type: application/octet-stream'); 629 header('Content-Disposition: attachment; filename="'.$filename.'"; size = "'.$filesize.'"'); 630 631 // Fix for IE6 PDF bug on servers configured to send cache headers. 632 header('Cache-Control: private'); 633 @ini_set("zlib.output_compression", "Off"); 634 @set_time_limit(0); 635 @ignore_user_abort(true); 636 637 if ($file = fopen($fullpath, 'rb')) { 638 while (!feof($file) and (connection_status() == 0)) { 639 echo fread($file, 1024 * 64); 640 $sent += (1024 * 64); 641 ob_flush(); 642 flush(); 643 } 644 645 fclose($file); 646 647 // Record download. 648 if ((connection_status() == 0) and !connection_aborted()) { 649 safe_update('txp_file', "downloads = downloads + 1", "id = ".intval($pretext['id'])); 650 } else { 651 $pretext['request_uri'] .= ($sent >= $filesize) 652 ? '#aborted' 653 : "#aborted-at-".floor($sent * 100 / $filesize)."%"; 654 } 655 656 log_hit('200'); 657 } 658 } else { 659 $file_error = 404; 660 } 661 } 662 663 // Deal with error. 664 if (isset($file_error)) { 665 switch ($file_error) { 666 case 403: 667 txp_die(gTxt('403_forbidden'), '403'); 668 break; 669 case 404: 670 txp_die(gTxt('404_not_found'), '404'); 671 break; 672 default: 673 txp_die(gTxt('500_internal_server_error'), '500'); 674 break; 675 } 676 } 677 } 678 679 // article() is called when parse() finds a <txp:article /> tag. 680 // If an $id has been established, we output a single article, 681 // otherwise, output a list. 682 683 // ------------------------------------------------------------- 684 function article($atts, $thing = null) 685 { 686 global $is_article_body, $has_article_tag; 687 688 if ($is_article_body) { 689 trigger_error(gTxt('article_tag_illegal_body')); 690 691 return ''; 692 } 693 694 $has_article_tag = true; 695 696 return parseArticles($atts, '0', $thing); 697 } 698 699 // ------------------------------------------------------------- 700 701 function doArticles($atts, $iscustom, $thing = null) 702 { 703 global $pretext, $prefs; 704 extract($pretext); 705 extract($prefs); 706 $customFields = getCustomFields(); 707 $customlAtts = array_null(array_flip($customFields)); 708 709 if ($iscustom) { 710 711 // Custom articles must not render search results. 712 $q = ''; 713 714 $extralAtts = array( 715 'category' => '', 716 'section' => '', 717 'excerpted' => '', 718 'author' => '', 719 'month' => '', 720 'expired' => $publish_expired_articles, 721 'id' => '', 722 'exclude' => '', 723 ); 724 } else { 725 $extralAtts = array( 726 'listform' => '', 727 'searchform' => '', 728 'searchall' => 1, 729 'searchsticky' => 0, 730 'pageby' => '', 731 'pgonly' => 0, 732 ); 733 } 734 735 // Getting attributes. 736 $theAtts = lAtts(array( 737 'form' => 'default', 738 'limit' => 10, 739 'sort' => '', 740 'sortby' => '', // Deprecated in 4.0.4. 741 'sortdir' => '', // Deprecated in 4.0.4. 742 'keywords' => '', 743 'time' => 'past', 744 'status' => STATUS_LIVE, 745 'allowoverride' => !$iscustom, 746 'frontpage' => !$iscustom, 747 'match' => 'Category1,Category2', 748 'offset' => 0, 749 'wraptag' => '', 750 'break' => '', 751 'label' => '', 752 'labeltag' => '', 753 'class' => '', 754 ) + $customlAtts + $extralAtts, $atts); 755 756 // For the txp:article tag, some attributes are taken from globals; 757 // override them, then stash all filter attributes. 758 if (!$iscustom) { 759 $theAtts['category'] = ($c) ? $c : ''; 760 $theAtts['section'] = ($s && $s != 'default') ? $s : ''; 761 $theAtts['author'] = (!empty($author) ? $author : ''); 762 $theAtts['month'] = (!empty($month) ? $month : ''); 763 $theAtts['frontpage'] = ($theAtts['frontpage'] && $s && $s == 'default'); 764 $theAtts['excerpted'] = 0; 765 $theAtts['exclude'] = 0; 766 $theAtts['expired'] = $publish_expired_articles; 767 768 filterAtts($theAtts); 769 } 770 771 extract($theAtts); 772 773 // If a listform is specified, $thing is for doArticle() - hence ignore here. 774 if (!empty($listform)) { 775 $thing = ''; 776 } 777 778 $pageby = (empty($pageby) ? $limit : $pageby); 779 780 // Treat sticky articles differently wrt search filtering, etc. 781 $status = in_array(strtolower($status), array('sticky', STATUS_STICKY)) ? STATUS_STICKY : STATUS_LIVE; 782 $issticky = ($status == STATUS_STICKY); 783 784 // Give control to search, if necessary. 785 if ($q && !$issticky) { 786 include_once txpath.'/publish/search.php'; 787 788 $s_filter = ($searchall ? filterSearch() : ''); 789 $q = trim($q); 790 $quoted = ($q[0] === '"') && ($q[strlen($q) - 1] === '"'); 791 $q = doSlash($quoted ? trim(trim($q, '"')) : $q); 792 793 // Searchable article fields are limited to the columns of the 794 // textpattern table and a matching fulltext index must exist. 795 $cols = do_list_unique($searchable_article_fields); 796 797 if (empty($cols) or $cols[0] == '') { 798 $cols = array('Title', 'Body'); 799 } 800 801 $score = ", MATCH (`".join("`, `", $cols)."`) AGAINST ('$q') AS score"; 802 $search_terms = preg_replace('/\s+/', ' ', str_replace(array('\\', '%', '_', '\''), array('\\\\', '\\%', '\\_', '\\\''), $q)); 803 804 if ($quoted || empty($m) || $m === 'exact') { 805 for ($i = 0; $i < count($cols); $i++) { 806 $cols[$i] = "`$cols[$i]` LIKE '%$search_terms%'"; 807 } 808 } else { 809 $colJoin = ($m === 'any') ? "OR" : "AND"; 810 $search_terms = explode(' ', $search_terms); 811 for ($i = 0; $i < count($cols); $i++) { 812 $like = array(); 813 foreach ($search_terms as $search_term) { 814 $like[] = "`$cols[$i]` LIKE '%$search_term%'"; 815 } 816 $cols[$i] = "(".join(" $colJoin ", $like).")"; 817 } 818 } 819 820 $cols = join(" OR ", $cols); 821 $search = " AND ($cols) $s_filter"; 822 823 // searchall=0 can be used to show search results for the current 824 // section only. 825 if ($searchall) { 826 $section = ''; 827 } 828 829 if (!$sort) { 830 $sort = "score DESC"; 831 } 832 } else { 833 $score = $search = ''; 834 835 if (!$sort) { 836 $sort = "Posted DESC"; 837 } 838 } 839 840 // For backwards compatibility. sortby and sortdir are deprecated. 841 if ($sortby) { 842 trigger_error(gTxt('deprecated_attribute', array('{name}' => 'sortby')), E_USER_NOTICE); 843 844 if (!$sortdir) { 845 $sortdir = "DESC"; 846 } else { 847 trigger_error(gTxt('deprecated_attribute', array('{name}' => 'sortdir')), E_USER_NOTICE); 848 } 849 850 $sort = "$sortby $sortdir"; 851 } elseif ($sortdir) { 852 trigger_error(gTxt('deprecated_attribute', array('{name}' => 'sortdir')), E_USER_NOTICE); 853 $sort = "Posted $sortdir"; 854 } 855 856 // Building query parts. 857 $frontpage = ($frontpage and (!$q or $issticky)) ? filterFrontPage() : ''; 858 $category = join("','", doSlash(do_list_unique($category))); 859 $categories = array(); 860 $match = do_list_unique($match); 861 862 if (in_array('Category1', $match)) { 863 $categories[] = "Category1 IN ('$category')"; 864 } 865 866 if (in_array('Category2', $match)) { 867 $categories[] = "Category2 IN ('$category')"; 868 } 869 870 $categories = join(" OR ", $categories); 871 $category = (!$category or !$categories) ? '' : " AND ($categories)"; 872 $section = (!$section) ? '' : " AND Section IN ('".join("','", doSlash(do_list_unique($section)))."')"; 873 $excerpted = (!$excerpted) ? '' : " AND Excerpt !=''"; 874 $author = (!$author) ? '' : " AND AuthorID IN ('".join("','", doSlash(do_list_unique($author)))."')"; 875 $month = (!$month) ? '' : " AND Posted LIKE '".doSlash($month)."%'"; 876 $ids = $id ? array_map('intval', do_list_unique($id)) : array(); 877 $exclude = $exclude ? array_map('intval', do_list_unique($exclude)) : array(); 878 $id = ((!$id) ? '' : " AND ID IN (".join(',', $ids).")") 879 .((!$exclude) ? '' : " AND ID NOT IN (".join(',', $exclude).")"); 880 881 switch ($time) { 882 case 'any': 883 $time = ""; 884 break; 885 case 'future': 886 $time = " AND Posted > ".now('posted'); 887 break; 888 default: 889 $time = " AND Posted <= ".now('posted'); 890 } 891 892 if (!$expired) { 893 $time .= " AND (".now('expires')." <= Expires OR Expires IS NULL)"; 894 } 895 896 $custom = ''; 897 898 if ($customFields) { 899 foreach ($customFields as $cField) { 900 if (isset($atts[$cField])) { 901 $customPairs[$cField] = $atts[$cField]; 902 } 903 } 904 905 if (!empty($customPairs)) { 906 $custom = buildCustomSql($customFields, $customPairs); 907 } 908 } 909 910 // Allow keywords for no-custom articles. That tagging mode, you know. 911 if ($keywords) { 912 $keys = doSlash(do_list_unique($keywords)); 913 914 foreach ($keys as $key) { 915 $keyparts[] = "FIND_IN_SET('".$key."', Keywords)"; 916 } 917 918 $keywords = " AND (".join(' or ', $keyparts).")"; 919 } 920 921 if ($q && $searchsticky) { 922 $statusq = " AND Status >= ".STATUS_LIVE; 923 } elseif ($id) { 924 $statusq = " AND Status >= ".STATUS_LIVE; 925 } else { 926 $statusq = " AND Status = ".intval($status); 927 } 928 929 $where = "1 = 1".$statusq.$time. 930 $search.$id.$category.$section.$excerpted.$month.$author.$keywords.$custom.$frontpage; 931 932 // Do not paginate if we are on a custom list. 933 if (!$iscustom and !$issticky) { 934 $grand_total = safe_count('textpattern', $where); 935 $total = $grand_total - $offset; 936 $numPages = ceil($total / $pageby); 937 $pg = (!$pg) ? 1 : $pg; 938 $pgoffset = $offset + (($pg - 1) * $pageby); 939 940 // Send paging info to txp:newer and txp:older. 941 $pageout['pg'] = $pg; 942 $pageout['numPages'] = $numPages; 943 $pageout['s'] = $s; 944 $pageout['c'] = $c; 945 $pageout['context'] = 'article'; 946 $pageout['grand_total'] = $grand_total; 947 $pageout['total'] = $total; 948 949 global $thispage; 950 951 if (empty($thispage)) { 952 $thispage = $pageout; 953 } 954 955 if ($pgonly) { 956 return; 957 } 958 } else { 959 $pgoffset = $offset; 960 } 961 962 // Preserve order of custom article ids unless 'sort' attribute is set. 963 if (!empty($atts['id']) && empty($atts['sort'])) { 964 $safe_sort = "FIELD(id, ".join(',', $ids).")"; 965 } else { 966 $safe_sort = doSlash($sort); 967 } 968 969 $rs = safe_rows_start("*, UNIX_TIMESTAMP(Posted) AS uPosted, UNIX_TIMESTAMP(Expires) AS uExpires, UNIX_TIMESTAMP(LastMod) AS uLastMod".$score, 970 'textpattern', 971 "$where ORDER BY $safe_sort LIMIT ".intval($pgoffset).", ".intval($limit)); 972 973 // Get the form name. 974 if ($q && !$issticky) { 975 $fname = ($searchform ? $searchform : 'search_results'); 976 } else { 977 $fname = (!empty($listform) ? $listform : $form); 978 } 979 980 if ($rs) { 981 $count = 0; 982 $last = numRows($rs); 983 984 $articles = array(); 985 986 while ($a = nextRow($rs)) { 987 ++$count; 988 populateArticleData($a); 989 global $thisarticle, $uPosted, $limit; 990 $thisarticle['is_first'] = ($count == 1); 991 $thisarticle['is_last'] = ($count == $last); 992 993 // Article form preview. 994 if (txpinterface === 'admin' && ps('Form')) { 995 doAuth(); 996 997 if (!has_privs('form')) { 998 txp_status_header('401 Unauthorized'); 999 exit(hed('401 Unauthorized', 1).graf(gTxt('restricted_area'))); 1000 } 1001 1002 $articles[] = parse(gps('Form')); 1003 } elseif ($allowoverride and $a['override_form']) { 1004 $articles[] = parse_form($a['override_form']); 1005 } else { 1006 $articles[] = ($thing) ? parse($thing) : parse_form($fname); 1007 } 1008 1009 // Sending these to paging_link(); Required? 1010 $uPosted = $a['uPosted']; 1011 1012 unset($GLOBALS['thisarticle']); 1013 } 1014 1015 return doLabel($label, $labeltag).doWrap($articles, $wraptag, $break, $class); 1016 } 1017 } 1018 1019 // ------------------------------------------------------------- 1020 1021 function doArticle($atts, $thing = null) 1022 { 1023 global $pretext, $prefs, $thisarticle; 1024 extract($prefs); 1025 extract($pretext); 1026 1027 extract(gpsa(array( 1028 'parentid', 1029 'preview', 1030 ))); 1031 1032 $theAtts = lAtts(array( 1033 'allowoverride' => '1', 1034 'form' => 'default', 1035 'status' => STATUS_LIVE, 1036 'pgonly' => 0, 1037 ), $atts, 0); 1038 extract($theAtts); 1039 1040 // Save *all* atts to get hold of the current article filter criteria. 1041 filterAtts($atts); 1042 1043 // No output required. 1044 if ($pgonly) { 1045 return ''; 1046 } 1047 1048 // If a form is specified, $thing is for doArticles() - hence ignore 1049 // $thing here. 1050 if (!empty($atts['form'])) { 1051 $thing = ''; 1052 } 1053 1054 if ($status) { 1055 $status = in_array(strtolower($status), array('sticky', STATUS_STICKY)) ? STATUS_STICKY : STATUS_LIVE; 1056 } 1057 1058 if (empty($thisarticle) or $thisarticle['thisid'] != $id) { 1059 $id = assert_int($id); 1060 $thisarticle = null; 1061 1062 $q_status = ($status ? "AND Status = ".intval($status) : "AND Status IN (".STATUS_LIVE.",".STATUS_STICKY.")"); 1063 1064 $rs = safe_row("*, UNIX_TIMESTAMP(Posted) AS uPosted, UNIX_TIMESTAMP(Expires) AS uExpires, UNIX_TIMESTAMP(LastMod) AS uLastMod", 1065 'textpattern', 1066 "ID = $id $q_status LIMIT 1"); 1067 1068 if ($rs) { 1069 extract($rs); 1070 populateArticleData($rs); 1071 } 1072 } 1073 1074 if (!empty($thisarticle) and ($thisarticle['status'] == $status or gps('txpreview'))) { 1075 extract($thisarticle); 1076 $thisarticle['is_first'] = 1; 1077 $thisarticle['is_last'] = 1; 1078 1079 if ($allowoverride and $override_form) { 1080 $article = parse_form($override_form); 1081 } else { 1082 $article = ($thing) ? parse($thing) : parse_form($form); 1083 } 1084 1085 if ($use_comments and $comments_auto_append) { 1086 $article .= parse_form('comments_display'); 1087 } 1088 1089 unset($GLOBALS['thisarticle']); 1090 1091 return $article; 1092 } 1093 } 1094 1095 // ------------------------------------------------------------- 1096 1097 function article_custom($atts, $thing = null) 1098 { 1099 return parseArticles($atts, '1', $thing); 1100 } 1101 1102 // ------------------------------------------------------------- 1103 1104 function parseArticles($atts, $iscustom = 0, $thing = null) 1105 { 1106 global $pretext, $is_article_list; 1107 $old_ial = $is_article_list; 1108 $is_article_list = empty($pretext['id']) || $iscustom; 1109 article_push(); 1110 $r = ($is_article_list) ? doArticles($atts, $iscustom, $thing) : doArticle($atts, $thing); 1111 article_pop(); 1112 $is_article_list = $old_ial; 1113 1114 return $r; 1115 } 1116 1117 // ------------------------------------------------------------- 1118 1119 function makeOut() 1120 { 1121 $array['status'] = '200'; 1122 1123 foreach (func_get_args() as $a) { 1124 $in = gps($a); 1125 1126 if (is_scalar($in)) { 1127 $array[$a] = strval($in); 1128 } else { 1129 $array[$a] = ''; 1130 $array['status'] = '404'; 1131 } 1132 } 1133 1134 return $array; 1135 } 1136 1137 // ------------------------------------------------------------- 1138 1139 function validContext($context) 1140 { 1141 foreach (array('article', 'image', 'file', 'link') as $type) { 1142 $valid[gTxt($type.'_context')] = $type; 1143 } 1144 1145 return isset($valid[$context]) ? $valid[$context] : 'article'; 1146 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
title