Textpattern | PHP Cross Reference | Content Management Systems |
Description: Collection of tag functions.
1 <?php 2 3 /* 4 * Textpattern Content Management System 5 * https://textpattern.com/ 6 * 7 * Copyright (C) 2020 The Textpattern Development Team 8 * 9 * This file is part of Textpattern. 10 * 11 * Textpattern is free software; you can redistribute it and/or 12 * modify it under the terms of the GNU General Public License 13 * as published by the Free Software Foundation, version 2. 14 * 15 * Textpattern is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with Textpattern. If not, see <https://www.gnu.org/licenses/>. 22 */ 23 24 /** 25 * Collection of tag functions. 26 * 27 * @package Tag 28 */ 29 30 Txp::get('\Textpattern\Tag\Registry') 31 ->register('page_title') 32 ->register('css') 33 ->register('image') 34 ->register('thumbnail') 35 ->register('output_form') 36 ->register('txp_yield', 'yield') 37 ->register('txp_if_yield', 'if_yield') 38 ->register('feed_link') 39 ->register('link_feed_link') 40 ->register('linklist') 41 ->register('tpt_link', 'link') 42 ->register('linkdesctitle') 43 ->register('link_name') 44 ->register('link_url') 45 ->register('link_author') 46 ->register('link_description') 47 ->register('link_date') 48 ->register('link_category') 49 ->register('link_id') 50 ->register('if_first', 'if_first_link', 'link') 51 ->register('if_last', 'if_last_link', 'link') 52 ->register('email') 53 ->register('password_protect') 54 ->register('recent_articles') 55 ->register('recent_comments') 56 ->register('related_articles') 57 ->register('popup') 58 ->register('category_list') 59 ->register('section_list') 60 ->register('search_input') 61 ->register('search_term') 62 ->register('link_to', 'link_to_next') 63 ->register('link_to', 'link_to_prev', 'prev') 64 ->register('next_title') 65 ->register('prev_title') 66 ->register('site_name') 67 ->register('site_slogan') 68 ->register('link_to_home') 69 ->register('txp_pager', 'newer', true) 70 ->register('txp_pager', 'older', false) 71 ->register('txp_pager', 'pages') 72 ->register('text') 73 ->register('article_id') 74 ->register('article_url_title') 75 ->register('if_article_id') 76 ->register('posted') 77 ->register('posted', 'modified', 'modified') 78 ->register('posted', 'expires', 'expires') 79 ->register('if_expires') 80 ->register('if_expired') 81 ->register('comments_count') 82 ->register('comments_invite') 83 ->register('comments_form') 84 ->register('comments_error') 85 ->register('if_comments_error') 86 ->register('comments') 87 ->register('comments_preview') 88 ->register('if_comments_preview') 89 ->register('comment_permlink') 90 ->register('comment_id') 91 ->register('comment_name') 92 ->register('comment_email') 93 ->register('comment_web') 94 ->register('comment_time') 95 ->register('comment_message') 96 ->register('comment_anchor') 97 ->register(array('\Textpattern\Tag\Syntax\Authors', 'renderAuthors'), 'authors') 98 ->register('author') 99 ->register('author_email') 100 ->register('if_author') 101 ->register('if_article_author') 102 ->register('body') 103 ->register('title') 104 ->register('excerpt') 105 ->register('article_category', 'category1') 106 ->register('article_category', array('category2', array('number' => 2))) 107 ->register('category') 108 ->register('section') 109 ->register('keywords') 110 ->register('if_keywords') 111 ->register('if_description') 112 ->register('if_article_image') 113 ->register('article_image') 114 ->register('search_result_title') 115 ->register('search_result_excerpt') 116 ->register('search_result_url') 117 ->register('search_result_date') 118 ->register('search_result_count') 119 ->register('image_index') 120 ->register('image_display') 121 ->register('images') 122 ->register('image_info') 123 ->register('image_url') 124 ->register('image_author') 125 ->register('image_date') 126 ->register('if_first', 'if_first_image', 'image') 127 ->register('if_last', 'if_last_image', 'image') 128 ->register('if_thumbnail') 129 ->register('if_comments') 130 ->register('if_comments_allowed') 131 ->register('if_comments_disallowed') 132 ->register('if_individual_article') 133 ->register('if_article_list') 134 ->register('meta_keywords') 135 ->register('meta_description') 136 ->register('meta_author') 137 ->register('permlink') 138 ->register('lang') 139 ->register('breadcrumb') 140 ->register('if_excerpt') 141 ->register('if_search') 142 ->register('if_search_results') 143 ->register('if_category') 144 ->register('if_article_category') 145 ->register('if_first', 'if_first_category', 'category') 146 ->register('if_last', 'if_last_category', 'category') 147 ->register('if_section') 148 ->register('if_article_section') 149 ->register('if_first', 'if_first_section', 'section') 150 ->register('if_last', 'if_last_section', 'section') 151 ->register('if_logged_in') 152 ->register('if_request') 153 ->register('php') 154 ->register('txp_header', 'header') 155 ->register('custom_field') 156 ->register('if_custom_field') 157 ->register('site_url') 158 ->register('error_message') 159 ->register('error_status') 160 ->register('if_status') 161 ->register('page_url') 162 ->register('if_different') 163 ->register('if_first', 'if_first_article') 164 ->register('if_last', 'if_last_article') 165 ->register('if_plugin') 166 ->register('file_download_list') 167 ->register('file_download') 168 ->register('file_download_link') 169 ->register('file_download_size') 170 ->register('file_download_time', 'file_download_created') 171 ->register('file_download_time', 'file_download_modified', 'modified') 172 ->register('file_download_id') 173 ->register('file_download_name') 174 ->register('file_download_category') 175 ->register('file_download_author') 176 ->register('file_download_downloads') 177 ->register('file_download_description') 178 ->register('if_first', 'if_first_file', 'file') 179 ->register('if_last', 'if_last_file', 'file') 180 ->register('hide') 181 ->register('rsd') 182 ->register('variable') 183 ->register('if_variable') 184 ->register('article') 185 ->register('article_custom') 186 ->register('txp_die') 187 ->register('txp_eval', 'evaluate') 188 ->register('comments_help') 189 ->register('comment_input', 'comment_name_input') 190 ->register('comment_input', 'comment_email_input', 'email', 'clean_url') 191 ->register('comment_input', array('comment_web_input', array('placeholder' => 'http(s)://')), 'web', 'clean_url') 192 ->register('comment_message_input') 193 ->register('comment_remember') 194 ->register('comment_preview') 195 ->register('comment_submit') 196 // Global attributes (false just removes unknown attribute warning) 197 ->registerAttr(false, 'class, html_id, labeltag') 198 ->registerAttr(true, 'not, txp-process, breakby, breakclass, wrapform, evaluate') 199 ->registerAttr('txp_escape', 'escape') 200 ->registerAttr('txp_wraptag', 'wraptag, label, trim, replace, default'); 201 202 // ------------------------------------------------------------- 203 204 function page_title($atts) 205 { 206 global $parentid, $thisarticle, $q, $c, $author, $context, $s, $pg, $sitename; 207 208 extract(lAtts(array('separator' => ' | '), $atts)); 209 210 $appending = txpspecialchars($separator.$sitename); 211 $parent_id = (int) $parentid; 212 $pageStr = ($pg ? $separator.gTxt('page').' '.$pg : ''); 213 214 if ($parent_id) { 215 $out = gTxt('comments_on').' '.escape_title(safe_field("Title", 'textpattern', "ID = $parent_id")).$appending; 216 } elseif (isset($thisarticle['title'])) { 217 $out = escape_title($thisarticle['title']).$appending; 218 } elseif ($q) { 219 $out = gTxt('search_results').' '.gTxt('txt_quote_double_open').txpspecialchars($q).gTxt('txt_quote_double_close').$pageStr.$appending; 220 } elseif ($c) { 221 $out = txpspecialchars(fetch_category_title($c, $context)).$pageStr.$appending; 222 } elseif ($s && $s != 'default') { 223 $out = txpspecialchars(fetch_section_title($s)).$pageStr.$appending; 224 } elseif ($author) { 225 $out = txpspecialchars(get_author_name($author)).$pageStr.$appending; 226 } elseif ($pg) { 227 $out = gTxt('page').' '.$pg.$appending; 228 } else { 229 $out = txpspecialchars($sitename); 230 } 231 232 return $out; 233 } 234 235 // ------------------------------------------------------------- 236 237 function css($atts) 238 { 239 global $css, $doctype, $pretext; 240 241 extract(lAtts(array( 242 'format' => 'url', 243 'media' => 'screen', 244 'name' => $css, 245 'rel' => 'stylesheet', 246 'theme' => $pretext['skin'], 247 'title' => '', 248 ), $atts)); 249 250 if (empty($name)) { 251 $name = 'default'; 252 } 253 254 $out = ''; 255 $format = strtolower(preg_replace('/\s+/', '', $format)); 256 list($mode, $format) = explode('.', $format.'.'.$format); 257 258 if (has_handler('css.url')) { 259 $url = callback_event('css.url', '', false, compact('name', 'theme')); 260 } elseif ($mode === 'flat') { 261 $url = array(); 262 $skin_dir = urlencode(get_pref('skin_dir')); 263 264 foreach (do_list_unique($name) as $n) { 265 $url[] = hu.$skin_dir.'/'.urlencode($theme).'/'.Txp::get('Textpattern\Skin\Css')->getDir().'/'.urlencode($n).'.css'; 266 } 267 } else { 268 $url = hu.'css.php?n='.urlencode($name).'&t='.urlencode($theme); 269 } 270 271 switch ($format) { 272 case 'link': 273 foreach ((array)$url as $href) { 274 $out .= tag_void('link', array( 275 'rel' => $rel, 276 'type' => $doctype != 'html5' ? 'text/css' : '', 277 'media' => $media, 278 'title' => $title, 279 'href' => $href, 280 )).n; 281 } 282 break; 283 default: 284 $out .= txpspecialchars(is_array($url) ? implode(',', $url) : $url); 285 break; 286 } 287 288 return $out; 289 } 290 291 // ------------------------------------------------------------- 292 293 function component($atts) 294 { 295 global $doctype, $pretext, $txp_context; 296 static $mimetypes = null, $dir = null, 297 $internals = array('id', 's', 'c', 'context', 'q', 'm', 'pg', 'p', 'month', 'author'), 298 $defaults = array( 299 'format' => 'url', 300 'form' => '', 301 'context' => null, 302 'rel' => '', 303 'title' => '', 304 ); 305 306 if (!isset($mimetypes)) { 307 $mimetypes = Txp::get('Textpattern\Skin\Form')->getMimeTypes(); 308 $dir = urlencode(Txp::get('Textpattern\Skin\Form')->getDir()); 309 } 310 311 extract(lAtts($defaults, $atts, false)); 312 313 if (empty($form)) { 314 return; 315 } 316 317 $format = strtolower(preg_replace('/\s+/', '', $format)); 318 list($mode, $format) = explode('.', $format.'.'.$format); 319 $theme = urlencode($pretext['skin']); 320 $out = ''; 321 $qs = get_context($context, $internals) + array_diff_key($atts, $defaults); 322 323 if ($mode === 'flat') { 324 $url = array(); 325 $skin_dir = urlencode(get_pref('skin_dir')); 326 327 foreach (do_list_unique($form) as $n) { 328 $type = pathinfo($n, PATHINFO_EXTENSION); 329 if (isset($mimetypes[$type])) { 330 $url[] = hu.$skin_dir.'/'.$theme.'/'.$dir.'/'.urlencode($type).'/'.urlencode($n).($qs ? join_qs($qs) : ''); 331 } else { 332 $url[] = pagelinkurl(array('f' => $n) + $qs); 333 } 334 } 335 } else { 336 $url = pagelinkurl(array('f' => $form) + $qs); 337 } 338 339 switch ($format) { 340 case 'url': 341 case 'flat': 342 $out .= is_array($url) ? implode(',', $url) : $url; 343 break; 344 case 'link': 345 foreach ((array)$url as $href) { 346 $out .= tag_void('link', array( 347 'rel' => $rel, 348 'title' => $title, 349 'href' => $href, 350 )).n; 351 } 352 break; 353 case 'script': 354 foreach ((array)$url as $href) { 355 $out .= tag(null, 'script', array( 356 'title' => $title, 357 'type' => $doctype != 'html5' ? 'application/javascript' : '', 358 'src' => $href, 359 )).n; 360 } 361 break; 362 case 'image': 363 foreach ((array)$url as $href) { 364 $out .= tag_void('img', array( 365 'title' => $title, 366 'src' => $href, 367 )).n; 368 } 369 break; 370 default: 371 foreach ((array)$url as $href) { 372 $out .= href($title ? $title : $href, $href, array( 373 'rel' => $rel, 374 )).n; 375 } 376 } 377 378 return $out; 379 } 380 381 // ------------------------------------------------------------- 382 383 function image($atts) 384 { 385 return thumbnail(array('thumbnail' => isset($atts['thumbnail']) ? $atts['thumbnail'] : false) + $atts); 386 } 387 388 // ------------------------------------------------------------- 389 390 function thumbnail($atts) 391 { 392 global $doctype; 393 394 extract(lAtts(array( 395 'escape' => true, 396 'title' => '', 397 'class' => '', 398 'html_id' => '', 399 'height' => '', 400 'id' => '', 401 'link' => 0, 402 'link_rel' => '', 403 'loading' => null, 404 'name' => '', 405 'poplink' => 0, // Deprecated, 4.7 406 'style' => '', 407 'wraptag' => '', 408 'width' => '', 409 'thumbnail' => null, 410 ), $atts)); 411 412 if (isset($atts['poplink'])) { 413 trigger_error(gTxt('deprecated_attribute', array('{name}' => 'poplink')), E_USER_NOTICE); 414 } 415 416 if ($imageData = imageFetchInfo($id, $name)) { 417 $thumb_ = $thumbnail || !isset($thumbnail) ? 'thumb_' : ''; 418 419 if ($thumb_ && empty($imageData['thumbnail'])) { 420 $thumb_ = ''; 421 422 if (!isset($thumbnail)) { 423 return; 424 } 425 } 426 427 extract($imageData); 428 429 if ($escape) { 430 $alt = txp_escape(array('escape' => $escape), $alt); 431 } 432 433 if ($title === true) { 434 $title = $caption; 435 } 436 437 if ($width == '' && ($thumb_ && $thumb_w || !$thumb_ && $w)) { 438 $width = ${$thumb_.'w'}; 439 } 440 441 if ($height == '' && ($thumb_ && $thumb_h || !$thumb_ && $h)) { 442 $height = ${$thumb_.'h'}; 443 } 444 445 $out = '<img src="'.imagesrcurl($id, $ext, !empty($thumb_)). 446 '" alt="'.txpspecialchars($alt, ENT_QUOTES, 'UTF-8', false).'"'; 447 448 if ($title) { 449 $out .= ' title="'.txpspecialchars($title, ENT_QUOTES, 'UTF-8', false).'"'; 450 } 451 452 if ($html_id && !$wraptag) { 453 $out .= ' id="'.txpspecialchars($html_id).'"'; 454 } 455 456 if ($class && !$wraptag) { 457 $out .= ' class="'.txpspecialchars($class).'"'; 458 } 459 460 if ($style) { 461 $out .= ' style="'.txpspecialchars($style).'"'; 462 } 463 464 if ($width) { 465 $out .= ' width="'.(int) $width.'"'; 466 } 467 468 if ($height) { 469 $out .= ' height="'.(int) $height.'"'; 470 } 471 472 if ($loading && $doctype == 'html5' && in_array($loading, array('auto', 'eager', 'lazy'))) { 473 $out .= ' loading="'.$loading.'"'; 474 } 475 476 $out .= ' />'; 477 478 if ($link && $thumb_) { 479 $attribs = ''; 480 481 if (!empty($link_rel)) { 482 $attribs .= " rel='".txpspecialchars($link_rel)."'"; 483 } 484 485 $out = href($out, imagesrcurl($id, $ext), $attribs); 486 } elseif ($poplink) { 487 $out = '<a href="'.imagesrcurl($id, $ext).'"'. 488 ' onclick="window.open(this.href, \'popupwindow\', '. 489 '\'width='.$w.', height='.$h.', scrollbars, resizable\'); return false;">'.$out.'</a>'; 490 } 491 492 return $wraptag ? doTag($out, $wraptag, $class, '', $html_id) : $out; 493 } 494 } 495 496 // ------------------------------------------------------------- 497 498 function output_form($atts, $thing = null) 499 { 500 global $txp_atts, $yield, $txp_yield; 501 502 if (empty($atts['form'])) { 503 trigger_error(gTxt('form_not_specified')); 504 505 return ''; 506 } 507 508 $form = $atts['form']; 509 $to_yield = isset($atts['yield']) ? $atts['yield'] : false; 510 unset($atts['form'], $atts['yield'], $txp_atts['form'], $txp_atts['yield']); 511 512 if ($form === true && empty($atts)) { 513 return fetch_form(do_list_unique($to_yield)); 514 } 515 516 if (!empty($to_yield)) { 517 $to_yield = $to_yield === true ? $atts : array_fill_keys(do_list_unique($to_yield), null); 518 empty($txp_atts) or $txp_atts = array_diff_key($txp_atts, $to_yield); 519 } 520 521 if (isset($atts['format'])) {// component 522 empty($txp_atts) or $atts = array_diff_key($atts, $txp_atts); 523 524 return component($atts + array('form' => $form)); 525 } elseif (is_array($to_yield)) { 526 $atts = lAtts($to_yield, $atts) or $atts = array(); 527 } 528 529 foreach ($atts as $name => $value) { 530 if (!isset($txp_yield[$name])) { 531 $txp_yield[$name] = array(); 532 } 533 534 $txp_yield[$name][] = array($value, false); 535 } 536 537 $yield[] = $thing; 538 $out = parse_form($form); 539 array_pop($yield); 540 541 foreach ($atts as $name => $value) { 542 $result = array_pop($txp_yield[$name]); 543 544 if (!empty($result[1])) { 545 unset($txp_atts[$name]); 546 } 547 } 548 549 return $out; 550 } 551 552 // ------------------------------------------------------------- 553 554 function txp_yield($atts, $thing = null) 555 { 556 global $yield, $txp_yield, $txp_atts, $txp_item; 557 558 extract(lAtts(array( 559 'name' => '', 560 'else' => false, 561 'default' => false, 562 'item' => null 563 ), $atts)); 564 565 if (isset($item)) { 566 $inner = isset($txp_item[$item]) ? $txp_item[$item] : null; 567 } elseif ($name === '') { 568 $end = empty($yield) ? null : end($yield); 569 570 if (isset($end)) { 571 $inner = parse($end, empty($else)); 572 } 573 } elseif (!empty($txp_yield[$name])) { 574 list($inner) = end($txp_yield[$name]); 575 $txp_yield[$name][key($txp_yield[$name])][1] = true; 576 } 577 578 if (!isset($inner)) { 579 $escape = isset($txp_atts['escape']) ? $txp_atts['escape'] : null; 580 $inner = $default !== false ? 581 ($default === true ? page_url(array('type' => $name, 'escape' => $escape)) : $default) : 582 ($thing ? parse($thing) : $thing); 583 } 584 585 return $inner; 586 } 587 588 589 function txp_if_yield($atts, $thing = null) 590 { 591 global $yield, $txp_yield, $txp_item; 592 593 extract(lAtts(array( 594 'name' => '', 595 'else' => false, 596 'value' => null, 597 'item' => null 598 ), $atts)); 599 600 if (isset($item)) { 601 $inner = isset($txp_item[$item]) ? $txp_item[$item] : null; 602 } elseif ($name === '') { 603 $end = empty($yield) ? null : end($yield); 604 605 if (isset($end)) { 606 $inner = $value === null ? ($else ? getIfElse($end, false) : true) : parse($end, empty($else)); 607 } 608 } elseif (empty($txp_yield[$name])) { 609 $inner = null; 610 } else { 611 list($inner) = end($txp_yield[$name]); 612 } 613 614 return parse($thing, isset($inner) && ($value === null || (string)$inner === (string)$value || $inner && $value === true)); 615 } 616 617 // ------------------------------------------------------------- 618 619 function feed_link($atts, $thing = null) 620 { 621 global $s, $c; 622 623 extract(lAtts(array( 624 'category' => $c, 625 'flavor' => 'rss', 626 'format' => 'a', 627 'label' => '', 628 'limit' => '', 629 'section' => ($s == 'default' ? '' : $s), 630 'title' => gTxt('rss_feed_title'), 631 ), $atts)); 632 633 $url = pagelinkurl(array( 634 $flavor => '1', 635 'section' => $section, 636 'category' => $category, 637 'limit' => $limit, 638 )); 639 640 if ($flavor == 'atom') { 641 $title = ($title == gTxt('rss_feed_title')) ? gTxt('atom_feed_title') : $title; 642 } 643 644 $title = txpspecialchars($title); 645 646 $type = ($flavor == 'atom') ? 'application/atom+xml' : 'application/rss+xml'; 647 648 if ($format == 'link') { 649 return '<link rel="alternate" type="'.$type.'" title="'.$title.'" href="'.$url.'" />'; 650 } 651 652 $txt = ($thing === null ? $label : parse($thing)); 653 654 $out = href($txt, $url, array( 655 'type' => $type, 656 'title' => $title, 657 )); 658 659 return $out; 660 } 661 662 // ------------------------------------------------------------- 663 664 function link_feed_link($atts) 665 { 666 global $c; 667 668 extract(lAtts(array( 669 'category' => $c, 670 'flavor' => 'rss', 671 'format' => 'a', 672 'label' => '', 673 'title' => gTxt('rss_feed_title'), 674 'wraptag' => '', 675 'class' => __FUNCTION__, 676 ), $atts)); 677 678 $url = pagelinkurl(array( 679 $flavor => '1', 680 'area' => 'link', 681 'category' => $category, 682 )); 683 684 if ($flavor == 'atom') { 685 $title = ($title == gTxt('rss_feed_title')) ? gTxt('atom_feed_title') : $title; 686 } 687 688 $title = txpspecialchars($title); 689 690 $type = ($flavor == 'atom') ? 'application/atom+xml' : 'application/rss+xml'; 691 692 if ($format == 'link') { 693 return '<link rel="alternate" type="'.$type.'" title="'.$title.'" href="'.$url.'" />'; 694 } 695 696 $out = href($label, $url, array( 697 'type' => $type, 698 'title' => $title, 699 )); 700 701 return ($wraptag) ? doTag($out, $wraptag, $class) : $out; 702 } 703 704 // ------------------------------------------------------------- 705 706 function linklist($atts, $thing = null) 707 { 708 global $s, $c, $context, $thislink, $thispage, $pretext; 709 710 extract(lAtts(array( 711 'break' => '', 712 'category' => '', 713 'author' => '', 714 'realname' => '', 715 'auto_detect' => 'category, author', 716 'class' => __FUNCTION__, 717 'form' => 'plainlinks', 718 'id' => '', 719 'pageby' => '', 720 'limit' => 0, 721 'offset' => 0, 722 'month' => '', 723 'time' => null, 724 'sort' => 'linksort asc', 725 'wraptag' => '', 726 ), $atts)); 727 728 $where = array(); 729 $filters = isset($atts['category']) || isset($atts['author']) || isset($atts['realname']); 730 $context_list = (empty($auto_detect) || $filters) ? array() : do_list_unique($auto_detect); 731 $pageby = ($pageby == 'limit') ? $limit : $pageby; 732 733 if ($category) { 734 $where[] = "category IN ('".join("','", doSlash(do_list_unique($category)))."')"; 735 } 736 737 if ($id) { 738 $where[] = "id IN ('".join("','", doSlash(do_list_unique($id, array(',', '-'))))."')"; 739 } 740 741 if ($author) { 742 $where[] = "author IN ('".join("','", doSlash(do_list_unique($author)))."')"; 743 } 744 745 if ($realname) { 746 $authorlist = safe_column("name", 'txp_users', "RealName IN ('".join("','", doArray(doSlash(do_list_unique($realname)), 'urldecode'))."')"); 747 if ($authorlist) { 748 $where[] = "author IN ('".join("','", doSlash($authorlist))."')"; 749 } 750 } 751 752 // If no links are selected, try... 753 if (!$where && !$filters) { 754 foreach ($context_list as $ctxt) { 755 switch ($ctxt) { 756 case 'category': 757 // ...the global category in the URL. 758 if ($context == 'link' && !empty($c)) { 759 $where[] = "category = '".doSlash($c)."'"; 760 } 761 break; 762 case 'author': 763 // ...the global author in the URL. 764 if ($context == 'link' && !empty($pretext['author'])) { 765 $where[] = "author = '".doSlash($pretext['author'])."'"; 766 } 767 break; 768 } 769 770 // Only one context can be processed. 771 if ($where) { 772 break; 773 } 774 } 775 } 776 777 if (!$where && $filters) { 778 // If nothing matches, output nothing. 779 return ''; 780 } 781 782 if ($time === null || $time || $month) { 783 $where[] = buildTimeSql($month, $time === null ? 'past' : $time, 'date'); 784 } 785 786 if (!$where) { 787 // If nothing matches, start with all links. 788 $where[] = "1 = 1"; 789 } 790 791 $where = join(" AND ", $where); 792 793 // Set up paging if required. 794 if ($limit && $pageby) { 795 $pg = (!$pretext['pg']) ? 1 : $pretext['pg']; 796 $pgoffset = $offset + (($pg - 1) * $pageby); 797 798 if (empty($thispage)) { 799 $grand_total = safe_count('txp_link', $where); 800 $total = $grand_total - $offset; 801 $numPages = ($pageby > 0) ? ceil($total/$pageby) : 1; 802 803 // Send paging info to txp:newer and txp:older. 804 $pageout['pg'] = $pg; 805 $pageout['numPages'] = $numPages; 806 $pageout['s'] = $s; 807 $pageout['c'] = $c; 808 $pageout['context'] = 'link'; 809 $pageout['grand_total'] = $grand_total; 810 $pageout['total'] = $total; 811 $thispage = $pageout; 812 } 813 } else { 814 $pgoffset = $offset; 815 } 816 817 $qparts = array( 818 $where, 819 'ORDER BY '.sanitizeForSort($sort), 820 ($limit) ? 'LIMIT '.intval($pgoffset).', '.intval($limit) : '', 821 ); 822 823 $rs = safe_rows_start("*, UNIX_TIMESTAMP(date) AS uDate", 'txp_link', join(' ', $qparts)); 824 825 if ($rs) { 826 $count = 0; 827 $last = numRows($rs); 828 $out = array(); 829 830 while ($a = nextRow($rs)) { 831 ++$count; 832 $thislink = $a; 833 $thislink['date'] = $thislink['uDate']; 834 $thislink['is_first'] = ($count == 1); 835 $thislink['is_last'] = ($count == $last); 836 unset($thislink['uDate']); 837 838 $out[] = ($thing) ? parse($thing) : parse_form($form); 839 840 $thislink = ''; 841 } 842 843 if ($out) { 844 return doWrap($out, $wraptag, $break, $class); 845 } 846 } 847 848 return ''; 849 } 850 851 // ------------------------------------------------------------- 852 853 // NOTE: tpt_ prefix used because link() is a PHP function. See publish.php. 854 function tpt_link($atts) 855 { 856 global $thislink; 857 858 extract(lAtts(array( 859 'rel' => '', 860 'id' => '', 861 'name' => '', 862 'escape' => true, 863 ), $atts)); 864 865 $rs = $thislink; 866 $sql = array(); 867 868 if ($id) { 869 $sql[] = "id = ".intval($id); 870 } elseif ($name) { 871 $sql[] = "linkname = '".doSlash($name)."'"; 872 } 873 874 if ($sql) { 875 $rs = safe_row("linkname, url", 'txp_link', implode(" AND ", $sql)." LIMIT 1"); 876 } 877 878 if (!$rs) { 879 trigger_error(gTxt('unknown_link')); 880 881 return ''; 882 } 883 884 return tag( 885 $escape ? txp_escape(array('escape' => $escape), $rs['linkname']) : $rs['linkname'], 'a', 886 ($rel ? ' rel="'.txpspecialchars($rel).'"' : ''). 887 ' href="'.txpspecialchars($rs['url']).'"' 888 ); 889 } 890 891 // ------------------------------------------------------------- 892 893 function linkdesctitle($atts) 894 { 895 global $thislink; 896 897 assert_link(); 898 899 extract(lAtts(array('rel' => '', 'escape' => true), $atts)); 900 901 $description = ($thislink['description']) 902 ? ' title="'.txpspecialchars($thislink['description']).'"' 903 : ''; 904 905 return tag( 906 $escape ? txp_escape(array('escape' => $escape), $thislink['linkname']) : $thislink['linkname'], 'a', 907 ($rel ? ' rel="'.txpspecialchars($rel).'"' : ''). 908 ' href="'.doSpecial($thislink['url']).'"'.$description 909 ); 910 } 911 912 // ------------------------------------------------------------- 913 914 function link_name($atts) 915 { 916 global $thislink; 917 918 assert_link(); 919 920 extract(lAtts(array('escape' => null), $atts)); 921 922 return ($escape === null) 923 ? txpspecialchars($thislink['linkname']) 924 : $thislink['linkname']; 925 } 926 927 // ------------------------------------------------------------- 928 929 function link_url() 930 { 931 global $thislink; 932 933 assert_link(); 934 935 return doSpecial($thislink['url']); 936 } 937 938 // ------------------------------------------------------------- 939 940 function link_author($atts) 941 { 942 global $thislink, $s; 943 944 assert_link(); 945 946 extract(lAtts(array( 947 'link' => 0, 948 'title' => 1, 949 'section' => '', 950 'this_section' => '', 951 ), $atts)); 952 953 if ($thislink['author']) { 954 $author_name = get_author_name($thislink['author']); 955 $display_name = txpspecialchars(($title) ? $author_name : $thislink['author']); 956 957 $section = ($this_section) ? ($s == 'default' ? '' : $s) : $section; 958 959 $author = ($link) 960 ? href($display_name, pagelinkurl(array( 961 's' => $section, 962 'author' => $author_name, 963 'context' => 'link', 964 ))) 965 : $display_name; 966 967 return $author; 968 } 969 } 970 971 // ------------------------------------------------------------- 972 973 function link_description($atts) 974 { 975 global $thislink; 976 977 assert_link(); 978 979 extract(lAtts(array('escape' => null), $atts)); 980 981 if ($thislink['description']) { 982 return ($escape === null) ? 983 txpspecialchars($thislink['description']) : 984 $thislink['description']; 985 } 986 } 987 988 // ------------------------------------------------------------- 989 990 function link_date($atts) 991 { 992 global $thislink, $dateformat; 993 994 assert_link(); 995 996 extract(lAtts(array( 997 'format' => $dateformat, 998 'gmt' => '', 999 'lang' => '', 1000 ), $atts)); 1001 1002 return safe_strftime($format, $thislink['date'], $gmt, $lang); 1003 } 1004 1005 // ------------------------------------------------------------- 1006 1007 function link_category($atts) 1008 { 1009 global $thislink; 1010 1011 assert_link(); 1012 1013 extract(lAtts(array('title' => 0), $atts)); 1014 1015 if ($thislink['category']) { 1016 $category = ($title) 1017 ? fetch_category_title($thislink['category'], 'link') 1018 : $thislink['category']; 1019 1020 return $category; 1021 } 1022 } 1023 1024 // ------------------------------------------------------------- 1025 1026 function link_id() 1027 { 1028 global $thislink; 1029 1030 assert_link(); 1031 1032 return $thislink['id']; 1033 } 1034 1035 // ------------------------------------------------------------- 1036 1037 function email($atts, $thing = null) 1038 { 1039 extract(lAtts(array( 1040 'email' => '', 1041 'linktext' => gTxt('contact'), 1042 'title' => '', 1043 ), $atts)); 1044 1045 if ($email) { 1046 if ($thing !== null) { 1047 $linktext = parse($thing); 1048 } 1049 1050 // Obfuscate link text? 1051 if (is_valid_email($linktext)) { 1052 $linktext = Txp::get('\Textpattern\Mail\Encode')->entityObfuscateAddress($linktext); 1053 } 1054 1055 return href( 1056 $linktext, 1057 Txp::get('\Textpattern\Mail\Encode')->entityObfuscateAddress('mailto:'.$email), 1058 ($title ? ' title="'.txpspecialchars($title).'"' : '') 1059 ); 1060 } 1061 1062 return ''; 1063 } 1064 1065 // ------------------------------------------------------------- 1066 1067 function password_protect($atts, $thing = null) 1068 { 1069 ob_start(); 1070 1071 extract(lAtts(array( 1072 'login' => null, 1073 'pass' => null, 1074 'privs' => null, 1075 ), $atts)); 1076 1077 if ($pass === null) { 1078 $access = ($user = is_logged_in($login)) !== false && ($privs === null || in_list($user['privs'], $privs)); 1079 } else { 1080 $au = serverSet('PHP_AUTH_USER'); 1081 $ap = serverSet('PHP_AUTH_PW'); 1082 1083 // For PHP as (f)cgi, two rules in htaccess often allow this workaround. 1084 $ru = serverSet('REDIRECT_REMOTE_USER'); 1085 1086 if (!$au && !$ap && strpos($ru, 'Basic') === 0) { 1087 list($au, $ap) = explode(':', base64_decode(substr($ru, 6))); 1088 } 1089 1090 $access = $au === $login && $ap === $pass; 1091 } 1092 1093 if ($access === false && $pass !== null) { 1094 header('WWW-Authenticate: Basic realm="Private"'); 1095 } 1096 1097 if ($thing === null) { 1098 if ($access === false) { 1099 txp_die(gTxt('auth_required'), '401'); 1100 } 1101 1102 return ''; 1103 } 1104 1105 return parse($thing, $access); 1106 } 1107 1108 // ------------------------------------------------------------- 1109 1110 function recent_articles($atts, $thing = null) 1111 { 1112 global $prefs; 1113 1114 $atts = lAtts(array( 1115 'break' => 'br', 1116 'category' => '', 1117 'class' => __FUNCTION__, 1118 'form' => '', 1119 'label' => gTxt('recent_articles'), 1120 'labeltag' => '', 1121 'limit' => 10, 1122 'offset' => 0, 1123 'section' => '', 1124 'sort' => 'Posted DESC', 1125 'wraptag' => '', 1126 'no_widow' => '', 1127 ), $atts); 1128 1129 if (!isset($thing) && !$atts['form']) { 1130 $thing = '<txp:permlink><txp:title no_widow="'.($atts['no_widow'] ? '1' : '').'" /></txp:permlink>'; 1131 } 1132 1133 unset($atts['no_widow']); 1134 1135 return article_custom($atts, $thing); 1136 } 1137 1138 // ------------------------------------------------------------- 1139 1140 function recent_comments($atts, $thing = null) 1141 { 1142 global $prefs; 1143 global $thisarticle, $thiscomment; 1144 1145 extract(lAtts(array( 1146 'break' => br, 1147 'class' => __FUNCTION__, 1148 'form' => '', 1149 'limit' => 10, 1150 'offset' => 0, 1151 'sort' => 'posted DESC', 1152 'wraptag' => '', 1153 ), $atts)); 1154 1155 $sort = preg_replace('/\bposted\b/', 'd.posted', $sort); 1156 $expired = ($prefs['publish_expired_articles']) ? '' : " AND (".now('expires')." <= t.Expires OR t.Expires IS NULL) "; 1157 1158 $rs = startRows("SELECT d.name, d.email, d.web, d.message, d.discussid, UNIX_TIMESTAMP(d.Posted) AS time, t.ID AS thisid, 1159 UNIX_TIMESTAMP(t.Posted) AS posted, t.Title AS title, t.Section AS section, t.Category1, t.Category2, t.url_title 1160 FROM ".safe_pfx('txp_discuss')." AS d INNER JOIN ".safe_pfx('textpattern')." AS t ON d.parentid = t.ID 1161 WHERE t.Status >= ".STATUS_LIVE.$expired." AND d.visible = ".VISIBLE." 1162 ORDER BY ".sanitizeForSort($sort)." 1163 LIMIT ".intval($offset).", ".intval($limit)); 1164 1165 if ($rs) { 1166 $out = array(); 1167 $old_article = $thisarticle; 1168 1169 while ($c = nextRow($rs)) { 1170 if ($form === '' && $thing === null) { 1171 $out[] = href( 1172 txpspecialchars($c['name']).' ('.escape_title($c['title']).')', 1173 permlinkurl($c).'#c'.$c['discussid'] 1174 ); 1175 } else { 1176 $thiscomment['name'] = $c['name']; 1177 $thiscomment['email'] = $c['email']; 1178 $thiscomment['web'] = $c['web']; 1179 $thiscomment['message'] = $c['message']; 1180 $thiscomment['discussid'] = $c['discussid']; 1181 $thiscomment['time'] = $c['time']; 1182 1183 // Allow permlink guesstimation in permlinkurl(), elsewhere. 1184 $thisarticle['thisid'] = $c['thisid']; 1185 $thisarticle['posted'] = $c['posted']; 1186 $thisarticle['title'] = $c['title']; 1187 $thisarticle['section'] = $c['section']; 1188 $thisarticle['url_title'] = $c['url_title']; 1189 1190 if ($thing === null && $form !== '') { 1191 $out[] = parse_form($form); 1192 } else { 1193 $out[] = parse($thing); 1194 } 1195 } 1196 } 1197 1198 if ($out) { 1199 unset($GLOBALS['thiscomment']); 1200 $thisarticle = $old_article; 1201 1202 return doWrap($out, $wraptag, $break, $class); 1203 } 1204 } 1205 1206 return ''; 1207 } 1208 1209 // ------------------------------------------------------------- 1210 1211 function related_articles($atts, $thing = null) 1212 { 1213 global $thisarticle, $prefs; 1214 1215 assert_article(); 1216 1217 $atts = lAtts(array( 1218 'break' => br, 1219 'class' => __FUNCTION__, 1220 'form' => '', 1221 'limit' => 10, 1222 'offset' => 0, 1223 'match' => 'Category', 1224 'no_widow' => '', 1225 'section' => '', 1226 'sort' => 'Posted DESC', 1227 'wraptag' => '', 1228 ), $atts); 1229 1230 $match = array_intersect(do_list_unique(strtolower($atts['match'])), array_merge(array('category', 'category1', 'category2', 'author', 'keywords'), getCustomFields())); 1231 $categories = $cats = array(); 1232 1233 foreach ($match as $cf) { 1234 switch ($cf) { 1235 case 'category': 1236 case 'category1': 1237 case 'category2': 1238 foreach(($cf == 'category' ? array('category1', 'category2') : array($cf)) as $cat) { 1239 if (!empty($thisarticle[$cat])) { 1240 $cats[] = $thisarticle[$cat]; 1241 } 1242 } 1243 1244 $categories[] = ucwords($cf); 1245 break; 1246 case 'author': 1247 $atts['author'] = $thisarticle['authorid']; 1248 break; 1249 default: 1250 if (empty($thisarticle[$cf])) { 1251 return ''; 1252 } 1253 1254 $atts[$cf] = $thisarticle[$cf]; 1255 break; 1256 } 1257 } 1258 1259 if (!empty($cats)) { 1260 $atts['category'] = implode(',', $cats); 1261 } elseif ($categories) { 1262 return ''; 1263 } 1264 1265 $atts['match'] = implode(',', $categories); 1266 $atts['exclude'] = $thisarticle['thisid']; 1267 1268 if ($atts['form'] === '' && $thing === null) { 1269 $thing = '<txp:permlink><txp:title no_widow="'.($atts['no_widow'] ? '1' : '').'" /></txp:permlink>'; 1270 } 1271 1272 unset($atts['no_widow']); 1273 1274 return article_custom($atts, $thing); 1275 } 1276 1277 // ------------------------------------------------------------- 1278 1279 function popup($atts) 1280 { 1281 global $s, $c, $permlink_mode; 1282 1283 extract(lAtts(array( 1284 'label' => gTxt('browse'), 1285 'wraptag' => '', 1286 'class' => '', 1287 'section' => '', 1288 'this_section' => 0, 1289 'type' => 'category', 1290 ), $atts)); 1291 1292 $type = substr($type, 0, 1); 1293 1294 if ($type == 's') { 1295 $rs = safe_rows_start("name, title", 'txp_section', "name != 'default' ORDER BY name"); 1296 } else { 1297 $rs = safe_rows_start("name, title", 'txp_category', "type = 'article' AND name != 'root' ORDER BY name"); 1298 } 1299 1300 if ($rs) { 1301 $out = array(); 1302 1303 $current = ($type == 's') ? $s : $c; 1304 1305 $sel = ''; 1306 $selected = false; 1307 1308 while ($a = nextRow($rs)) { 1309 extract($a); 1310 1311 if ($name == $current) { 1312 $sel = ' selected="selected"'; 1313 $selected = true; 1314 } 1315 1316 $out[] = '<option value="'.$name.'"'.$sel.'>'.txpspecialchars($title).'</option>'; 1317 1318 $sel = ''; 1319 } 1320 1321 if ($out) { 1322 $section = ($this_section) ? ($s == 'default' ? '' : $s) : $section; 1323 1324 $out = n.'<select name="'.txpspecialchars($type).'" onchange="submit(this.form);">'. 1325 n.t.'<option value=""'.($selected ? '' : ' selected="selected"').'> </option>'. 1326 n.t.join(n.t, $out). 1327 n.'</select>'; 1328 1329 if ($label) { 1330 $out = $label.br.$out; 1331 } 1332 1333 if ($wraptag) { 1334 $out = doTag($out, $wraptag, $class); 1335 } 1336 1337 if (($type == 's' || $permlink_mode == 'messy')) { 1338 $action = hu; 1339 $his = ($section !== '') ? n.hInput('s', $section) : ''; 1340 } else { 1341 // Clean URLs for category popup. 1342 $action = pagelinkurl(array('s' => $section)); 1343 $his = ''; 1344 } 1345 1346 return '<form method="get" action="'.$action.'">'. 1347 '<div>'. 1348 $his. 1349 n.$out. 1350 n.'<noscript><div><input type="submit" value="'.gTxt('go').'" /></div></noscript>'. 1351 n.'</div>'. 1352 n.'</form>'; 1353 } 1354 } 1355 } 1356 1357 // ------------------------------------------------------------- 1358 1359 // Output href list of site categories. 1360 function category_list($atts, $thing = null) 1361 { 1362 global $s, $c, $thiscategory; 1363 static $cache = array(), $level = 0; 1364 1365 extract(lAtts(array( 1366 'active_class' => '', 1367 'break' => br, 1368 'categories' => '', 1369 'class' => __FUNCTION__, 1370 'exclude' => '', 1371 'form' => '', 1372 'html_id' => '', 1373 'label' => '', 1374 'labeltag' => '', 1375 'parent' => '', 1376 'section' => '', 1377 'children' => 1, 1378 'sort' => empty($atts['categories']) ? 'name ASC' : '', 1379 'this_section' => 0, 1380 'type' => 'article', 1381 'wraptag' => '', 1382 'limit' => '', 1383 'offset' => '', 1384 ), $atts)); 1385 1386 $categories = $categories === true ? 1387 array(isset($thiscategory['name']) ? $thiscategory['name'] : ($c ? $c : 'root')) : 1388 do_list_unique($categories); 1389 1390 if (isset($atts['categories']) && empty($categories)) { 1391 return ''; 1392 } 1393 1394 $roots = ($parent === true ? array(isset($thiscategory['name']) ? $thiscategory['name'] : ($c ? $c : 'root')) : do_list_unique($parent)) or $roots = $categories or $roots = array('root'); 1395 $level++; 1396 $section = ($this_section) ? ($s == 'default' ? '' : $s) : $section; 1397 $multiple = count($roots) > 1; 1398 $root = implode(',', $roots); 1399 $children = $children === true ? PHP_INT_MAX : intval(is_numeric($children) ? $children : !empty($children)); 1400 $sql_query = "type = '".doSlash($type)."'".($sort ? ' order by '.sanitizeForSort($sort) : ($categories ? " order by FIELD(name, ".implode(',', quote_list($categories)).")": '')); 1401 $sql_limit = $limit !== '' || $offset ? "LIMIT ".intval($offset).", ".($limit === '' || $limit === true ? PHP_INT_MAX : intval($limit)) : ''; 1402 $exclude = $exclude ? ($exclude === true ? $roots : do_list_unique($exclude)) : array(); 1403 $sql_exclude = $exclude && $sql_limit ? " and name not in(".implode(',', quote_list($exclude)).")" : ''; 1404 $nocache = !$children || $sql_limit || $children == $level; 1405 $hash = md5($nocache ? uniqid() : $sql_query); 1406 1407 if (!isset($cache[$hash])) { 1408 $cache[$hash] = array(); 1409 } 1410 1411 if (!isset($cache[$hash][$root]) || !$multiple && $root != 'root' && empty($cache[$hash][$root][$root])) { 1412 $cache[$hash][$root] = array(); 1413 1414 if (!$children || !in_array('root', $roots)) { 1415 $cats = safe_rows('name, parent, title, description, lft, rgt', 'txp_category', "name IN (".implode(',', quote_list($roots)).") and $sql_query") or $cats = array(); 1416 $retrieve = false; 1417 $between = array(); 1418 1419 foreach ($cats as $cat) { 1420 extract($cat); 1421 $name = doSlash($name); 1422 $between[] = $children ? "lft>=$lft and rgt<=$rgt" : "name='$name' or parent='$name'"; 1423 1424 if ($rgt - $lft > 1) { 1425 $retrieve = true; 1426 } 1427 } 1428 1429 $cats = $retrieve ? safe_rows('name, parent, title, description', 'txp_category', "name!='root' $sql_exclude and (".implode(' or ', $between).") and $sql_query $sql_limit") : $cats; 1430 } else { 1431 $cats = safe_rows('name, parent, title, description', 'txp_category', "name !='root' $sql_exclude and $sql_query $sql_limit"); 1432 } 1433 1434 foreach ($cats as $cat) { 1435 extract($cat); 1436 $node = $children == $level ? $root : $name; 1437 1438 if (!isset($cache[$hash][$node])) { 1439 $cache[$hash][$node] = array(); 1440 } 1441 1442 $cache[$hash][$node][$name] = $cat; 1443 1444 if ($children != $level) { 1445 if ($multiple && in_array($name, $roots)) { 1446 $cache[$hash][$root][$name] = $cat; 1447 } 1448 1449 if (!isset($cache[$hash][$parent])) { 1450 $cache[$hash][$parent] = array(); 1451 } 1452 1453 $cache[$hash][$parent][$name] = $cat; 1454 1455 if ($multiple && in_array($parent, $roots)) { 1456 $cache[$hash][$root][$name] = $cat; 1457 } 1458 } 1459 } 1460 } 1461 1462 $oldcategory = isset($thiscategory) ? $thiscategory : null; 1463 $out = array(); 1464 $count = 0; 1465 $last = count($cache[$hash][$root]); 1466 1467 foreach ($cache[$hash][$root] as $name => $thiscategory) { 1468 if (!in_array($name, $exclude) && (!$categories || in_array($name, $categories))) { 1469 $count++; 1470 1471 if (!isset($thing) && !$form) { 1472 extract($thiscategory); 1473 $out[] = tag(txpspecialchars($title), 'a', 1474 (($active_class && (0 == strcasecmp($c, $name))) ? ' class="'.txpspecialchars($active_class).'"' : ''). 1475 ' href="'.pagelinkurl(array( 1476 's' => $section, 1477 'c' => $name, 1478 'context' => $type, 1479 )).'"' 1480 ).( 1481 isset($cache[$hash][$name]) && $children > $level && count($cache[$hash][$name]) > 1 1482 ? category_list(array( 1483 'parent' => $name, 1484 'exclude' => ($exclude ? implode(',', $exclude).',' : '').$name, 1485 'label' => '', 1486 'html_id' => '', 1487 ) + $atts) 1488 : '' 1489 ); 1490 } else { 1491 $thiscategory['type'] = $type; 1492 $thiscategory['is_first'] = ($count == 1); 1493 $thiscategory['is_last'] = ($count == $last); 1494 1495 if (isset($atts['section'])) { 1496 $thiscategory['section'] = $section; 1497 } 1498 1499 $out[] = $form ? parse_form($form) : parse($thing); 1500 } 1501 } else { 1502 $last--; 1503 } 1504 } 1505 1506 $thiscategory = $oldcategory; 1507 $level--; 1508 1509 if ($nocache || $level <= 0) { 1510 unset($cache[$hash]); 1511 } 1512 1513 return $out ? ($label ? doLabel($label, $labeltag) : '').doWrap($out, $wraptag, compact('break', 'class', 'html_id')) : ''; 1514 } 1515 1516 // ------------------------------------------------------------- 1517 1518 // Output href list of site sections. 1519 function section_list($atts, $thing = null) 1520 { 1521 global $sitename, $s, $thissection; 1522 1523 extract(lAtts(array( 1524 'active_class' => '', 1525 'break' => br, 1526 'class' => __FUNCTION__, 1527 'default_title' => $sitename, 1528 'exclude' => '', 1529 'form' => '', 1530 'html_id' => '', 1531 'include_default' => '', 1532 'sections' => '', 1533 'sort' => '', 1534 'wraptag' => '', 1535 'offset' => '', 1536 'limit' => '', 1537 ), $atts)); 1538 1539 $sql_limit = ''; 1540 $sql_sort = sanitizeForSort($sort); 1541 $sql = array(); 1542 1543 if ($limit !== '' || $offset) { 1544 $sql_limit = " LIMIT ".intval($offset).", ".($limit === '' ? PHP_INT_MAX : intval($limit)); 1545 } 1546 1547 1548 if ($sections === true) { 1549 $sql[] = '1'; 1550 } elseif ($sections) { 1551 if ($include_default) { 1552 $sections .= ', default'; 1553 } 1554 1555 $sections = join(',', quote_list(do_list_unique($sections))); 1556 $sql[] = "name IN ($sections)"; 1557 1558 if (!$sql_sort) { 1559 $sql_sort = "FIELD(name, $sections)"; 1560 } 1561 } else { 1562 $sql[] = '1'.filterFrontPage('name', 'page'); 1563 } 1564 1565 if ($exclude === true) { 1566 $sql[] = "searchable"; 1567 } elseif ($exclude) { 1568 $exclude = join(',', quote_list(do_list_unique($exclude))); 1569 $sql[] = "name NOT IN ($exclude)"; 1570 } 1571 1572 if (!$include_default) { 1573 $sql[] = "name != 'default'"; 1574 } 1575 1576 if (!$sql_sort) { 1577 $sql_sort = "name ASC"; 1578 } 1579 1580 if ($include_default) { 1581 $sql_sort = "name != 'default', ".$sql_sort; 1582 } 1583 1584 $rs = safe_rows_start( 1585 "name, title, description", 1586 'txp_section', 1587 join(" AND ", $sql)." ORDER BY ".$sql_sort.$sql_limit 1588 ); 1589 1590 if ($rs && $last = numRows($rs)) { 1591 $out = array(); 1592 $count = 0; 1593 1594 if (isset($thissection)) { 1595 $old_section = $thissection; 1596 } 1597 1598 while ($a = nextRow($rs)) { 1599 ++$count; 1600 extract($a); 1601 1602 if ($name == 'default') { 1603 $title = $default_title; 1604 } 1605 1606 if ($form === '' && $thing === null) { 1607 $url = pagelinkurl(array('s' => $name)); 1608 1609 $out[] = tag(txpspecialchars($title), 'a', 1610 (($active_class && (0 == strcasecmp($s, $name))) ? ' class="'.txpspecialchars($active_class).'"' : ''). 1611 ' href="'.$url.'"' 1612 ); 1613 } else { 1614 $thissection = array( 1615 'name' => $name, 1616 'title' => $title, 1617 'description' => $description, 1618 'is_first' => ($count == 1), 1619 'is_last' => ($count == $last), 1620 ); 1621 1622 if ($thing === null && $form !== '') { 1623 $out[] = parse_form($form); 1624 } else { 1625 $out[] = parse($thing); 1626 } 1627 } 1628 } 1629 1630 $thissection = isset($old_section) ? $old_section : null; 1631 1632 if ($out) { 1633 return doWrap($out, $wraptag, compact('break', 'class', 'html_id')); 1634 } 1635 } 1636 1637 return ''; 1638 } 1639 1640 // ------------------------------------------------------------- 1641 1642 // Input form for search queries. 1643 function search_input($atts, $thing = null) 1644 { 1645 global $q, $permlink_mode, $doctype; 1646 static $outside = null; 1647 1648 $inside = is_array($outside); 1649 1650 extract(lAtts(array( 1651 'form' => null, 1652 'wraptag' => 'p', 1653 'class' => __FUNCTION__, 1654 'size' => '15', 1655 'html_id' => '', 1656 'label' => gTxt('search'), 1657 'aria_label' => '', 1658 'placeholder' => '', 1659 'button' => '', 1660 'section' => '', 1661 'match' => 'exact', 1662 ), $inside ? $atts + $outside : $atts)); 1663 1664 unset($atts['form']); 1665 1666 if (!$inside && !isset($form) && !isset($thing) && empty($atts)) { 1667 $form = 'search_input'; 1668 } 1669 1670 if ($form && $form = fetch_form($form)) { 1671 $thing = $form; 1672 } 1673 1674 if (isset($thing)) { 1675 $oldatts = $outside; 1676 $outside = $atts; 1677 $out = parse($thing); 1678 $outside = $oldatts; 1679 } else { 1680 $h5 = ($doctype == 'html5'); 1681 $out = fInput( 1682 $h5 ? 'search' : 'text', 1683 array( 1684 'name' => 'q', 1685 'aria-label' => $aria_label, 1686 'placeholder' => $placeholder, 1687 'required' => $h5, 1688 'size' => $size, 1689 'class' => $wraptag || empty($atts['class']) ? false : $class 1690 ), 1691 $q 1692 ); 1693 } 1694 1695 if ($form || $inside) { 1696 empty($atts['wraptag']) or $out = doTag($out, $wraptag, $class); 1697 1698 return $out; 1699 } 1700 1701 $sub = (!empty($button)) ? '<input type="submit" value="'.txpspecialchars($button).'" />' : ''; 1702 $id = (!empty($html_id)) ? ' id="'.txpspecialchars($html_id).'"' : ''; 1703 1704 $out = (!empty($label)) ? txpspecialchars($label).br.$out.$sub : $out.$sub; 1705 $out = ($match === 'exact') ? $out : hInput('m', txpspecialchars($match)).$out; 1706 $out = ($wraptag) ? doTag($out, $wraptag, $class) : $out; 1707 1708 if (!$section) { 1709 return '<form role="search" method="get" action="'.hu.'"'.$id.'>'. 1710 n.$out. 1711 n.'</form>'; 1712 } 1713 1714 if ($permlink_mode != 'messy') { 1715 return '<form role="search" method="get" action="'.pagelinkurl(array('s' => $section)).'"'.$id.'>'. 1716 n.$out. 1717 n.'</form>'; 1718 } 1719 1720 return '<form role="search" method="get" action="'.hu.'"'.$id.'>'. 1721 n.hInput('s', $section). 1722 n.$out. 1723 n.'</form>'; 1724 } 1725 1726 // ------------------------------------------------------------- 1727 1728 function search_term($atts) 1729 { 1730 global $q; 1731 1732 if (empty($q)) { 1733 return ''; 1734 } 1735 1736 return txpspecialchars($q); 1737 } 1738 1739 // ------------------------------------------------------------- 1740 1741 // Link to next/prev article, if it exists. 1742 function link_to($atts, $thing = null, $target = 'next') 1743 { 1744 global $thisarticle, $txp_context; 1745 static $lAtts = array( 1746 'form' => '', 1747 'link' => 1, 1748 'showalways' => 0 1749 ); 1750 1751 if (!in_array($target, array('next', 'prev')) || !assert_article()) { 1752 return ''; 1753 } 1754 1755 $atts += array('context' => empty($txp_context) ? true : null); 1756 extract($atts + $lAtts, EXTR_SKIP); 1757 1758 if (is_array($thisarticle)) { 1759 if (!isset($thisarticle[$target])) { 1760 $thisarticle = $thisarticle + getNextPrev(); 1761 } 1762 1763 if ($thisarticle[$target] !== false) { 1764 $oldarticle = $thisarticle; 1765 $thisarticle = $thisarticle[$target]; 1766 $url = permlink(array_diff_key($atts, $lAtts)); 1767 1768 if ($form || $thing !== null) { 1769 populateArticleData($thisarticle); 1770 $thisarticle['is_first'] = $thisarticle['is_last'] = true; 1771 $thing = $form ? parse_form($form) : parse($thing); 1772 $target_title = escape_title($thisarticle['Title']); 1773 1774 $url = $link ? href( 1775 $thing, 1776 $url, 1777 ($target_title != $thing ? ' title="'.$target_title.'"' : ''). 1778 ' rel="'.$target.'"' 1779 ) : $thing; 1780 } 1781 1782 $thisarticle = $oldarticle; 1783 1784 return $url; 1785 } 1786 } 1787 1788 return ($showalways) ? parse($thing) : ''; 1789 } 1790 1791 // ------------------------------------------------------------- 1792 1793 function next_title() 1794 { 1795 global $thisarticle, $is_article_list; 1796 1797 if (!assert_article()) { 1798 return $is_article_list ? '' : null; 1799 } 1800 1801 if (!isset($thisarticle['next'])) { 1802 $thisarticle = $thisarticle + getNextPrev(); 1803 } 1804 1805 if ($thisarticle['next'] !== false) { 1806 return escape_title($thisarticle['next']['Title']); 1807 } else { 1808 return ''; 1809 } 1810 } 1811 1812 // ------------------------------------------------------------- 1813 1814 function prev_title() 1815 { 1816 global $thisarticle, $is_article_list; 1817 1818 if (!assert_article()) { 1819 return $is_article_list ? '' : null; 1820 } 1821 1822 if (!isset($thisarticle['prev'])) { 1823 $thisarticle = $thisarticle + getNextPrev(); 1824 } 1825 1826 if ($thisarticle['prev'] !== false) { 1827 return escape_title($thisarticle['prev']['Title']); 1828 } else { 1829 return ''; 1830 } 1831 } 1832 1833 // ------------------------------------------------------------- 1834 1835 function site_name() 1836 { 1837 global $sitename; 1838 1839 return txpspecialchars($sitename); 1840 } 1841 1842 // ------------------------------------------------------------- 1843 1844 function site_slogan() 1845 { 1846 global $site_slogan; 1847 1848 return txpspecialchars($site_slogan); 1849 } 1850 1851 // ------------------------------------------------------------- 1852 1853 function link_to_home($atts, $thing = null) 1854 { 1855 extract(lAtts(array('class' => false), $atts)); 1856 1857 if ($thing) { 1858 $class = ($class) ? ' class="'.txpspecialchars($class).'"' : ''; 1859 1860 return href( 1861 parse($thing), 1862 hu, 1863 $class. 1864 ' rel="home"' 1865 ); 1866 } 1867 1868 return hu; 1869 } 1870 1871 // ------------------------------------------------------------- 1872 1873 function txp_pager($atts, $thing = null, $newer = null) 1874 { 1875 global $thispage, $is_article_list, $txp_context, $txp_item; 1876 static $pg = true, $numPages = null, $linkall = false, $top = 1, $shown = array(); 1877 static $items = array('page' => null, 'total' => null, 'url' => null); 1878 1879 $get = isset($atts['total']) && $atts['total'] === true; 1880 $set = $newer === null && (isset($atts['pg']) || isset($atts['total']) && !$get); 1881 $oldPages = $numPages; 1882 $oldpg = $pg; 1883 1884 extract(lAtts($set ? array( 1885 'pg' => $pg, 1886 'total' => $numPages, 1887 'shift' => 1, 1888 'showalways' => true, 1889 'link' => false, 1890 ) : array( 1891 'showalways' => false, 1892 'title' => '', 1893 'link' => $linkall, 1894 'escape' => 'html', 1895 'rel' => '', 1896 'shift' => false, 1897 'limit' => 0, 1898 'break' => '',) + 1899 ($get ? array( 1900 'total' => true, 1901 ) : array()), $atts)); 1902 1903 if ($set) { 1904 if (isset($total) && $total !== true) { 1905 $numPages = (int)$total; 1906 } elseif ($pg === true) { 1907 $numPages = isset($thispage['numPages']) ? (int)$thispage['numPages'] : null; 1908 } 1909 } 1910 1911 if (!isset($numPages)) { 1912 if (isset($thispage['numPages'])) { 1913 $numPages = (int)$thispage['numPages']; 1914 } else { 1915 return $is_article_list ? postpone_process() : ''; 1916 } 1917 } 1918 1919 if ($set) { 1920 $oldtop = $top; 1921 $top = $shift === true ? 0 : ((int)$shift < 0 ? $numPages + $shift + 1 : $shift); 1922 $oldshown = $shown; 1923 $oldlink = $linkall; 1924 $linkall = $link; 1925 $shown = array(); 1926 1927 if ($thing !== null) { 1928 $thing = $numPages >= ($showalways ? (int)$showalways : 2) ? parse($thing) : ''; 1929 $numPages = $oldPages; 1930 $pg = $oldpg; 1931 $top = $oldtop; 1932 $linkall = $oldlink; 1933 $shown = $oldshown; 1934 } 1935 1936 return $thing; 1937 } 1938 1939 $pgc = $pg === true ? 'pg' : $pg; 1940 $thispg = $pg === true && isset($thispage['pg']) ? $thispage['pg'] : intval(gps($pgc, $top)); 1941 $thepg = max(1, min($thispg, $numPages)); 1942 1943 if ($get) { 1944 if ($thing === null && $shift === false) { 1945 return $newer === null ? $numPages : ($newer ? $thepg - 1 : $numPages - $thepg); 1946 } elseif ($shift === true || $shift === false) { 1947 if ($newer !== null) { 1948 $range = $newer ? $thepg - 1 : $numPages - $thepg; 1949 } 1950 } else { 1951 $range = (int)$shift; 1952 } 1953 } 1954 1955 if (isset($range)) { 1956 if (!$range) { 1957 $pages = array(); 1958 } elseif ($range > 0) { 1959 $pages = $newer === null ? range(-max($range, 2*$range + $thepg - $numPages), max($range, 2*$range - $thepg + 1)) : 1960 range($newer ? max($range, 2*$range + $thepg - $numPages) : 1, $newer ? 1 : max($range, 2*$range - $thepg + 1)); 1961 } else { 1962 $pages = $newer !== null ? ($newer ? range(-1, -max(-$range, -2*$range + $thepg - $numPages)) : range(-max(-$range, -2*$range - $thepg + 1), -1)) : 1963 range(min(max(1 - $range - $thepg, 1 - 2*$range - $numPages), 0), max(0, min($numPages + $range - $thepg, $numPages + 2*$range - 1))); 1964 } 1965 } elseif (is_bool($shift)) { 1966 $pages = $newer === null ? ($shift ? range(1 - $thepg, $numPages - $thepg) : array(0)) : array($shift ? true : 1); 1967 $range = !$shift; 1968 } else { 1969 $pages = array_map('intval', do_list($shift, array(',', '-'))); 1970 $range = false; 1971 } 1972 1973 foreach ($items as $item => $val) { 1974 $items[$item] = isset($txp_item[$item]) ? $txp_item[$item] : null; 1975 } 1976 1977 $txp_item['total'] = $numPages; 1978 $limit = $limit ? (int)$limit : -1; 1979 $old_context = $txp_context; 1980 $txp_context += get_context(); 1981 $out = array(); 1982 1983 foreach ($pages as $page) { 1984 if ($newer === null) { 1985 $nextpg = $thepg + $page; 1986 } elseif ($newer) { 1987 $nextpg = $page === true ? 1 : ((int)$page < 0 ? -$page : $thepg - $page); 1988 } else { 1989 $nextpg = $page === true ? $numPages : ((int)$page < 0 ? $numPages + $page + 1 : $thepg + $page); 1990 } 1991 1992 if ( 1993 $nextpg >= ($newer === false && $range !== false ? $thepg + 1 : 1) && 1994 $nextpg <= ($newer === true && $range !== false ? $thepg - 1 : $numPages) 1995 ) { 1996 if (empty($shown[$nextpg]) || $showalways) { 1997 $txp_context[$pgc] = $nextpg == $top ? null : $nextpg; 1998 $url = pagelinkurl($txp_context); 1999 $txp_item['page'] = $nextpg; 2000 $txp_item['url'] = $url; 2001 2002 if ($shift !== false || $newer === null || !is_bool($range)) { 2003 $shown[$nextpg] = true; 2004 $limit--; 2005 } 2006 2007 if (isset($thing)) { 2008 if ($escape == 'html') { 2009 $title = escape_title($title); 2010 } elseif ($escape) { 2011 $title = txp_escape(array('escape' => $escape), $title); 2012 } 2013 2014 $url = $link || $link === false && $nextpg != $thispg ? href( 2015 parse($thing), 2016 $url, 2017 (empty($title) ? '' : ' title="'.$title.'"'). 2018 (empty($rel) ? '' : ' rel="'.txpspecialchars($rel).'"') 2019 ) : parse($thing); 2020 } 2021 } else { 2022 $url = false; 2023 } 2024 } else { 2025 $url = isset($thing) ? parse($thing, false) : false; 2026 } 2027 2028 empty($url) or $out[] = $url; 2029 2030 if (!$limit) { 2031 break; 2032 } 2033 } 2034 2035 foreach ($items as $item => $val) { 2036 $txp_item[$item] = $val; 2037 } 2038 2039 $txp_context = $old_context; 2040 2041 return doWrap($out, '', $break); 2042 } 2043 2044 // ------------------------------------------------------------- 2045 2046 function text($atts) 2047 { 2048 extract(lAtts(array( 2049 'item' => '', 2050 'escape' => 'html', 2051 ), $atts, false)); 2052 2053 if (!$item) { 2054 return ''; 2055 } 2056 2057 unset( 2058 $atts['item'], 2059 $atts['escape'] 2060 ); 2061 2062 $tags = array(); 2063 2064 foreach ($atts as $name => $value) { 2065 $tags['{'.$name.'}'] = $value; 2066 } 2067 2068 return gTxt($item, $tags, $escape); 2069 } 2070 2071 // ------------------------------------------------------------- 2072 2073 function article_id() 2074 { 2075 global $thisarticle; 2076 2077 assert_article(); 2078 2079 return $thisarticle['thisid']; 2080 } 2081 2082 // ------------------------------------------------------------- 2083 2084 function article_url_title() 2085 { 2086 global $thisarticle; 2087 2088 assert_article(); 2089 2090 return $thisarticle['url_title']; 2091 } 2092 2093 // ------------------------------------------------------------- 2094 2095 function if_article_id($atts, $thing = null) 2096 { 2097 global $thisarticle, $pretext; 2098 2099 assert_article(); 2100 2101 extract(lAtts(array('id' => $pretext['id']), $atts)); 2102 2103 $x = $id && in_list($thisarticle['thisid'], $id); 2104 return isset($thing) ? parse($thing, $x) : $x; 2105 } 2106 2107 // ------------------------------------------------------------- 2108 2109 function posted($atts, $thing = null, $time = 'posted') 2110 { 2111 global $thisarticle, $id, $c, $pg, $dateformat, $archive_dateformat; 2112 2113 assert_article(); 2114 2115 if (empty($thisarticle[$time])) { 2116 return ''; 2117 } 2118 2119 extract(lAtts(array( 2120 'format' => '', 2121 'gmt' => '', 2122 'lang' => '', 2123 ), $atts)); 2124 2125 if ($format) { 2126 $out = safe_strftime($format, $thisarticle[$time], $gmt, $lang); 2127 } else { 2128 if ($id || $c || $pg) { 2129 $out = safe_strftime($archive_dateformat, $thisarticle[$time], $gmt, $lang); 2130 } else { 2131 $out = safe_strftime($dateformat, $thisarticle[$time], $gmt, $lang); 2132 } 2133 } 2134 2135 return $out; 2136 } 2137 2138 // ------------------------------------------------------------- 2139 2140 function if_expires($atts, $thing = null) 2141 { 2142 global $thisarticle; 2143 2144 assert_article(); 2145 2146 $x = !empty($thisarticle['expires']); 2147 return isset($thing) ? parse($thing, $x) : $x; 2148 } 2149 2150 // ------------------------------------------------------------- 2151 2152 function if_expired($atts, $thing = null) 2153 { 2154 global $thisarticle; 2155 2156 assert_article(); 2157 2158 $x = !empty($thisarticle['expires']) && ($thisarticle['expires'] <= time()); 2159 return isset($thing) ? parse($thing, $x) : $x; 2160 } 2161 2162 // ------------------------------------------------------------- 2163 2164 function comments_count() 2165 { 2166 global $thisarticle; 2167 2168 assert_article(); 2169 2170 return $thisarticle['comments_count']; 2171 } 2172 2173 // ------------------------------------------------------------- 2174 2175 function comments_invite($atts) 2176 { 2177 global $thisarticle, $is_article_list; 2178 2179 assert_article(); 2180 2181 extract($thisarticle); 2182 global $comments_mode; 2183 2184 if (!$comments_invite) { 2185 $comments_invite = get_pref('comments_default_invite'); 2186 } 2187 2188 extract(lAtts(array( 2189 'class' => __FUNCTION__, 2190 'showcount' => true, 2191 'textonly' => false, 2192 'showalways' => false, // FIXME in crockery. This is only for BC. 2193 'wraptag' => '', 2194 ), $atts)); 2195 2196 $invite_return = ''; 2197 2198 if (($annotate || $comments_count) && ($showalways || $is_article_list)) { 2199 $comments_invite = txpspecialchars($comments_invite); 2200 $ccount = ($comments_count && $showcount) ? ' ['.$comments_count.']' : ''; 2201 2202 if ($textonly) { 2203 $invite_return = $comments_invite.$ccount; 2204 } else { 2205 if (!$comments_mode) { 2206 $invite_return = doTag($comments_invite, 'a', $class, ' href="'.permlinkurl($thisarticle).'#'.gTxt('comment').'" ').$ccount; 2207 } else { 2208 $invite_return = "<a href=\"".hu."?parentid=$thisid\" onclick=\"window.open(this.href, 'popupwindow', 'width=500,height=500,scrollbars,resizable,status'); return false;\"".(($class) ? ' class="'.txpspecialchars($class).'"' : '').'>'.$comments_invite.'</a> '.$ccount; 2209 } 2210 } 2211 2212 if ($wraptag) { 2213 $invite_return = doTag($invite_return, $wraptag, $class); 2214 } 2215 } 2216 2217 return $invite_return; 2218 } 2219 2220 // ------------------------------------------------------------- 2221 2222 function popup_comments($atts, $thing = null) 2223 { 2224 extract(lAtts(array('form' => 'comments_display'), $atts)); 2225 2226 $rs = safe_row( 2227 "*, UNIX_TIMESTAMP(Posted) AS uPosted, UNIX_TIMESTAMP(LastMod) AS uLastMod, UNIX_TIMESTAMP(Expires) AS uExpires", 2228 'textpattern', 2229 "ID=".intval(gps('parentid'))." AND Status >= 4" 2230 ); 2231 2232 if ($rs) { 2233 populateArticleData($rs); 2234 2235 return ($thing === null ? parse_form($form) : parse($thing)); 2236 } 2237 2238 return ''; 2239 } 2240 2241 // ------------------------------------------------------------- 2242 2243 function comments_form($atts, $thing = null) 2244 { 2245 global $thisarticle, $has_comments_preview; 2246 global $thiscommentsform; // TODO: Remove any uses of $thiscommentsform when removing deprecated attributes from below. 2247 2248 // deprecated attributes since TXP 4.6. Most of these (except msgstyle) 2249 // were moved to the tags that occur within a comments_form, although 2250 // some of the names changed. 2251 $deprecated = array('isize', 'msgrows', 'msgcols', 'msgstyle', 2252 'previewlabel', 'submitlabel', 'rememberlabel', 'forgetlabel'); 2253 2254 foreach ($deprecated as $att) { 2255 if (isset($atts[$att])) { 2256 trigger_error(gTxt('deprecated_attribute', array('{name}' => $att)), E_USER_NOTICE); 2257 } 2258 } 2259 2260 $atts = lAtts(array( 2261 'class' => __FUNCTION__, 2262 'form' => 'comment_form', 2263 'isize' => '25', 2264 'msgcols' => '25', 2265 'msgrows' => '5', 2266 'msgstyle' => '', 2267 'show_preview' => empty($has_comments_preview), 2268 'wraptag' => '', 2269 'previewlabel' => gTxt('preview'), 2270 'submitlabel' => gTxt('submit'), 2271 'rememberlabel' => gTxt('remember'), 2272 'forgetlabel' => gTxt('forget'), 2273 ), $atts); 2274 2275 extract($atts); 2276 2277 $thiscommentsform = array_intersect_key($atts, array_flip($deprecated)); 2278 2279 assert_article(); 2280 2281 extract($thisarticle); 2282 2283 $out = ''; 2284 $ip = serverSet('REMOTE_ADDR'); 2285 $blacklisted = is_blacklisted($ip); 2286 2287 if (!checkCommentsAllowed($thisid)) { 2288 $out = graf(gTxt('comments_closed'), ' id="comments_closed"'); 2289 } elseif ($blacklisted) { 2290 $out = graf(gTxt('your_ip_is_blacklisted_by'.' '.$blacklisted), ' id="comments_blocklisted"'); 2291 } elseif (gps('commented') !== '') { 2292 $out = gTxt('comment_posted'); 2293 2294 if (gps('commented') === '0') { 2295 $out .= " ".gTxt('comment_moderated'); 2296 } 2297 2298 $out = graf($out, ' id="txpCommentInputForm"'); 2299 } else { 2300 // Display a comment preview if required. 2301 if (ps('preview') && $show_preview) { 2302 $out = comments_preview(array()); 2303 } 2304 2305 extract(doDeEnt(psa(array('parentid', 'backpage')))); 2306 2307 // If the form fields are filled (anything other than blank), pages really 2308 // should not be saved by a public cache (rfc2616/14.9.1). 2309 if (pcs('name') || pcs('email') || pcs('web')) { 2310 header('Cache-Control: private'); 2311 } 2312 2313 $url = $GLOBALS['pretext']['request_uri']; 2314 2315 // Experimental clean URLs with only 404-error-document on Apache possibly 2316 // requires messy URLs for POST requests. 2317 if (defined('PARTLY_MESSY') && (PARTLY_MESSY)) { 2318 $url = hu.'?id='.intval($parentid); 2319 } 2320 2321 $out .= '<form id="txpCommentInputForm" method="post" action="'.txpspecialchars($url).'#cpreview">'. 2322 n.'<div class="comments-wrapper">'.n. // Prevent XHTML Strict validation gotchas. 2323 ($thing === null ? parse_form($form) : parse($thing)). 2324 n.hInput('parentid', ($parentid ? $parentid : $thisid)). 2325 n.hInput('backpage', (ps('preview') ? $backpage : $url)). 2326 n.'</div>'. 2327 n.'</form>'; 2328 } 2329 2330 return (!$wraptag ? $out : doTag($out, $wraptag, $class)); 2331 } 2332 2333 // ------------------------------------------------------------- 2334 2335 function comment_input($atts, $thing = null, $field = 'name', $clean = false) 2336 { 2337 global $prefs, $thiscommentsform; 2338 2339 extract(lAtts(array( 2340 'class' => '', 2341 'size' => $thiscommentsform['isize'], 2342 'aria_label' => '', 2343 'placeholder' => '', 2344 ), $atts)); 2345 2346 $warn = false; 2347 $val = is_callable($clean) ? $clean(pcs($field)) : pcs($field); 2348 $h5 = ($prefs['doctype'] == 'html5'); 2349 $required = get_pref('comments_require_'.$field); 2350 2351 if (!empty($class)) { 2352 $class = ' '.txpspecialchars($class); 2353 } 2354 2355 if (ps('preview')) { 2356 $comment = getComment(); 2357 $val = $comment[$field]; 2358 $warn = $required && !$val; 2359 } 2360 2361 return fInput('text', array( 2362 'name' => $field, 2363 'aria-label' => $aria_label, 2364 'autocomplete' => $field == 'web' ? 'url' : $field, 2365 'placeholder' => $placeholder, 2366 'required' => $h5 && $required 2367 ), $val, 'comment_'.$field.'_input'.$class.($warn ? ' comments_error' : ''), '', '', $size, '', $field); 2368 } 2369 2370 // ------------------------------------------------------------- 2371 2372 function comment_message_input($atts) 2373 { 2374 global $prefs, $thiscommentsform; 2375 2376 extract(lAtts(array( 2377 'class' => '', 2378 'rows' => $thiscommentsform['msgrows'], 2379 'cols' => $thiscommentsform['msgcols'], 2380 'aria_label' => '', 2381 'placeholder' => '' 2382 ), $atts)); 2383 2384 $style = $thiscommentsform['msgstyle']; 2385 $commentwarn = false; 2386 $n_message = 'message'; 2387 $formnonce = ''; 2388 $message = ''; 2389 2390 if (!empty($class)) { 2391 $class = ' '.txpspecialchars($class); 2392 } 2393 2394 if (ps('preview')) { 2395 $comment = getComment(); 2396 $message = $comment['message']; 2397 $split = rand(1, 31); 2398 $nonce = getNextNonce(); 2399 $secret = getNextSecret(); 2400 safe_insert('txp_discuss_nonce', "issue_time = NOW(), nonce = '".doSlash($nonce)."', secret = '".doSlash($secret)."'"); 2401 $n_message = md5('message'.$secret); 2402 $formnonce = n.hInput(substr($nonce, 0, $split), substr($nonce, $split)); 2403 $commentwarn = (!trim($message)); 2404 } 2405 2406 $attr = join_atts(array( 2407 'cols' => intval($cols), 2408 'rows' => intval($rows), 2409 'required' => $prefs['doctype'] == 'html5', 2410 'style' => $style, 2411 'aria-label' => $aria_label, 2412 'placeholder' => $placeholder 2413 )); 2414 2415 return '<textarea class="txpCommentInputMessage'.$class.(($commentwarn) ? ' comments_error"' : '"'). 2416 ' id="message" name="'.$n_message.'"'.$attr. 2417 '>'.txpspecialchars(substr(trim($message), 0, 65535)).'</textarea>'. 2418 callback_event('comment.form'). 2419 $formnonce; 2420 } 2421 2422 // ------------------------------------------------------------- 2423 2424 function comment_remember($atts) 2425 { 2426 global $thiscommentsform; 2427 2428 extract(lAtts(array( 2429 'class' => '', 2430 'rememberlabel' => $thiscommentsform['rememberlabel'], 2431 'forgetlabel' => $thiscommentsform['forgetlabel'] 2432 ), $atts)); 2433 2434 if (!empty($class)) { 2435 $class = ' class="'.txpspecialchars($class).'"'; 2436 } 2437 2438 extract(doDeEnt(psa(array('checkbox_type', 'remember', 'forget')))); 2439 2440 if (!ps('preview')) { 2441 $rememberCookie = cs('txp_remember'); 2442 2443 if (!$rememberCookie) { 2444 $checkbox_type = 'remember'; 2445 } else { 2446 $checkbox_type = 'forget'; 2447 } 2448 2449 // Inhibit default remember. 2450 if ($forget == 1 || (string) $rememberCookie === '0') { 2451 destroyCookies(); 2452 } 2453 } 2454 2455 if ($checkbox_type == 'forget') { 2456 $checkbox = checkbox('forget', 1, $forget, '', 'forget').' '.tag(txpspecialchars($forgetlabel), 'label', ' for="forget"'.$class); 2457 } else { 2458 $checkbox = checkbox('remember', 1, $remember, '', 'remember').' '.tag(txpspecialchars($rememberlabel), 'label', ' for="remember"'.$class); 2459 } 2460 2461 $checkbox .= ' '.hInput('checkbox_type', $checkbox_type); 2462 2463 return $checkbox; 2464 } 2465 2466 // ------------------------------------------------------------- 2467 2468 function comment_preview($atts) 2469 { 2470 global $thiscommentsform; 2471 2472 extract(lAtts(array( 2473 'class' => '', 2474 'label' => $thiscommentsform['previewlabel'] 2475 ), $atts)); 2476 2477 if (!empty($class)) { 2478 $class = ' '.txpspecialchars($class); 2479 } 2480 2481 return fInput('submit', 'preview', $label, 'button'.$class, '', '', '', '', 'txpCommentPreview', false); 2482 } 2483 2484 // ------------------------------------------------------------- 2485 2486 function comment_submit($atts) 2487 { 2488 global $thiscommentsform; 2489 2490 extract(lAtts(array( 2491 'class' => '', 2492 'label' => $thiscommentsform['submitlabel'] 2493 ), $atts)); 2494 2495 if (!empty($class)) { 2496 $class = ' '.txpspecialchars($class); 2497 } 2498 2499 // If all fields check out, the submit button is active/clickable. 2500 if (ps('preview')) { 2501 return fInput('submit', 'submit', $label, 'button'.$class, '', '', '', '', 'txpCommentSubmit', false); 2502 } else { 2503 return fInput('submit', 'submit', $label, 'button disabled'.$class, '', '', '', '', 'txpCommentSubmit', true); 2504 } 2505 } 2506 2507 // ------------------------------------------------------------- 2508 2509 function comments_error($atts) 2510 { 2511 extract(lAtts(array( 2512 'break' => 'br', 2513 'class' => __FUNCTION__, 2514 'wraptag' => 'div', 2515 ), $atts)); 2516 2517 $evaluator = & get_comment_evaluator(); 2518 2519 $errors = $evaluator->get_result_message(); 2520 2521 if ($errors) { 2522 return doWrap($errors, $wraptag, $break, $class); 2523 } 2524 } 2525 2526 // ------------------------------------------------------------- 2527 2528 function if_comments_error($atts, $thing = null) 2529 { 2530 $evaluator = & get_comment_evaluator(); 2531 2532 $x = (count($evaluator->get_result_message()) > 0); 2533 return isset($thing) ? parse($thing, $x) : $x; 2534 } 2535 2536 // ------------------------------------------------------------- 2537 2538 function comments($atts, $thing = null) 2539 { 2540 global $thisarticle, $prefs; 2541 extract($prefs); 2542 2543 extract(lAtts(array( 2544 'form' => 'comments', 2545 'wraptag' => ($comments_are_ol ? 'ol' : ''), 2546 'break' => ($comments_are_ol ? 'li' : 'div'), 2547 'class' => __FUNCTION__, 2548 'limit' => 0, 2549 'offset' => 0, 2550 'sort' => 'posted ASC', 2551 ), $atts)); 2552 2553 assert_article(); 2554 extract($thisarticle); 2555 2556 if (!$comments_count) { 2557 return ''; 2558 } 2559 2560 $qparts = array( 2561 "parentid = ".intval($thisid)." AND visible = ".VISIBLE, 2562 "ORDER BY ".sanitizeForSort($sort), 2563 ($limit) ? "LIMIT ".intval($offset).", ".intval($limit) : '', 2564 ); 2565 2566 $rs = safe_rows_start("*, UNIX_TIMESTAMP(posted) AS time", 'txp_discuss', join(' ', $qparts)); 2567 2568 $out = ''; 2569 2570 if ($rs) { 2571 $comments = array(); 2572 2573 while ($vars = nextRow($rs)) { 2574 $GLOBALS['thiscomment'] = $vars; 2575 $comments[] = ($thing === null ? parse_form($form) : parse($thing)).n; 2576 unset($GLOBALS['thiscomment']); 2577 } 2578 2579 $out .= doWrap($comments, $wraptag, $break, $class); 2580 } 2581 2582 return $out; 2583 } 2584 2585 // ------------------------------------------------------------- 2586 2587 function comments_preview($atts, $thing = null) 2588 { 2589 global $has_comments_preview; 2590 2591 if (!ps('preview')) { 2592 return ''; 2593 } 2594 2595 extract(lAtts(array( 2596 'form' => 'comments', 2597 'wraptag' => '', 2598 'class' => __FUNCTION__, 2599 ), $atts)); 2600 2601 assert_article(); 2602 2603 $preview = psa(array('name', 'email', 'web', 'message', 'parentid', 'remember')); 2604 $preview['time'] = time(); 2605 $preview['discussid'] = 0; 2606 $preview['name'] = strip_tags($preview['name']); 2607 $preview['email'] = clean_url($preview['email']); 2608 2609 if ($preview['message'] == '') { 2610 $in = getComment(); 2611 $preview['message'] = $in['message']; 2612 } 2613 2614 // It is called 'message', not 'novel'! 2615 $preview['message'] = markup_comment(substr(trim($preview['message']), 0, 65535)); 2616 2617 $preview['web'] = clean_url($preview['web']); 2618 2619 $GLOBALS['thiscomment'] = $preview; 2620 $comments = ($thing === null ? parse_form($form) : parse($thing)).n; 2621 unset($GLOBALS['thiscomment']); 2622 $out = doTag($comments, $wraptag, $class); 2623 2624 // Set a flag to tell the comments_form tag that it doesn't have to show 2625 // a preview. 2626 $has_comments_preview = true; 2627 2628 return $out; 2629 } 2630 2631 // ------------------------------------------------------------- 2632 2633 function if_comments_preview($atts, $thing = null) 2634 { 2635 $x = ps('preview') && checkCommentsAllowed(gps('parentid')); 2636 return isset($thing) ? parse($thing, $x) : $x; 2637 } 2638 2639 // ------------------------------------------------------------- 2640 2641 function comment_permlink($atts, $thing) 2642 { 2643 global $thisarticle, $thiscomment; 2644 2645 assert_article(); 2646 assert_comment(); 2647 2648 extract($thiscomment); 2649 extract(lAtts(array('anchor' => empty($thiscomment['has_anchor_tag'])), $atts)); 2650 2651 $dlink = permlinkurl($thisarticle).'#c'.$discussid; 2652 2653 $thing = parse($thing); 2654 2655 $name = ($anchor ? ' id="c'.$discussid.'"' : ''); 2656 2657 return tag($thing, 'a', ' href="'.$dlink.'"'.$name); 2658 } 2659 2660 // ------------------------------------------------------------- 2661 2662 function comment_id() 2663 { 2664 global $thiscomment; 2665 2666 assert_comment(); 2667 2668 return $thiscomment['discussid']; 2669 } 2670 2671 // ------------------------------------------------------------- 2672 2673 function comment_name($atts) 2674 { 2675 global $thiscomment, $prefs; 2676 static $encoder = null; 2677 2678 assert_comment(); 2679 isset($encoder) or $encoder = Txp::get('\Textpattern\Mail\Encode'); 2680 2681 extract($prefs); 2682 extract($thiscomment); 2683 2684 extract(lAtts(array('link' => 1), $atts)); 2685 2686 $name = txpspecialchars($name); 2687 2688 if ($link) { 2689 $web = comment_web(); 2690 $nofollow = (@$comment_nofollow ? ' rel="nofollow"' : ''); 2691 2692 if (!empty($web)) { 2693 return href($name, $web, $nofollow); 2694 } 2695 2696 if ($email && !$never_display_email) { 2697 return href($name, $encoder->entityObfuscateAddress('mailto:'.$email), $nofollow); 2698 } 2699 } 2700 2701 return $name; 2702 } 2703 2704 // ------------------------------------------------------------- 2705 2706 function comment_email() 2707 { 2708 global $thiscomment; 2709 2710 assert_comment(); 2711 2712 return txpspecialchars($thiscomment['email']); 2713 } 2714 2715 // ------------------------------------------------------------- 2716 2717 function comment_web() 2718 { 2719 global $thiscomment; 2720 2721 assert_comment(); 2722 2723 if (preg_match('/^\S/', $thiscomment['web'])) { 2724 // Prepend default protocol 'http' for all non-local URLs. 2725 if (!preg_match('!^https?://|^#|^/[^/]!', $thiscomment['web'])) { 2726 $thiscomment['web'] = 'http://'.$thiscomment['web']; 2727 } 2728 2729 return txpspecialchars($thiscomment['web']); 2730 } 2731 2732 return ''; 2733 } 2734 2735 // ------------------------------------------------------------- 2736 2737 function comment_time($atts) 2738 { 2739 global $thiscomment, $comments_dateformat; 2740 2741 assert_comment(); 2742 2743 extract(lAtts(array( 2744 'format' => $comments_dateformat, 2745 'gmt' => '', 2746 'lang' => '', 2747 ), $atts)); 2748 2749 return safe_strftime($format, $thiscomment['time'], $gmt, $lang); 2750 } 2751 2752 // ------------------------------------------------------------- 2753 2754 function comment_message() 2755 { 2756 global $thiscomment; 2757 2758 assert_comment(); 2759 2760 return $thiscomment['message']; 2761 } 2762 2763 // ------------------------------------------------------------- 2764 2765 function comment_anchor() 2766 { 2767 global $thiscomment; 2768 2769 assert_comment(); 2770 2771 $thiscomment['has_anchor_tag'] = 1; 2772 2773 return '<a id="c'.$thiscomment['discussid'].'"></a>'; 2774 } 2775 2776 // ------------------------------------------------------------- 2777 2778 function author($atts) 2779 { 2780 global $thisarticle, $thisauthor, $s, $author; 2781 2782 extract(lAtts(array( 2783 'escape' => 'html', 2784 'link' => 0, 2785 'title' => 1, 2786 'section' => '', 2787 'this_section' => 0, 2788 'format' => '', // empty, link, or url 2789 ), $atts)); 2790 2791 // Synonym. 2792 if ($format === 'link') { 2793 $link = 1; 2794 } 2795 2796 $fetchRealName = $link || $title || $format === 'url'; 2797 2798 if ($thisauthor) { 2799 $realname = $thisauthor['realname']; 2800 $name = $thisauthor['name']; 2801 } elseif ($author) { 2802 $name = $author; 2803 } else { 2804 assert_article(); 2805 $name = $thisarticle['authorid']; 2806 } 2807 2808 isset($realname) or $realname = $fetchRealName ? get_author_name($name) : $name; 2809 2810 if ($title) { 2811 $display_name = $realname; 2812 } else { 2813 $display_name = $name; 2814 } 2815 2816 if ($escape === 'html') { 2817 $display_name = txpspecialchars($display_name); 2818 } elseif ($escape) { 2819 $display_name = txp_escape(array('escape' => $escape), $display_name); 2820 } 2821 2822 if (!$link && $format !== 'url') { 2823 return $display_name; 2824 } 2825 2826 if ($this_section && $s != 'default') { 2827 $section = $s; 2828 } 2829 2830 $href = pagelinkurl(array( 2831 's' => $section, 2832 'author' => $realname, 2833 )); 2834 2835 return $format === 'url' ? $href : href($display_name, $href, ' rel="author"'); 2836 } 2837 2838 // ------------------------------------------------------------- 2839 2840 function author_email($atts) 2841 { 2842 global $thisarticle, $thisauthor; 2843 2844 extract(lAtts(array( 2845 'escape' => 'html', 2846 'link' => '', 2847 ), $atts)); 2848 2849 if ($thisauthor) { 2850 $email = get_author_email($thisauthor['name']); 2851 } else { 2852 assert_article(); 2853 $email = get_author_email($thisarticle['authorid']); 2854 } 2855 2856 if ($escape == 'html') { 2857 $display_email = txpspecialchars($email); 2858 } else { 2859 $display_email = $escape ? txp_escape(array('escape' => $escape), $email) : $email; 2860 } 2861 2862 if ($link) { 2863 return email(array( 2864 'email' => $email, 2865 'linktext' => $display_email, 2866 )); 2867 } 2868 2869 return $display_email; 2870 } 2871 2872 // ------------------------------------------------------------- 2873 2874 function if_author($atts, $thing = null) 2875 { 2876 global $author, $context, $thisauthor; 2877 2878 extract(lAtts(array( 2879 'type' => 'article', 2880 'name' => '', 2881 ), $atts)); 2882 2883 $theType = ($type) ? $type == $context : true; 2884 2885 if ($thisauthor) { 2886 $x = $name === '' || in_list($thisauthor['name'], $name); 2887 } elseif ($name) { 2888 $x = ($theType && in_list($author, $name)); 2889 } else { 2890 $x = ($theType && (string) $author !== ''); 2891 } 2892 2893 return isset($thing) ? parse($thing, $x) : $x; 2894 } 2895 2896 // ------------------------------------------------------------- 2897 2898 function if_article_author($atts, $thing = null) 2899 { 2900 global $thisarticle; 2901 2902 assert_article(); 2903 2904 extract(lAtts(array('name' => ''), $atts)); 2905 2906 $author = $thisarticle['authorid']; 2907 2908 $x = $name ? in_list($author, $name) : (string) $author !== ''; 2909 return isset($thing) ? parse($thing, $x) : $x; 2910 } 2911 2912 // ------------------------------------------------------------- 2913 2914 function if_logged_in($atts, $thing = null) 2915 { 2916 global $txp_groups; 2917 static $cache = array(); 2918 2919 extract(lAtts(array( 2920 'group' => '', 2921 'name' => '', 2922 ), $atts)); 2923 2924 $user = isset($cache[$name]) ? $cache[$name] : ($cache[$name] = is_logged_in($name)); 2925 $x = false; 2926 2927 if ($user && $group !== '') { 2928 $privs = do_list($group); 2929 $groups = array_flip($txp_groups); 2930 2931 foreach ($privs as &$priv) { 2932 if (!is_numeric($priv) && isset($groups[$priv])) { 2933 $priv = $groups[$priv]; 2934 } else { 2935 $priv = intval($priv); 2936 } 2937 } 2938 2939 $privs = array_unique($privs); 2940 2941 if (in_array($user['privs'], $privs)) { 2942 $x = true; 2943 } 2944 } else { 2945 $x = (bool) $user; 2946 } 2947 2948 return isset($thing) ? parse($thing, $x) : $x; 2949 } 2950 2951 // ------------------------------------------------------------- 2952 2953 function txp_sandbox($atts = array(), $thing = null, $parse = true) 2954 { 2955 static $articles = array(), $uniqid = null, $stack = array(), $depth = null; 2956 global $thisarticle, $is_article_body; 2957 2958 isset($depth) or $depth = get_pref('form_circular_depth', 15); 2959 2960 extract($atts + array('field' => '')); 2961 unset($atts['field']); 2962 2963 if (empty($id)) { 2964 assert_article(); 2965 $id = $thisarticle['thisid']; 2966 } elseif (!isset($articles[$id])) { 2967 return; 2968 } 2969 2970 if ($field) { 2971 if (!isset($stack[$id])) { 2972 $stack[$id] = 1; 2973 } elseif ($stack[$id] >= $depth) { 2974 trigger_error(gTxt('form_circular_reference', array( 2975 '{name}' => '<txp:article id="'.$id.'"/>' 2976 ))); 2977 2978 return ''; 2979 } else { 2980 $stack[$id]++; 2981 } 2982 } 2983 2984 if ($parse) { 2985 $oldarticle = $thisarticle; 2986 isset($articles[$id]) and $thisarticle = $articles[$id]; 2987 $was_article_body = $is_article_body; 2988 !$field or $is_article_body = 1; 2989 2990 $thing = parse(isset($thing) ? $thing : $thisarticle[$field]); 2991 2992 $is_article_body = $was_article_body; 2993 $thisarticle = $oldarticle; 2994 } 2995 2996 !$field or $stack[$id]--; 2997 2998 if (!preg_match('@<(?:'.TXP_PATTERN.'):@', $thing)) { 2999 return $thing; 3000 } 3001 3002 if (!isset($uniqid)) { 3003 $uniqid = 'sandbox_'.strtr(uniqid('', true), '.', '_'); 3004 Txp::get('\Textpattern\Tag\Registry')->register('txp_sandbox', $uniqid); 3005 } 3006 3007 if ($field) { 3008 $tag = $field; 3009 unset($atts['id']); 3010 } else { 3011 $tag = $uniqid; 3012 $atts['id'] = $id; 3013 } 3014 3015 isset($articles[$id]) or $articles[$id] = $thisarticle; 3016 3017 return "<txp:$tag".($atts ? join_atts($atts) : '').">{$thing}</txp:$tag>"; 3018 } 3019 3020 // ------------------------------------------------------------- 3021 3022 function body($atts = array(), $thing = null) 3023 { 3024 return txp_sandbox(array('field' => 'body'), $thing); 3025 } 3026 3027 // ------------------------------------------------------------- 3028 3029 function title($atts) 3030 { 3031 global $thisarticle, $prefs; 3032 3033 assert_article(); 3034 3035 extract(lAtts(array( 3036 'escape' => null, 3037 'no_widow' => '', 3038 ), $atts)); 3039 3040 $t = $escape === null ? escape_title($thisarticle['title']) : $thisarticle['title']; 3041 3042 if ($no_widow && $escape === null) { 3043 $t = noWidow($t); 3044 } 3045 3046 return $t; 3047 } 3048 3049 // ------------------------------------------------------------- 3050 3051 function excerpt($atts = array(), $thing = null) 3052 { 3053 return txp_sandbox(array('field' => 'excerpt'), $thing); 3054 } 3055 3056 // ------------------------------------------------------------- 3057 3058 function article_category($atts, $thing = null) 3059 { 3060 global $thisarticle, $s, $permlink_mode; 3061 3062 assert_article(); 3063 3064 extract(lAtts(array( 3065 'number' => 1, 3066 'class' => '', 3067 'link' => 0, 3068 'title' => 0, 3069 'escape' => true, 3070 'section' => '', 3071 'this_section' => 0, 3072 'wraptag' => '', 3073 ), $atts)); 3074 3075 $cat = 'category'.intval($number); 3076 3077 if (!empty($thisarticle[$cat])) { 3078 $section = ($this_section) ? ($s == 'default' ? '' : $s) : $section; 3079 $category = $thisarticle[$cat]; 3080 3081 $label = $title ? fetch_category_title($category) : $category; 3082 3083 if ($thing) { 3084 $out = href( 3085 parse($thing), 3086 pagelinkurl(array( 3087 's' => $section, 3088 'c' => $category, 3089 )), 3090 (($class && !$wraptag) ? ' class="'.txpspecialchars($class).'"' : ''). 3091 ($title ? ' title="'.txpspecialchars($label).'"' : ''). 3092 ($permlink_mode != 'messy' ? ' rel="tag"' : '') 3093 ); 3094 } else { 3095 if ($escape) { 3096 $label = txp_escape(array('escape' => $escape), $label); 3097 } 3098 3099 if ($link) { 3100 $out = href( 3101 $label, 3102 pagelinkurl(array( 3103 's' => $section, 3104 'c' => $category, 3105 )), 3106 ($permlink_mode != 'messy' ? ' rel="tag"' : '') 3107 ); 3108 } else { 3109 $out = $label; 3110 } 3111 } 3112 3113 return doTag($out, $wraptag, $class); 3114 } 3115 } 3116 3117 // ------------------------------------------------------------- 3118 3119 function category($atts, $thing = null) 3120 { 3121 global $s, $c, $thiscategory, $context; 3122 3123 extract(lAtts(array( 3124 'class' => '', 3125 'link' => 0, 3126 'name' => '', 3127 'section' => $s, 3128 'this_section' => 0, 3129 'title' => 0, 3130 'type' => 'article', 3131 'url' => 0, 3132 'wraptag' => '', 3133 ), $atts)); 3134 3135 if ($name) { 3136 $category = $name; 3137 $type = validContext($type); 3138 } elseif (!empty($thiscategory['name'])) { 3139 $category = $thiscategory['name']; 3140 $type = $thiscategory['type']; 3141 } else { 3142 $category = $c; 3143 $type = $context; 3144 } 3145 3146 if ($category) { 3147 if ($this_section) { 3148 $section = ($s == 'default' ? '' : $s); 3149 } elseif (isset($thiscategory['section'])) { 3150 $section = $thiscategory['section']; 3151 } 3152 3153 $label = txpspecialchars(($title) ? fetch_category_title($category, $type) : $category); 3154 3155 $href = pagelinkurl(array( 3156 's' => $section, 3157 'c' => $category, 3158 'context' => $type, 3159 )); 3160 3161 if ($thing) { 3162 $out = href( 3163 parse($thing), 3164 $href, 3165 (($class && !$wraptag) ? ' class="'.txpspecialchars($class).'"' : ''). 3166 ($title ? ' title="'.$label.'"' : '') 3167 ); 3168 } elseif ($link) { 3169 $out = href( 3170 $label, 3171 $href, 3172 ($class && !$wraptag) ? ' class="'.txpspecialchars($class).'"' : '' 3173 ); 3174 } elseif ($url) { 3175 $out = $href; 3176 } else { 3177 $out = $label; 3178 } 3179 3180 return doTag($out, $wraptag, $class); 3181 } 3182 } 3183 3184 // ------------------------------------------------------------- 3185 3186 function section($atts, $thing = null) 3187 { 3188 global $thisarticle, $s, $thissection; 3189 3190 extract(lAtts(array( 3191 'class' => '', 3192 'link' => 0, 3193 'name' => '', 3194 'title' => 0, 3195 'url' => 0, 3196 'wraptag' => '', 3197 ), $atts)); 3198 3199 if ($name) { 3200 $sec = $name; 3201 } elseif (!empty($thissection['name'])) { 3202 $sec = $thissection['name']; 3203 } elseif (!empty($thisarticle['section'])) { 3204 $sec = $thisarticle['section']; 3205 } else { 3206 $sec = $s; 3207 } 3208 3209 if ($sec) { 3210 $label = txpspecialchars(($title) ? fetch_section_title($sec) : $sec); 3211 3212 $href = pagelinkurl(array('s' => $sec)); 3213 3214 if ($thing) { 3215 $out = href( 3216 parse($thing), 3217 $href, 3218 (($class && !$wraptag) ? ' class="'.txpspecialchars($class).'"' : ''). 3219 ($title ? ' title="'.$label.'"' : '') 3220 ); 3221 } elseif ($link) { 3222 $out = href( 3223 $label, 3224 $href, 3225 ($class && !$wraptag) ? ' class="'.txpspecialchars($class).'"' : '' 3226 ); 3227 } elseif ($url) { 3228 $out = $href; 3229 } else { 3230 $out = $label; 3231 } 3232 3233 return doTag($out, $wraptag, $class); 3234 } 3235 } 3236 3237 // ------------------------------------------------------------- 3238 3239 function keywords($atts) 3240 { 3241 global $thisarticle; 3242 3243 assert_article(); 3244 3245 extract(lAtts(array( 3246 'class' => '', 3247 'break' => ',', 3248 'wraptag' => '', 3249 ), $atts)); 3250 3251 $out = do_list_unique(txpspecialchars($thisarticle['keywords'])); 3252 3253 return doWrap($out, $wraptag, $break, $class); 3254 } 3255 3256 // ------------------------------------------------------------- 3257 3258 function if_keywords($atts, $thing = null) 3259 { 3260 global $thisarticle; 3261 3262 assert_article(); 3263 3264 extract(lAtts(array('keywords' => ''), $atts)); 3265 3266 $condition = empty($keywords) 3267 ? $thisarticle['keywords'] 3268 : array_intersect(do_list($keywords), do_list($thisarticle['keywords'])); 3269 3270 $x = !empty($condition); 3271 return isset($thing) ? parse($thing, $x) : $x; 3272 } 3273 3274 // ------------------------------------------------------------- 3275 3276 function if_article_image($atts, $thing = null) 3277 { 3278 global $thisarticle; 3279 3280 assert_article(); 3281 3282 $x = !empty($thisarticle['article_image']); 3283 return isset($thing) ? parse($thing, $x) : $x; 3284 } 3285 3286 // ------------------------------------------------------------- 3287 3288 function article_image($atts) 3289 { 3290 global $doctype, $thisarticle; 3291 3292 assert_article(); 3293 3294 extract(lAtts(array( 3295 'escape' => true, 3296 'title' => '', 3297 'class' => '', 3298 'html_id' => '', 3299 'style' => '', 3300 'width' => '', 3301 'height' => '', 3302 'thumbnail' => 0, 3303 'wraptag' => '', 3304 'loading' => null, 3305 ), $atts)); 3306 3307 if ($thisarticle['article_image']) { 3308 $image = $thisarticle['article_image']; 3309 } else { 3310 return ''; 3311 } 3312 3313 if (intval($image)) { 3314 $rs = safe_row("*", 'txp_image', "id = ".intval($image)); 3315 3316 if (empty($rs)) { 3317 trigger_error(gTxt('unknown_image')); 3318 3319 return ''; 3320 } 3321 3322 if ($thumbnail && empty($rs['thumbnail'])) { 3323 return ''; 3324 } 3325 3326 $width = ($width == '') ? (($thumbnail) ? $rs['thumb_w'] : $rs['w']) : $width; 3327 $height = ($height == '') ? (($thumbnail) ? $rs['thumb_h'] : $rs['h']) : $height; 3328 3329 extract($rs); 3330 3331 if ($title === true) { 3332 $title = $caption; 3333 } 3334 3335 $out = '<img src="'.imagesrcurl($id, $ext, !empty($atts['thumbnail'])). 3336 '" alt="'.txpspecialchars($alt, ENT_QUOTES, 'UTF-8', false).'"'. 3337 ($title ? ' title="'.txpspecialchars($title, ENT_QUOTES, 'UTF-8', false).'"' : ''); 3338 } else { 3339 $out = '<img src="'.txpspecialchars($image).'" alt=""'. 3340 ($title && $title !== true ? ' title="'.txpspecialchars($title).'"' : ''); 3341 } 3342 3343 if ($loading && $doctype == 'html5' && in_array($loading, array('auto', 'eager', 'lazy'))) { 3344 $out .= ' loading="'.$loading.'"'; 3345 } 3346 3347 $out .= 3348 (($html_id && !$wraptag) ? ' id="'.txpspecialchars($html_id).'"' : ''). 3349 (($class && !$wraptag) ? ' class="'.txpspecialchars($class).'"' : ''). 3350 ($style ? ' style="'.txpspecialchars($style).'"' : ''). 3351 ($width ? ' width="'.(int) $width.'"' : ''). 3352 ($height ? ' height="'.(int) $height.'"' : ''). 3353 ' />'; 3354 3355 return ($wraptag) ? doTag($out, $wraptag, $class, '', $html_id) : $out; 3356 } 3357 3358 // ------------------------------------------------------------- 3359 3360 function search_result_title($atts) 3361 { 3362 return permlink($atts, '<txp:title />'); 3363 } 3364 3365 // ------------------------------------------------------------- 3366 3367 function search_result_excerpt($atts) 3368 { 3369 global $thisarticle, $pretext; 3370 3371 assert_article(); 3372 3373 extract(lAtts(array( 3374 'break' => ' …', // Deprecated in 4.7.0. 3375 'hilight' => 'strong', 3376 'limit' => 5, 3377 'separator' => ' …', 3378 ), $atts)); 3379 3380 if (isset($atts['break'])) { 3381 trigger_error(gTxt('deprecated_attribute_with', array( 3382 '{name}' => 'break', 3383 '{with}' => 'separator', 3384 )), E_USER_NOTICE); 3385 3386 if (!isset($atts['separator'])) { 3387 $separator = $break; 3388 } 3389 } 3390 3391 $m = $pretext['m']; 3392 $q = $pretext['q']; 3393 3394 $quoted = ($q[0] === '"') && ($q[strlen($q) - 1] === '"'); 3395 $q = $quoted ? trim(trim($q, '"')) : trim($q); 3396 3397 $result = preg_replace('/\s+/', ' ', strip_tags(str_replace('><', '> <', $thisarticle['body']))); 3398 3399 if ($quoted || empty($m) || $m === 'exact') { 3400 $regex_search = '/(?:\G|\s).{0,50}'.preg_quote($q, '/').'.{0,50}(?:\s|$)/iu'; 3401 $regex_hilite = '/('.preg_quote($q, '/').')/i'; 3402 } else { 3403 $regex_search = '/(?:\G|\s).{0,50}('.preg_replace('/\s+/', '|', preg_quote($q, '/')).').{0,50}(?:\s|$)/iu'; 3404 $regex_hilite = '/('.preg_replace('/\s+/', '|', preg_quote($q, '/')).')/i'; 3405 } 3406 3407 preg_match_all($regex_search, $result, $concat); 3408 $concat = $concat[0]; 3409 3410 for ($i = 0, $r = array(); $i < min($limit, count($concat)); $i++) { 3411 $r[] = trim($concat[$i]); 3412 } 3413 3414 $concat = join($separator.n, $r); 3415 $concat = preg_replace('/^[^>]+>/U', '', $concat); 3416 $concat = preg_replace($regex_hilite, "<$hilight>$1</$hilight>", $concat); 3417 3418 return ($concat) ? trim($separator.$concat.$separator) : ''; 3419 } 3420 3421 // ------------------------------------------------------------- 3422 3423 function search_result_url($atts) 3424 { 3425 global $thisarticle; 3426 3427 assert_article(); 3428 3429 $l = permlinkurl($thisarticle); 3430 3431 return permlink($atts, $l); 3432 } 3433 3434 // ------------------------------------------------------------- 3435 3436 function search_result_date($atts) 3437 { 3438 assert_article(); 3439 3440 return posted($atts); 3441 } 3442 3443 // ------------------------------------------------------------- 3444 3445 function search_result_count($atts) 3446 { 3447 global $thispage; 3448 3449 if (empty($thispage)) { 3450 return postpone_process(); 3451 } 3452 3453 extract(lAtts(array( 3454 'text' => null, 3455 'pageby' => 1, 3456 ), $atts)); 3457 3458 $by = (int)$pageby or $by = 1; 3459 $t = ceil(@$thispage[$pageby === true ? 'numPages' : 'grand_total']/$by); 3460 3461 if (!isset($text)) { 3462 $text = $pageby === true || $by > 1 ? gTxt($t == 1 ? 'page' : 'pages') : gTxt($t == 1 ? 'article_found' : 'articles_found'); 3463 } 3464 3465 return $t.($text ? ' '.$text : ''); 3466 } 3467 3468 // ------------------------------------------------------------- 3469 3470 function image_index($atts) 3471 { 3472 trigger_error(gTxt('deprecated_tag'), E_USER_NOTICE); 3473 3474 global $c; 3475 3476 lAtts(array( 3477 'break' => br, 3478 'wraptag' => '', 3479 'class' => __FUNCTION__, 3480 'category' => $c, 3481 'limit' => 0, 3482 'offset' => 0, 3483 'sort' => 'name ASC', 3484 ), $atts); 3485 3486 if (!isset($atts['category'])) { 3487 $atts['category'] = $c; 3488 } 3489 3490 if (!isset($atts['class'])) { 3491 $atts['class'] = __FUNCTION__; 3492 } 3493 3494 if ($atts['category']) { 3495 return images($atts); 3496 } 3497 3498 return ''; 3499 } 3500 3501 // ------------------------------------------------------------- 3502 3503 function image_display($atts) 3504 { 3505 trigger_error(gTxt('deprecated_tag'), E_USER_NOTICE); 3506 3507 global $p; 3508 3509 if ($p) { 3510 return image(array('id' => $p)); 3511 } 3512 } 3513 3514 // ------------------------------------------------------------- 3515 3516 function images($atts, $thing = null) 3517 { 3518 global $s, $c, $context, $thisimage, $thisarticle, $thispage, $pretext; 3519 3520 extract(lAtts(array( 3521 'name' => '', 3522 'id' => '', 3523 'category' => '', 3524 'author' => '', 3525 'realname' => '', 3526 'extension' => '', 3527 'thumbnail' => '', 3528 'size' => '', 3529 'auto_detect' => 'article, category, author', 3530 'break' => br, 3531 'wraptag' => '', 3532 'class' => __FUNCTION__, 3533 'html_id' => '', 3534 'form' => '', 3535 'pageby' => '', 3536 'limit' => 0, 3537 'offset' => 0, 3538 'sort' => 'name ASC', 3539 ), $atts)); 3540 3541 $safe_sort = sanitizeForSort($sort); 3542 $where = array(); 3543 $has_content = $thing || $form; 3544 $filters = isset($atts['id']) || isset($atts['name']) || isset($atts['category']) || isset($atts['author']) || isset($atts['realname']) || isset($atts['extension']) || isset($atts['size']) || $thumbnail === '1' || $thumbnail === '0'; 3545 $context_list = (empty($auto_detect) || $filters) ? array() : do_list_unique($auto_detect); 3546 $pageby = ($pageby == 'limit') ? $limit : $pageby; 3547 3548 if ($name) { 3549 $where[] = "name IN ('".join("','", doSlash(do_list_unique($name)))."')"; 3550 } 3551 3552 if ($category) { 3553 $where[] = "category IN ('".join("','", doSlash(do_list_unique($category)))."')"; 3554 } 3555 3556 if ($id) { 3557 $id = join(',', array_map('intval', do_list_unique($id, array(',', '-')))); 3558 $where[] = "id IN ($id)"; 3559 } 3560 3561 if ($author) { 3562 $where[] = "author IN ('".join("','", doSlash(do_list_unique($author)))."')"; 3563 } 3564 3565 if ($realname) { 3566 $authorlist = safe_column("name", 'txp_users', "RealName IN ('".join("','", doArray(doSlash(do_list_unique($realname)), 'urldecode'))."')"); 3567 if ($authorlist) { 3568 $where[] = "author IN ('".join("','", doSlash($authorlist))."')"; 3569 } 3570 } 3571 3572 if ($extension) { 3573 $where[] = "ext IN ('".join("','", doSlash(do_list_unique($extension)))."')"; 3574 } 3575 3576 if ($thumbnail === '0' || $thumbnail === '1') { 3577 $where[] = "thumbnail = $thumbnail"; 3578 } 3579 3580 // Handle aspect ratio filtering. 3581 if ($size === 'portrait') { 3582 $where[] = "h > w"; 3583 } elseif ($size === 'landscape') { 3584 $where[] = "w > h"; 3585 } elseif ($size === 'square') { 3586 $where[] = "w = h"; 3587 } elseif (is_numeric($size)) { 3588 $where[] = "ROUND(w/h, 2) = $size"; 3589 } elseif (strpos($size, ':') !== false) { 3590 $ratio = explode(':', $size); 3591 $ratiow = $ratio[0]; 3592 $ratioh = !empty($ratio[1]) ? $ratio[1] : ''; 3593 3594 if (is_numeric($ratiow) && is_numeric($ratioh)) { 3595 $where[] = "ROUND(w/h, 2) = ".round($ratiow/$ratioh, 2); 3596 } elseif (is_numeric($ratiow)) { 3597 $where[] = "w = $ratiow"; 3598 } elseif (is_numeric($ratioh)) { 3599 $where[] = "h = $ratioh"; 3600 } 3601 } 3602 3603 // If no images are selected, try... 3604 if (!$where && !$filters) { 3605 foreach ($context_list as $ctxt) { 3606 switch ($ctxt) { 3607 case 'article': 3608 // ...the article image field. 3609 if ($thisarticle && !empty($thisarticle['article_image'])) { 3610 if (!is_numeric(str_replace(array(',', '-', ' '), '', $thisarticle['article_image']))) { 3611 return article_image(compact('class', 'html_id', 'wraptag')); 3612 } 3613 3614 $id = join(",", array_map('intval', do_list_unique($thisarticle['article_image'], array(',', '-')))); 3615 3616 // Note: This clause will squash duplicate ids. 3617 $where[] = "id IN ($id)"; 3618 } 3619 break; 3620 case 'category': 3621 // ...the global category in the URL. 3622 if ($context == 'image' && !empty($c)) { 3623 $where[] = "category = '".doSlash($c)."'"; 3624 } 3625 break; 3626 case 'author': 3627 // ...the global author in the URL. 3628 if ($context == 'image' && !empty($pretext['author'])) { 3629 $where[] = "author = '".doSlash($pretext['author'])."'"; 3630 } 3631 break; 3632 } 3633 // Only one context can be processed. 3634 if ($where) { 3635 break; 3636 } 3637 } 3638 } 3639 3640 // Order of ids in 'id' attribute overrides default 'sort' attribute. 3641 if (empty($atts['sort']) && $id) { 3642 $safe_sort = "FIELD(id, $id)"; 3643 } 3644 3645 // If nothing matches from the filterable attributes, output nothing. 3646 if (!$where && $filters) { 3647 return ''; 3648 } 3649 3650 // If no images are filtered, start with all images. 3651 if (!$where) { 3652 $where[] = "1 = 1"; 3653 } 3654 3655 $where = join(" AND ", $where); 3656 3657 // Set up paging if required. 3658 if ($limit && $pageby) { 3659 $pg = (!$pretext['pg']) ? 1 : $pretext['pg']; 3660 $pgoffset = $offset + (($pg - 1) * $pageby); 3661 3662 if (empty($thispage)) { 3663 $grand_total = safe_count('txp_image', $where); 3664 $total = $grand_total - $offset; 3665 $numPages = ($pageby > 0) ? ceil($total / $pageby) : 1; 3666 3667 // Send paging info to txp:newer and txp:older. 3668 $pageout['pg'] = $pg; 3669 $pageout['numPages'] = $numPages; 3670 $pageout['s'] = $s; 3671 $pageout['c'] = $c; 3672 $pageout['context'] = 'image'; 3673 $pageout['grand_total'] = $grand_total; 3674 $pageout['total'] = $total; 3675 $thispage = $pageout; 3676 } 3677 } else { 3678 $pgoffset = $offset; 3679 } 3680 3681 $qparts = array( 3682 $where, 3683 "ORDER BY ".$safe_sort, 3684 ($limit) ? "LIMIT ".intval($pgoffset).", ".intval($limit) : '', 3685 ); 3686 3687 $rs = safe_rows_start("*", 'txp_image', join(' ', $qparts)); 3688 3689 if ($rs) { 3690 $out = array(); 3691 $count = 0; 3692 $last = numRows($rs); 3693 3694 if (isset($thisimage)) { 3695 $old_image = $thisimage; 3696 } 3697 3698 while ($a = nextRow($rs)) { 3699 ++$count; 3700 $thisimage = image_format_info($a); 3701 $thisimage['is_first'] = ($count == 1); 3702 $thisimage['is_last'] = ($count == $last); 3703 3704 if (!$has_content) { 3705 $url = pagelinkurl(array( 3706 'c' => $thisimage['category'], 3707 'context' => 'image', 3708 's' => $s, 3709 'p' => $thisimage['id'], 3710 )); 3711 $src = image_url(array('thumbnail' => '1')); 3712 $thing = href( 3713 '<img src="'.$src.'" alt="'.txpspecialchars($thisimage['alt']).'" />', 3714 $url 3715 ); 3716 } 3717 3718 $out[] = ($thing) ? parse($thing) : parse_form($form); 3719 } 3720 3721 $thisimage = (isset($old_image) ? $old_image : null); 3722 3723 if ($out) { 3724 return doWrap($out, $wraptag, compact('break', 'class', 'html_id')); 3725 } 3726 } 3727 3728 return ''; 3729 } 3730 3731 // ------------------------------------------------------------- 3732 3733 function image_info($atts) 3734 { 3735 extract(lAtts(array( 3736 'name' => '', 3737 'id' => '', 3738 'type' => 'caption', 3739 'escape' => true, 3740 'wraptag' => '', 3741 'class' => '', 3742 'break' => '', 3743 ), $atts)); 3744 3745 $validItems = array('id', 'name', 'category', 'category_title', 'alt', 'caption', 'ext', 'author', 'w', 'h', 'thumb_w', 'thumb_h', 'date'); 3746 $type = do_list($type); 3747 3748 $out = array(); 3749 empty($escape) or $escape = compact('escape'); 3750 3751 if ($imageData = imageFetchInfo($id, $name)) { 3752 $imageData['category_title'] = fetch_category_title($imageData['category'], 'image'); 3753 3754 foreach ($type as $item) { 3755 if (in_array($item, $validItems)) { 3756 if (isset($imageData[$item])) { 3757 $out[] = $escape ? 3758 txp_escape($escape, $imageData[$item]) : $imageData[$item]; 3759 } 3760 } else { 3761 trigger_error(gTxt('invalid_attribute_value', array('{name}' => $item)), E_USER_NOTICE); 3762 } 3763 } 3764 } 3765 3766 return doWrap($out, $wraptag, $break, $class); 3767 } 3768 3769 // ------------------------------------------------------------- 3770 3771 function image_url($atts, $thing = null) 3772 { 3773 extract(lAtts(array( 3774 'name' => '', 3775 'id' => '', 3776 'thumbnail' => 0, 3777 'link' => 'auto', 3778 ), $atts)); 3779 3780 if (($name || $id) && $thing) { 3781 global $thisimage; 3782 $stash = $thisimage; 3783 } 3784 3785 if ($thisimage = imageFetchInfo($id, $name)) { 3786 $url = imagesrcurl($thisimage['id'], $thisimage['ext'], $thumbnail); 3787 $link = ($link == 'auto') ? (($thing) ? 1 : 0) : $link; 3788 $out = ($thing) ? parse($thing) : $url; 3789 $out = ($link) ? href($out, $url) : $out; 3790 } 3791 3792 if (isset($stash)) { 3793 $thisimage = $stash; 3794 } 3795 3796 return isset($out) ? $out : ''; 3797 } 3798 3799 // ------------------------------------------------------------- 3800 3801 function image_author($atts) 3802 { 3803 global $s; 3804 3805 extract(lAtts(array( 3806 'name' => '', 3807 'id' => '', 3808 'link' => 0, 3809 'title' => 1, 3810 'section' => '', 3811 'this_section' => '', 3812 ), $atts)); 3813 3814 if ($imageData = imageFetchInfo($id, $name)) { 3815 $author_name = get_author_name($imageData['author']); 3816 $display_name = txpspecialchars(($title) ? $author_name : $imageData['author']); 3817 3818 $section = ($this_section) ? ($s == 'default' ? '' : $s) : $section; 3819 3820 $author = ($link) 3821 ? href($display_name, pagelinkurl(array( 3822 's' => $section, 3823 'author' => $author_name, 3824 'context' => 'image', 3825 ))) 3826 : $display_name; 3827 3828 return $author; 3829 } 3830 } 3831 3832 // ------------------------------------------------------------- 3833 3834 function image_date($atts) 3835 { 3836 extract(lAtts(array( 3837 'name' => '', 3838 'id' => '', 3839 'format' => '', 3840 ), $atts)); 3841 3842 if ($imageData = imageFetchInfo($id, $name)) { 3843 // Not a typo: use fileDownloadFormatTime() since it's fit for purpose. 3844 $out = fileDownloadFormatTime(array( 3845 'ftime' => $imageData['date'], 3846 'format' => $format, 3847 )); 3848 3849 return $out; 3850 } 3851 } 3852 3853 // ------------------------------------------------------------- 3854 3855 function if_thumbnail($atts, $thing = null) 3856 { 3857 global $thisimage; 3858 3859 assert_image(); 3860 3861 $x = ($thisimage['thumbnail'] == 1); 3862 return isset($thing) ? parse($thing, $x) : $x; 3863 } 3864 3865 // ------------------------------------------------------------- 3866 3867 function if_comments($atts, $thing = null) 3868 { 3869 global $thisarticle; 3870 3871 assert_article(); 3872 3873 $x = ($thisarticle['comments_count'] > 0); 3874 return isset($thing) ? parse($thing, $x) : $x; 3875 } 3876 3877 // ------------------------------------------------------------- 3878 3879 function if_comments_allowed($atts, $thing = null) 3880 { 3881 global $thisarticle; 3882 3883 assert_article(); 3884 3885 $x = checkCommentsAllowed($thisarticle['thisid']); 3886 return isset($thing) ? parse($thing, $x) : $x; 3887 } 3888 3889 // ------------------------------------------------------------- 3890 3891 function if_comments_disallowed($atts, $thing = null) 3892 { 3893 global $thisarticle; 3894 3895 assert_article(); 3896 3897 $x = !checkCommentsAllowed($thisarticle['thisid']); 3898 return isset($thing) ? parse($thing, $x) : $x; 3899 } 3900 3901 // ------------------------------------------------------------- 3902 3903 function if_individual_article($atts, $thing = null) 3904 { 3905 global $is_article_list; 3906 3907 $x = ($is_article_list == false); 3908 return isset($thing) ? parse($thing, $x) : $x; 3909 } 3910 3911 // ------------------------------------------------------------- 3912 3913 function if_article_list($atts, $thing = null) 3914 { 3915 global $is_article_list, $pretext; 3916 static $defaults = array('s', 'c', 'q', 'month', 'author'); 3917 3918 $x = ($is_article_list == true); 3919 3920 if ($x && !empty($atts)) { 3921 extract(lAtts(array('type' => ''), $atts)); 3922 3923 foreach ($type === true ? $defaults : do_list_unique($type) as $q) { 3924 switch ($q) { 3925 case 's': 3926 $x = !empty($pretext['s']) && $pretext['s'] != 'default'; 3927 break; 3928 default: 3929 $x = !empty($pretext[$q]) || !isset($pretext[$q]) && gps($q); 3930 } 3931 3932 if ($x) { 3933 break; 3934 } 3935 } 3936 } 3937 3938 return isset($thing) ? parse($thing, $x) : $x; 3939 } 3940 3941 /** 3942 * Returns article keywords. 3943 * 3944 * @param array $atts Tag attributes 3945 * @return string 3946 */ 3947 3948 function meta_keywords($atts) 3949 { 3950 global $id_keywords; 3951 3952 extract(lAtts(array( 3953 'escape' => null, 3954 'format' => 'meta', // or empty for raw value 3955 'separator' => null, 3956 ), $atts)); 3957 3958 $out = ''; 3959 3960 if ($id_keywords) { 3961 $content = ($escape === null) ? txpspecialchars($id_keywords) : $id_keywords; 3962 3963 if ($separator !== null) { 3964 $content = implode($separator, do_list($content)); 3965 } 3966 3967 if ($format === 'meta') { 3968 // Can't use tag_void() since it escapes its content. 3969 $out = '<meta name="keywords" content="'.$content.'" />'; 3970 } else { 3971 $out = $content; 3972 } 3973 } 3974 3975 return $out; 3976 } 3977 3978 /** 3979 * Returns article, section or category meta description info. 3980 * 3981 * @param array $atts Tag attributes 3982 * @return string 3983 */ 3984 3985 function meta_description($atts) 3986 { 3987 extract(lAtts(array( 3988 'escape' => null, 3989 'format' => 'meta', // or empty for raw value 3990 'type' => null, 3991 ), $atts)); 3992 3993 $out = ''; 3994 $content = getMetaDescription($type); 3995 3996 if ($content) { 3997 $content = ($escape === null ? txpspecialchars($content) : $content); 3998 3999 if ($format === 'meta') { 4000 $out = '<meta name="description" content="'.$content.'" />'; 4001 } else { 4002 $out = $content; 4003 } 4004 } 4005 4006 return $out; 4007 } 4008 4009 /** 4010 * Determines if there is meta description content in the given context. 4011 * 4012 * @param array $atts Tag attributes 4013 * @param string $thing Tag container content 4014 * @return string 4015 */ 4016 4017 function if_description($atts, $thing = null) 4018 { 4019 extract(lAtts(array('type' => null), $atts)); 4020 4021 $content = getMetaDescription($type); 4022 $x = !empty($content); 4023 4024 return isset($thing) ? parse($thing, $x) : $x; 4025 } 4026 4027 4028 // ------------------------------------------------------------- 4029 4030 function meta_author($atts) 4031 { 4032 global $id_author; 4033 4034 extract(lAtts(array( 4035 'escape' => null, 4036 'format' => 'meta', // or empty for raw value 4037 'title' => 0, 4038 ), $atts)); 4039 4040 $out = ''; 4041 4042 if ($id_author) { 4043 $display_name = ($title) ? get_author_name($id_author) : $id_author; 4044 $display_name = ($escape === null) ? txpspecialchars($display_name) : $display_name; 4045 4046 if ($format === 'meta') { 4047 // Can't use tag_void() since it escapes its content. 4048 $out = '<meta name="author" content="'.$display_name.'" />'; 4049 } else { 4050 $out = $display_name; 4051 } 4052 } 4053 4054 return $out; 4055 } 4056 4057 // ------------------------------------------------------------- 4058 4059 function permlink($atts, $thing = null) 4060 { 4061 global $pretext, $thisarticle, $txp_context; 4062 static $lAtts = array( 4063 'class' => '', 4064 'id' => '', 4065 'style' => '', 4066 'title' => '', 4067 'context' => null, 4068 ); 4069 4070 $old_context = $txp_context; 4071 4072 if (!isset($atts['context'])) { 4073 if (empty($txp_context)) { 4074 $atts = lAtts($lAtts, $atts); 4075 } else { 4076 $atts = lAtts($lAtts + $txp_context, $atts); 4077 $txp_context = array_intersect_key($atts, $txp_context); 4078 } 4079 } elseif ($atts['context'] === true) { 4080 $atts = lAtts($lAtts, $atts); 4081 } else { 4082 $extralAtts = array_fill_keys(do_list_unique($atts['context']), null); 4083 $atts = lAtts($lAtts + $extralAtts, $atts); 4084 $extralAtts = array_intersect_key($atts, $extralAtts); 4085 } 4086 4087 $id = $atts['id']; 4088 4089 if (!$id && !assert_article()) { 4090 return; 4091 } 4092 4093 $txp_context = get_context(isset($extralAtts) ? $extralAtts : $atts['context']); 4094 $url = $id ? permlinkurl_id($id) : permlinkurl($thisarticle); 4095 $txp_context = $old_context; 4096 4097 if ($url) { 4098 if ($thing === null) { 4099 return $url; 4100 } 4101 4102 return tag(parse($thing), 'a', array( 4103 'rel' => 'bookmark', 4104 'href' => $url, 4105 'title' => $atts['title'], 4106 'style' => $atts['style'], 4107 'class' => $atts['class'], 4108 )); 4109 } 4110 } 4111 4112 // ------------------------------------------------------------- 4113 4114 function lang() 4115 { 4116 return txpspecialchars(LANG); 4117 } 4118 4119 // ------------------------------------------------------------- 4120 4121 function breadcrumb($atts, $thing = null) 4122 { 4123 global $c, $s, $sitename, $thiscategory, $context; 4124 4125 extract(lAtts(array( 4126 'type' => $context, 4127 'category' => $c, 4128 'section' => $s, 4129 'wraptag' => 'p', 4130 'separator' => ' » ', 4131 'limit' => null, 4132 'offset' => 0, 4133 'link' => 1, 4134 'label' => $sitename, 4135 'title' => '', 4136 'class' => '', 4137 'linkclass' => '', 4138 ), $atts)); 4139 4140 $content = array(); 4141 $label = txpspecialchars($label); 4142 $type = $type === true ? $context : validContext($type); 4143 $section != 'default' or $section = ''; 4144 4145 if ($link && $label) { 4146 $label = doTag($label, 'a', $linkclass, ' href="'.hu.'"'); 4147 } 4148 4149 if (!empty($section)) { 4150 $section_title = ($title) ? fetch_section_title($section) : $section; 4151 $section_title_html = escape_title($section_title); 4152 $content[] = ($link) 4153 ? (doTag($section_title_html, 'a', $linkclass, ' href="'.pagelinkurl(array('s' => $s)).'"')) 4154 : $section_title_html; 4155 } 4156 4157 if (!$category) { 4158 $catpath = array(); 4159 } else { 4160 $catpath = array_reverse(getRootPath($category, $type)); 4161 } 4162 4163 if ($limit || $offset) { 4164 $offset = (int)$offset < 0 ? (int)$offset - 1 : (int)$offset; 4165 $catpath = array_slice($catpath, $offset, isset($limit) ? (int)$limit : null); 4166 } 4167 4168 $oldcategory = isset($thiscategory) ? $thiscategory : null; 4169 4170 foreach ($catpath as $thiscategory) { 4171 $category_title_html = isset($thing) ? parse($thing) : ($title ? escape_title($thiscategory['title']) : $thiscategory['name']); 4172 $content[] = ($link) 4173 ? doTag($category_title_html, 'a', $linkclass, ' href="'.pagelinkurl(array( 4174 'c' => $thiscategory['name'], 4175 'context' => $type, 4176 's' => $section 4177 )).'"') 4178 : $category_title_html; 4179 } 4180 4181 $thiscategory = isset($oldcategory) ? $oldcategory : null; 4182 4183 // Add the label at the end, to prevent breadcrumb for homepage. 4184 if ($content) { 4185 return doWrap($label ? array_merge(array($label), $content) : $content, $wraptag, $separator, $class); 4186 } 4187 } 4188 4189 //------------------------------------------------------------------------ 4190 4191 function if_excerpt($atts, $thing = null) 4192 { 4193 global $thisarticle; 4194 4195 assert_article(); 4196 4197 $x = trim($thisarticle['excerpt']) !== ''; 4198 return isset($thing) ? parse($thing, $x) : $x; 4199 } 4200 4201 // ------------------------------------------------------------- 4202 // Searches use default page. This tag allows you to use different templates if searching 4203 // ------------------------------------------------------------- 4204 4205 function if_search($atts, $thing = null) 4206 { 4207 global $pretext; 4208 4209 $x = !empty($pretext['q']); 4210 return isset($thing) ? parse($thing, $x) : $x; 4211 } 4212 4213 // ------------------------------------------------------------- 4214 4215 function if_search_results($atts, $thing = null) 4216 { 4217 global $pretext, $thispage, $is_article_list; 4218 4219 if (empty($pretext['q']) || empty($thispage)) { 4220 return $is_article_list ? postpone_process() : ''; 4221 } 4222 4223 extract(lAtts(array( 4224 'min' => 1, 4225 'max' => 0, 4226 ), $atts)); 4227 4228 $results = (int) $thispage['grand_total']; 4229 4230 $x = $results >= $min && (!$max || $results <= $max); 4231 return isset($thing) ? parse($thing, $x) : $x; 4232 } 4233 4234 // ------------------------------------------------------------- 4235 4236 function if_category($atts, $thing = null) 4237 { 4238 global $c, $context, $thiscategory; 4239 4240 extract(lAtts(array( 4241 'category' => false, 4242 'type' => false, 4243 'name' => false, 4244 'parent' => 0, 4245 ), $atts)); 4246 4247 if ($category === false) { 4248 $category = $c; 4249 $theType = $context; 4250 } elseif ($category === true) { 4251 $category = empty($thiscategory['name']) ? $c : $thiscategory['name']; 4252 $theType = empty($thiscategory['type']) ? $context : $thiscategory['type']; 4253 } else { 4254 $theType = $type && $type !== true ? validContext($type) : $context; 4255 ($parent || $type === false) or $parent = true; 4256 $category = trim($category); 4257 } 4258 4259 if ($type && $type !== true && $theType !== $type) { 4260 $x = false; 4261 } else { 4262 $parentname = $parent && is_numeric((string)$parent); 4263 $x = $name === false ? !empty($category) : $parentname || in_list($category, $name); 4264 } 4265 4266 if ($x && $parent && $category) { 4267 $path = array_column(getRootPath($category, $theType), 'name'); 4268 4269 if (!$parentname) { 4270 $name = $parent; 4271 $parent = true; 4272 } 4273 4274 $names = do_list_unique($name); 4275 4276 if ($parent === true) { 4277 $x = $path && ($name === false || array_intersect($path, $names)); 4278 } else { 4279 ($parent = (int)$parent) >= 0 or $parent = count($path) + $parent - 1; 4280 $x = isset($path[$parent]) && ($name === false || in_array($path[$parent], $names)); 4281 } 4282 } 4283 4284 return isset($thing) ? parse($thing, $x) : $x; 4285 } 4286 4287 // ------------------------------------------------------------- 4288 4289 function if_article_category($atts, $thing = null) 4290 { 4291 global $thisarticle; 4292 4293 assert_article(); 4294 4295 extract(lAtts(array( 4296 'name' => '', 4297 'number' => '', 4298 ), $atts)); 4299 4300 $cats = array(); 4301 4302 if ($number) { 4303 if (!empty($thisarticle['category'.$number])) { 4304 $cats = array($thisarticle['category'.$number]); 4305 } 4306 } else { 4307 if (!empty($thisarticle['category1'])) { 4308 $cats[] = $thisarticle['category1']; 4309 } 4310 4311 if (!empty($thisarticle['category2'])) { 4312 $cats[] = $thisarticle['category2']; 4313 } 4314 4315 $cats = array_unique($cats); 4316 } 4317 4318 if ($name) { 4319 $cats = array_intersect(do_list($name), $cats); 4320 } 4321 4322 $x = !empty($cats); 4323 4324 return isset($thing) ? parse($thing, $x) : $x; 4325 } 4326 4327 // ------------------------------------------------------------- 4328 4329 function if_section($atts, $thing = null) 4330 { 4331 global $s, $thissection; 4332 4333 extract(lAtts(array('name' => false, 'section' => false), $atts)); 4334 4335 switch ($section) { 4336 case true: $section = isset($thissection) ? $thissection['name'] : $s; break; 4337 case false: $section = $s; break; 4338 } 4339 4340 $section !== 'default' or $section = ''; 4341 $name === false or $name = do_list($name); 4342 4343 if ($section) { 4344 $x = $name === false || in_array($section, $name); 4345 } else { 4346 $x = $name !== false && (in_array('', $name) || in_array('default', $name)); 4347 } 4348 4349 return isset($thing) ? parse($thing, $x) : $x; 4350 } 4351 4352 // ------------------------------------------------------------- 4353 4354 function if_article_section($atts, $thing = null) 4355 { 4356 global $thisarticle, $txp_sections; 4357 4358 assert_article(); 4359 4360 extract(lAtts(array('name' => ''), $atts)); 4361 4362 $section = $thisarticle['section']; 4363 4364 $x = $name === true ? !empty($txp_sections[$section]['page']) : in_list($section, $name); 4365 return isset($thing) ? parse($thing, $x) : $x; 4366 } 4367 4368 // ------------------------------------------------------------- 4369 4370 function php($atts = null, $thing = null) 4371 { 4372 global $is_article_body, $thisarticle, $prefs, $pretext; 4373 4374 $error = null; 4375 4376 if (empty($is_article_body)) { 4377 if (empty($prefs['allow_page_php_scripting'])) { 4378 $error = 'php_code_disabled_page'; 4379 } 4380 } else { 4381 if (!empty($prefs['allow_article_php_scripting'])) { 4382 if (!has_privs('article.php', $thisarticle['authorid'])) { 4383 $error = 'php_code_forbidden_user'; 4384 } 4385 } else { 4386 $error = 'php_code_disabled_article'; 4387 } 4388 } 4389 4390 if ($thing !== null) { 4391 ob_start(); 4392 4393 if ($error) { 4394 trigger_error(gTxt($error)); 4395 } else { 4396 eval($thing); 4397 } 4398 4399 return ob_get_clean(); 4400 } 4401 4402 return empty($error); 4403 } 4404 4405 // ------------------------------------------------------------- 4406 4407 function txp_header($atts) 4408 { 4409 extract(lAtts(array( 4410 'name' => 'Content-Type', 4411 'replace' => 1, 4412 'value' => isset($atts['name']) ? true : 'text/html; charset=utf-8', 4413 'break' => '' 4414 ), $atts)); 4415 4416 $out = set_headers(array($name => $value), $replace); 4417 4418 return $out ? doWrap($out, null, $break) : null; 4419 } 4420 4421 // ------------------------------------------------------------- 4422 4423 function custom_field($atts, $thing = null) 4424 { 4425 global $thisarticle; 4426 4427 assert_article(); 4428 4429 extract(lAtts(array( 4430 'name' => get_pref('custom_1_set'), 4431 'escape' => null, 4432 'default' => '', 4433 ), $atts)); 4434 4435 $name = strtolower($name); 4436 4437 if (!isset($thisarticle[$name])) { 4438 trigger_error(gTxt('field_not_found', array('{name}' => $name)), E_USER_NOTICE); 4439 4440 return ''; 4441 } 4442 4443 if (!isset($thing)) { 4444 $thing = $thisarticle[$name] !== '' ? $thisarticle[$name] : $default; 4445 } 4446 4447 $thing = ($escape === null ? txpspecialchars($thing) : parse($thing)); 4448 4449 return txp_sandbox(array('field' => 'custom_field') + $atts, $thing, false); 4450 } 4451 4452 // ------------------------------------------------------------- 4453 4454 function if_custom_field($atts, $thing = null) 4455 { 4456 global $thisarticle; 4457 4458 assert_article(); 4459 4460 extract($atts = lAtts(array( 4461 'name' => get_pref('custom_1_set'), 4462 'value' => null, 4463 'match' => 'exact', 4464 'separator' => '', 4465 ), $atts)); 4466 4467 $name = strtolower($name); 4468 4469 if (!isset($thisarticle[$name])) { 4470 trigger_error(gTxt('field_not_found', array('{name}' => $name)), E_USER_NOTICE); 4471 4472 return ''; 4473 } 4474 4475 if ($value !== null) { 4476 $cond = txp_match($atts, $thisarticle[$name]); 4477 } else { 4478 $cond = ($thisarticle[$name] !== ''); 4479 } 4480 4481 return isset($thing) ? parse($thing, !empty($cond)) : !empty($cond); 4482 } 4483 4484 // ------------------------------------------------------------- 4485 4486 function site_url($atts) 4487 { 4488 extract(lAtts(array( 4489 'type' => '', 4490 ), $atts)); 4491 4492 return $type === 'admin' ? ahu : hu; 4493 } 4494 4495 // ------------------------------------------------------------- 4496 4497 function error_message() 4498 { 4499 global $txp_error_message; 4500 4501 return $txp_error_message; 4502 } 4503 4504 // ------------------------------------------------------------- 4505 4506 function error_status() 4507 { 4508 global $txp_error_status; 4509 4510 return $txp_error_status; 4511 } 4512 4513 // ------------------------------------------------------------- 4514 4515 function if_status($atts, $thing = null) 4516 { 4517 global $pretext, $txp_error_code; 4518 4519 extract(lAtts(array('status' => '200'), $atts)); 4520 4521 $page_status = $txp_error_code 4522 ? $txp_error_code 4523 : $pretext['status']; 4524 4525 $x = $status == $page_status; 4526 return isset($thing) ? parse($thing, $x) : $x; 4527 } 4528 4529 // ------------------------------------------------------------- 4530 4531 function page_url($atts, $thing = null) 4532 { 4533 global $pretext, $txp_context; 4534 static $specials = null, $internals = array('id', 's', 'c', 'context', 'q', 'm', 'p', 'month', 'author', 'f'), 4535 $lAtts = array( 4536 'type' => null, 4537 'default' => false, 4538 'escape' => null, 4539 'context' => null 4540 ); 4541 4542 isset($specials) or $specials = array( 4543 'admin_root' => ahu, 4544 'images_root' => ihu.get_pref('img_dir'), 4545 'themes_root' => hu.get_pref('skin_dir'), 4546 'theme_path' => hu.get_pref('skin_dir').'/'.$pretext['skin'], 4547 'theme' => $pretext['skin'], 4548 ); 4549 4550 $old_context = $txp_context; 4551 4552 if (!isset($atts['context'])) { 4553 if (empty($txp_context)) { 4554 $atts = lAtts($lAtts, $atts); 4555 } else { 4556 $atts = lAtts($lAtts + $txp_context, $atts); 4557 $txp_context = array_intersect_key($atts, $txp_context); 4558 } 4559 } elseif ($atts['context'] === true) { 4560 $atts = lAtts($lAtts, $atts); 4561 } else { 4562 $extralAtts = array_fill_keys(do_list_unique($atts['context']), null); 4563 $atts = lAtts($lAtts + $extralAtts, $atts); 4564 $extralAtts = array_intersect_key($atts, $extralAtts); 4565 } 4566 4567 extract($atts, EXTR_SKIP); 4568 4569 $txp_context = get_context(isset($extralAtts) ? $extralAtts : $context, $internals); 4570 4571 if ($default !== false) { 4572 if ($default === true) { 4573 if (isset($type)) { 4574 unset($txp_context[$type]); 4575 } else { 4576 $txp_context = array(); 4577 } 4578 } elseif (in_array($type, $internals)) { 4579 $txp_context[$type] = $default; 4580 } 4581 } 4582 4583 if (!isset($type)) { 4584 $type = 'request_uri'; 4585 } 4586 4587 if (isset($thing)) { 4588 $out = parse($thing); 4589 } elseif (isset($context)) { 4590 $out = pagelinkurl($txp_context); 4591 $escape === null or $out = str_replace('&', '&', $out); 4592 } elseif (isset($specials[$type])) { 4593 $out = $specials[$type]; 4594 } elseif ($type == 'pg' && $pretext['pg'] == '') { 4595 $out = '1'; 4596 } elseif (isset($pretext[$type]) && is_bool($default)) { 4597 $out = $escape === null ? txpspecialchars($pretext[$type]) : $pretext[$type]; 4598 } else { 4599 $out = gps($type, $default); 4600 !is_array($out) or $out = implode(',', $out); 4601 $escape !== null or $out = txpspecialchars($out); 4602 } 4603 4604 $txp_context = $old_context; 4605 4606 return $out; 4607 } 4608 4609 // ------------------------------------------------------------- 4610 4611 function if_different($atts, $thing = null) 4612 { 4613 static $last, $tested; 4614 4615 extract(lAtts(array( 4616 'test' => null, 4617 'not' => '' 4618 ), $atts)); 4619 4620 $key = md5($thing); 4621 $out = isset($test) ? $test : parse($thing); 4622 4623 if (isset($test)) { 4624 if ($different = !isset($tested[$key]) || $out != $tested[$key]) { 4625 $tested[$key] = $out; 4626 } 4627 } else { 4628 if ($different = !isset($last[$key]) || $out != $last[$key]) { 4629 $last[$key] = $out; 4630 } 4631 } 4632 4633 $condition = $not ? !$different : $different; 4634 4635 return isset($test) ? 4636 parse($thing, $condition) : 4637 ($condition ? $out : parse($thing, false)); 4638 } 4639 4640 // ------------------------------------------------------------- 4641 4642 function if_first($atts, $thing = null, $type = 'article') 4643 { 4644 global ${"this$type"}; 4645 4646 $assert = 'assert_'.$type; 4647 $assert(); 4648 4649 $x = !empty(${"this$type"}['is_first']); 4650 return isset($thing) ? parse($thing, $x) : $x; 4651 } 4652 4653 // ------------------------------------------------------------- 4654 4655 function if_last($atts, $thing = null, $type = 'article') 4656 { 4657 global ${"this$type"}; 4658 4659 $assert = 'assert_'.$type; 4660 $assert(); 4661 4662 $x = !empty(${"this$type"}['is_last']); 4663 return isset($thing) ? parse($thing, $x) : $x; 4664 } 4665 4666 // ------------------------------------------------------------- 4667 4668 function if_plugin($atts, $thing = null) 4669 { 4670 global $plugins, $plugins_ver; 4671 4672 extract(lAtts(array( 4673 'name' => '', 4674 'version' => '', 4675 ), $atts)); 4676 4677 $x = @in_array($name, $plugins) && (!$version || version_compare($plugins_ver[$name], $version) >= 0); 4678 return isset($thing) ? parse($thing, $x) : $x; 4679 } 4680 4681 // ------------------------------------------------------------- 4682 4683 function file_download_list($atts, $thing = null) 4684 { 4685 global $s, $c, $context, $thisfile, $thispage, $pretext; 4686 4687 extract(lAtts(array( 4688 'break' => br, 4689 'category' => '', 4690 'author' => '', 4691 'realname' => '', 4692 'auto_detect' => 'category, author', 4693 'class' => __FUNCTION__, 4694 'form' => 'files', 4695 'id' => '', 4696 'pageby' => '', 4697 'limit' => 10, 4698 'offset' => 0, 4699 'month' => '', 4700 'time' => null, 4701 'sort' => 'filename asc', 4702 'wraptag' => '', 4703 'status' => STATUS_LIVE, 4704 ), $atts)); 4705 4706 if (!is_numeric($status)) { 4707 $status = getStatusNum($status); 4708 } 4709 4710 // Note: status treated slightly differently. 4711 $where = array(); 4712 $filters = isset($atts['id']) || isset($atts['category']) || isset($atts['author']) || isset($atts['realname']) || isset($atts['status']); 4713 $context_list = (empty($auto_detect) || $filters) ? array() : do_list_unique($auto_detect); 4714 $pageby = ($pageby == 'limit') ? $limit : $pageby; 4715 4716 if ($category) { 4717 $where[] = "category IN ('".join("','", doSlash(do_list_unique($category)))."')"; 4718 } 4719 4720 $ids = $id ? array_map('intval', do_list_unique($id, array(',', '-'))) : array(); 4721 4722 if ($ids) { 4723 $where[] = "id IN ('".join("','", $ids)."')"; 4724 } 4725 4726 if ($author) { 4727 $where[] = "author IN ('".join("','", doSlash(do_list_unique($author)))."')"; 4728 } 4729 4730 if ($realname) { 4731 $authorlist = safe_column("name", 'txp_users', "RealName IN ('".join("','", doArray(doSlash(do_list_unique($realname)), 'urldecode'))."')"); 4732 if ($authorlist) { 4733 $where[] = "author IN ('".join("','", doSlash($authorlist))."')"; 4734 } 4735 } 4736 4737 // If no files are selected, try... 4738 if (!$where && !$filters) { 4739 foreach ($context_list as $ctxt) { 4740 switch ($ctxt) { 4741 case 'category': 4742 // ...the global category in the URL. 4743 if ($context == 'file' && !empty($c)) { 4744 $where[] = "category = '".doSlash($c)."'"; 4745 } 4746 break; 4747 case 'author': 4748 // ...the global author in the URL. 4749 if ($context == 'file' && !empty($pretext['author'])) { 4750 $where[] = "author = '".doSlash($pretext['author'])."'"; 4751 } 4752 break; 4753 } 4754 4755 // Only one context can be processed. 4756 if ($where) { 4757 break; 4758 } 4759 } 4760 } 4761 4762 if ($status) { 4763 $where[] = "status = '".doSlash($status)."'"; 4764 } elseif (!$where && $filters) { 4765 // If nothing matches, output nothing. 4766 return ''; 4767 } 4768 4769 if ($time === null || $time || $month) { 4770 $where[] = buildTimeSql($month, $time === null ? 'past' : $time, 'created'); 4771 } 4772 4773 $where = join(" AND ", $where); 4774 4775 // Set up paging if required. 4776 if ($limit && $pageby) { 4777 $pg = (!$pretext['pg']) ? 1 : $pretext['pg']; 4778 $pgoffset = $offset + (($pg - 1) * $pageby); 4779 4780 if (empty($thispage)) { 4781 $grand_total = safe_count('txp_file', $where); 4782 $total = $grand_total - $offset; 4783 $numPages = ($pageby > 0) ? ceil($total/$pageby) : 1; 4784 4785 // Send paging info to txp:newer and txp:older. 4786 $pageout['pg'] = $pg; 4787 $pageout['numPages'] = $numPages; 4788 $pageout['s'] = $s; 4789 $pageout['c'] = $c; 4790 $pageout['context'] = 'file'; 4791 $pageout['grand_total'] = $grand_total; 4792 $pageout['total'] = $total; 4793 $thispage = $pageout; 4794 } 4795 } else { 4796 $pgoffset = $offset; 4797 } 4798 4799 // Preserve order of custom file ids unless 'sort' attribute is set. 4800 if (!empty($ids) && empty($atts['sort'])) { 4801 $safe_sort = "FIELD(id, ".join(',', $ids).")"; 4802 } else { 4803 $safe_sort = sanitizeForSort($sort); 4804 } 4805 4806 $qparts = array( 4807 "ORDER BY ".$safe_sort, 4808 ($limit) ? "LIMIT ".intval($pgoffset).", ".intval($limit) : '', 4809 ); 4810 4811 $rs = safe_rows_start("*", 'txp_file', $where.' '.join(' ', $qparts)); 4812 4813 if ($rs) { 4814 $count = 0; 4815 $last = numRows($rs); 4816 $out = array(); 4817 4818 while ($a = nextRow($rs)) { 4819 ++$count; 4820 $thisfile = file_download_format_info($a); 4821 $thisfile['is_first'] = ($count == 1); 4822 $thisfile['is_last'] = ($count == $last); 4823 4824 $out[] = ($thing) ? parse($thing) : parse_form($form); 4825 4826 $thisfile = ''; 4827 } 4828 4829 if ($out) { 4830 return doWrap($out, $wraptag, $break, $class); 4831 } 4832 } 4833 4834 return ''; 4835 } 4836 4837 // ------------------------------------------------------------- 4838 4839 function file_download($atts, $thing = null) 4840 { 4841 global $thisfile; 4842 4843 extract(lAtts(array( 4844 'filename' => '', 4845 'form' => 'files', 4846 'id' => '', 4847 ), $atts)); 4848 4849 $from_form = false; 4850 4851 if ($id) { 4852 $thisfile = fileDownloadFetchInfo('id = '.intval($id).' and created <= '.now('created')); 4853 } elseif ($filename) { 4854 $thisfile = fileDownloadFetchInfo("filename = '".doSlash($filename)."' and created <= ".now('created')); 4855 } else { 4856 assert_file(); 4857 4858 $from_form = true; 4859 } 4860 4861 if ($thisfile) { 4862 $out = ($thing) ? parse($thing) : parse_form($form); 4863 4864 // Cleanup: this wasn't called from a form, so we don't want this 4865 // value remaining. 4866 if (!$from_form) { 4867 $thisfile = ''; 4868 } 4869 4870 return $out; 4871 } 4872 } 4873 4874 // ------------------------------------------------------------- 4875 4876 function file_download_link($atts, $thing = null) 4877 { 4878 global $thisfile; 4879 4880 extract(lAtts(array( 4881 'filename' => '', 4882 'id' => '', 4883 ), $atts)); 4884 4885 $from_form = false; 4886 4887 if ($id) { 4888 $thisfile = fileDownloadFetchInfo('id = '.intval($id).' and created <= '.now('created')); 4889 } elseif ($filename) { 4890 $thisfile = fileDownloadFetchInfo("filename = '".doSlash($filename)."' and created <= ".now('created')); 4891 } else { 4892 assert_file(); 4893 4894 $from_form = true; 4895 } 4896 4897 if ($thisfile) { 4898 $url = filedownloadurl($thisfile['id'], $thisfile['filename']); 4899 4900 $out = ($thing) ? href(parse($thing), $url) : $url; 4901 4902 // Cleanup: this wasn't called from a form, so we don't want this 4903 // value remaining 4904 if (!$from_form) { 4905 $thisfile = ''; 4906 } 4907 4908 return $out; 4909 } 4910 } 4911 4912 // ------------------------------------------------------------- 4913 4914 function file_download_size($atts) 4915 { 4916 global $thisfile; 4917 4918 assert_file(); 4919 4920 extract(lAtts(array( 4921 'decimals' => 2, 4922 'format' => '', 4923 ), $atts)); 4924 4925 if (is_numeric($decimals) && $decimals >= 0) { 4926 $decimals = intval($decimals); 4927 } else { 4928 $decimals = 2; 4929 } 4930 4931 if (isset($thisfile['size'])) { 4932 $format_unit = strtolower(substr($format, 0, 1)); 4933 4934 return format_filesize($thisfile['size'], $decimals, $format_unit); 4935 } else { 4936 return ''; 4937 } 4938 } 4939 4940 // ------------------------------------------------------------- 4941 4942 function file_download_time($atts, $thing = null, $time = 'created') 4943 { 4944 global $thisfile; 4945 4946 assert_file(); 4947 4948 extract(lAtts(array('format' => ''), $atts)); 4949 4950 if (!empty($thisfile[$time])) { 4951 return fileDownloadFormatTime(array( 4952 'ftime' => $thisfile[$time], 4953 'format' => $format, 4954 )); 4955 } 4956 } 4957 4958 // ------------------------------------------------------------- 4959 4960 function file_download_id() 4961 { 4962 global $thisfile; 4963 4964 assert_file(); 4965 4966 return $thisfile['id']; 4967 } 4968 4969 // ------------------------------------------------------------- 4970 4971 function file_download_name($atts) 4972 { 4973 global $thisfile; 4974 4975 assert_file(); 4976 4977 extract(lAtts(array('title' => 0), $atts)); 4978 4979 return ($title) ? $thisfile['title'] : $thisfile['filename']; 4980 } 4981 4982 // ------------------------------------------------------------- 4983 4984 function file_download_category($atts) 4985 { 4986 global $thisfile; 4987 4988 assert_file(); 4989 4990 extract(lAtts(array('title' => 0), $atts)); 4991 4992 if ($thisfile['category']) { 4993 $category = ($title) 4994 ? fetch_category_title($thisfile['category'], 'file') 4995 : $thisfile['category']; 4996 4997 return $category; 4998 } 4999 } 5000 5001 // ------------------------------------------------------------- 5002 5003 function file_download_author($atts) 5004 { 5005 global $thisfile, $s; 5006 5007 assert_file(); 5008 5009 extract(lAtts(array( 5010 'link' => 0, 5011 'title' => 1, 5012 'section' => '', 5013 'this_section' => '', 5014 ), $atts)); 5015 5016 if ($thisfile['author']) { 5017 $author_name = get_author_name($thisfile['author']); 5018 $display_name = txpspecialchars(($title) ? $author_name : $thisfile['author']); 5019 5020 $section = ($this_section) ? ($s == 'default' ? '' : $s) : $section; 5021 5022 $author = ($link) 5023 ? href($display_name, pagelinkurl(array( 5024 's' => $section, 5025 'author' => $author_name, 5026 'context' => 'file', 5027 ))) 5028 : $display_name; 5029 5030 return $author; 5031 } 5032 } 5033 5034 // ------------------------------------------------------------- 5035 5036 function file_download_downloads() 5037 { 5038 global $thisfile; 5039 5040 assert_file(); 5041 5042 return $thisfile['downloads']; 5043 } 5044 5045 // ------------------------------------------------------------- 5046 5047 function file_download_description($atts) 5048 { 5049 global $thisfile; 5050 5051 assert_file(); 5052 5053 extract(lAtts(array('escape' => null), $atts)); 5054 5055 if ($thisfile['description']) { 5056 return ($escape === null) 5057 ? txpspecialchars($thisfile['description']) 5058 : $thisfile['description']; 5059 } 5060 } 5061 5062 // ------------------------------------------------------------- 5063 5064 function hide($atts = array(), $thing = null) 5065 { 5066 if (!isset($atts['process'])) { 5067 return ''; 5068 } 5069 5070 global $pretext; 5071 5072 extract(lAtts(array('process' => null), $atts)); 5073 5074 if (!$process) { 5075 return $pretext['secondpass'] < get_pref('secondpass', 1) ? postpone_process() : $thing; 5076 } elseif (is_numeric($process)) { 5077 return abs($process) > $pretext['secondpass'] + 1 ? 5078 postpone_process($process) : 5079 ($process > 0 ? parse($thing) : '<txp:hide>'.parse($thing).'</txp:hide>'); 5080 } elseif ($process) { 5081 parse($thing); 5082 } 5083 5084 return ''; 5085 } 5086 5087 // ------------------------------------------------------------- 5088 5089 function rsd() 5090 { 5091 global $prefs; 5092 5093 trigger_error(gTxt('deprecated_tag'), E_USER_NOTICE); 5094 5095 return ($prefs['enable_xmlrpc_server']) ? '<link rel="EditURI" type="application/rsd+xml" title="RSD" href="'.hu.'rpc/" />' : ''; 5096 } 5097 5098 // ------------------------------------------------------------- 5099 5100 function variable($atts, $thing = null) 5101 { 5102 global $variable, $trace; 5103 5104 $set = isset($thing) || isset($atts['value']) || isset($atts['add']) || isset($atts['reset']) ? '' : null; 5105 5106 extract(lAtts(array( 5107 'escape' => $set, 5108 'name' => '', 5109 'value' => null, 5110 'add' => null, 5111 'reset' => null, 5112 'separator' => null, 5113 'output' => null 5114 ), $atts)); 5115 5116 $var = isset($variable[$name]) ? $variable[$name] : null; 5117 5118 if (empty($name)) { 5119 trigger_error(gTxt('variable_name_empty')); 5120 } elseif ($set === null && !isset($var)) { 5121 $trace->log("[<txp:variable>: Unknown variable '$name']"); 5122 } else { 5123 if ($add === true) { 5124 empty($thing) or $thing = parse($thing); 5125 5126 if (!isset($value)) { 5127 $add = isset($thing) ? $thing : 1; 5128 } elseif ($value === true) { 5129 $add = $var; 5130 !isset($thing) or $var = $thing; 5131 } else { 5132 $add = isset($thing) ? $thing : $var; 5133 $var = $value; 5134 } 5135 } elseif ($value === true) { 5136 isset($var) or $var = (isset($thing) ? parse($thing) : null); 5137 } else { 5138 $var = isset($value) ? 5139 $value : 5140 (isset($thing) ? parse($thing) : $var); 5141 } 5142 5143 if (isset($add)) { 5144 if (!isset($separator) && is_numeric($add) && (empty($var) || is_numeric($var))) { 5145 $var += $add; 5146 } else { 5147 $var .= ($var ? $separator : '').$add; 5148 } 5149 } 5150 } 5151 5152 if ($set !== null) { 5153 $var = $escape ? txp_escape(array('escape' => $escape), $var) : $var; 5154 5155 if (isset($reset)) { 5156 $variable[$name] = $reset === true ? null : $reset; 5157 isset($output) or $output = 1; 5158 } else { 5159 $variable[$name] = $var; 5160 } 5161 } else { 5162 isset($output) or $output = 1; 5163 } 5164 5165 return !$output ? '' : ((int)$output ? $var : txp_escape(array('escape' => $output), $var)); 5166 } 5167 5168 // ------------------------------------------------------------- 5169 5170 function if_variable($atts, $thing = null) 5171 { 5172 global $variable; 5173 5174 extract($atts = lAtts(array( 5175 'name' => '', 5176 'value' => false, 5177 'match' => 'exact', 5178 'separator' => '', 5179 ), $atts)); 5180 5181 if (empty($name)) { 5182 trigger_error(gTxt('variable_name_empty')); 5183 5184 return ''; 5185 } 5186 5187 if (isset($variable[$name])) { 5188 $x = $value === false ? true : txp_match($atts, $variable[$name]); 5189 } else { 5190 $x = false; 5191 } 5192 5193 return isset($thing) ? parse($thing, $x) : $x; 5194 } 5195 5196 // ------------------------------------------------------------- 5197 5198 function if_request($atts, $thing = null) 5199 { 5200 extract($atts = lAtts(array( 5201 'name' => '', 5202 'type' => 'request', 5203 'value' => null, 5204 'match' => 'exact', 5205 'separator' => '', 5206 ), $atts)); 5207 5208 switch ($type = strtoupper($type)) { 5209 case 'REQUEST': 5210 case 'GET': 5211 case 'POST': 5212 case 'COOKIE': 5213 case 'SERVER': 5214 global ${'_'.$type}; 5215 $what = isset(${'_'.$type}[$name]) ? ${'_'.$type}[$name] : null; 5216 $x = txp_match($atts, $what); 5217 break; 5218 case 'NAME': 5219 $x = txp_match($atts, $name); 5220 break; 5221 default: 5222 $x = false; 5223 trigger_error(gTxt('invalid_attribute_value', array('{name}' => 'type')), E_USER_NOTICE); 5224 } 5225 5226 return isset($thing) ? parse($thing, $x) : $x; 5227 } 5228 5229 // ------------------------------------------------------------- 5230 5231 function txp_eval($atts, $thing = null) 5232 { 5233 global $prefs, $txp_tag, $txp_atts; 5234 static $xpath = null, $functions = null; 5235 5236 unset($txp_atts['evaluate']); 5237 $staged = null; 5238 5239 extract(lAtts(array( 5240 'query' => null, 5241 'test' => !isset($atts['query']), 5242 ), $atts)); 5243 5244 if (!isset($thing) && isset($atts['test']) && $atts['test'] !== true) { 5245 $thing = $atts['test']; 5246 $test = null; 5247 } 5248 5249 if (!isset($query) || $query === true) { 5250 $x = true; 5251 } elseif (!($query = trim($query))) { 5252 $x = $query; 5253 } elseif (class_exists('DOMDocument')) { 5254 if (!isset($xpath)) { 5255 $xpath = new DOMXpath(new DOMDocument); 5256 $functions = do_list_unique(get_pref('txp_evaluate_functions')); 5257 $_functions = array(); 5258 5259 foreach ($functions as $function) { 5260 list($key, $val) = do_list($function, '=') + array(null, $function); 5261 5262 if (function_exists($val)) { 5263 $_functions[$key] = $val; 5264 } 5265 } 5266 5267 if ($_functions) { 5268 $functions = implode('|', array_keys($_functions)); 5269 $xpath->registerNamespace('php', 'http://php.net/xpath'); 5270 $xpath->registerPHPFunctions($_functions); 5271 } else { 5272 $functions = false; 5273 } 5274 5275 $prefs['_txp_evaluate_functions'] = $_functions; 5276 } 5277 5278 if ($functions) { 5279 $query = preg_replace_callback('/\b('.$functions.')\s*\(\s*(\)?)/', 5280 function ($match) { 5281 global $prefs; 5282 $function = empty($prefs['_txp_evaluate_functions'][$match[1]]) ? $match[1] : $prefs['_txp_evaluate_functions'][$match[1]]; 5283 5284 return "php:function('$function'".($match[2] ? ')' : ','); 5285 }, 5286 $query 5287 ); 5288 } 5289 5290 if (strpos($query, '<+>') !== false) { 5291 $staged = $x = true; 5292 } else { 5293 $x = $xpath->evaluate($query); 5294 5295 if ($x instanceof DOMNodeList) { 5296 $x = $x->length; 5297 } 5298 } 5299 } else { 5300 trigger_error(gTxt('missing_dom_extension')); 5301 return ''; 5302 } 5303 5304 if (!isset($thing)) { 5305 return $test === true ? !empty($x) : $x; 5306 } elseif (empty($x)) { 5307 return parse($thing, false); 5308 } 5309 5310 $txp_atts['evaluate'] = $test; 5311 $x = parse($thing); 5312 unset($txp_atts['evaluate']); 5313 5314 if ($txp_tag) { 5315 if ($staged) { 5316 $quoted = txp_escape(array('escape' => 'quote'), $x); 5317 $query = str_replace('<+>', $quoted, $query); 5318 $query = $xpath->evaluate($query); 5319 $query = $query instanceof DOMNodeList ? $query->length : $query; 5320 5321 if (empty($query)) { 5322 return parse($thing, false); 5323 } else { 5324 return $test === true ? $x : $query; 5325 } 5326 } 5327 } else { 5328 $txp_atts = null; 5329 $x = parse($thing, false); 5330 } 5331 5332 return $test === null && $query !== true ? !empty($x) : $x; 5333 } 5334 5335 // ------------------------------------------------------------- 5336 5337 function txp_escape($atts, $thing = '') 5338 { 5339 global $prefs; 5340 static $textile = null, $decimal = null, $spellout = null, $ordinal = null, 5341 $mb = null, $LocaleInfo = null, $tr = array("'" => "',\"'\",'"); 5342 5343 if (empty($atts['escape'])) { 5344 return $thing; 5345 } 5346 5347 extract(lAtts(array('escape' => true), $atts, false)); 5348 5349 $escape = $escape === true ? array('html') : do_list(strtolower($escape)); 5350 $filter = $tidy = false; 5351 5352 isset($mb) or $mb = extension_loaded('mbstring') ? 'mb_' : ''; 5353 5354 foreach ($escape as $attr) { 5355 switch ($attr) { 5356 case 'html': 5357 $thing = txpspecialchars($thing); 5358 break; 5359 case 'url': 5360 $thing = $tidy ? rawurlencode($thing) : urlencode($thing); 5361 break; 5362 case 'js': 5363 $thing = escape_js($thing); 5364 break; 5365 case 'json': 5366 $thing = substr(json_encode($thing, JSON_UNESCAPED_UNICODE), 1, -1); 5367 break; 5368 case 'integer': 5369 !$filter or $thing = do_list($thing); 5370 // no break 5371 case 'number': case 'float': case 'spell': case 'ordinal': 5372 isset($LocaleInfo) or $LocaleInfo = localeconv(); 5373 $dec_point = $LocaleInfo['decimal_point']; 5374 $thousands_sep = utf8_encode($LocaleInfo['thousands_sep']); 5375 !$thousands_sep or $thing = str_replace($thousands_sep, '', $thing); 5376 $dec_point == '.' or $thing = str_replace($dec_point, '.', $thing); 5377 5378 if (is_array($thing)) {// integer mode 5379 $value = $tidy ? 5380 array_map(function ($str) { 5381 return filter_var($str, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION); 5382 }, $thing) : 5383 $thing; 5384 } else { 5385 $value = floatval($tidy ? 5386 filter_var($thing, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION) : 5387 $thing 5388 ); 5389 } 5390 5391 switch ($attr) { 5392 case 'integer': 5393 $thing = $filter ? implode(',', array_filter(array_map('intval', $value))) : intval($value); 5394 break; 5395 case 'number': 5396 isset($decimal) 5397 or !($decimal = class_exists('NumberFormatter')) 5398 or $decimal = new NumberFormatter(LANG, NumberFormatter::DECIMAL); 5399 5400 if ($decimal) { 5401 $thing = $decimal->format($value); 5402 } else { 5403 $thing = number_format($value, 3, $dec_point, $thousands_sep); 5404 $thing = rtrim(rtrim($thing, '0'), $dec_point); 5405 } 5406 break; 5407 case 'spell': 5408 isset($spellout) 5409 or !($spellout = class_exists('NumberFormatter')) 5410 or $spellout = new NumberFormatter(LANG, NumberFormatter::SPELLOUT); 5411 5412 if ($spellout && ($tidy || is_numeric($thing))) { 5413 $thing = $spellout->format($value); 5414 } 5415 break; 5416 case 'ordinal': 5417 isset($ordinal) 5418 or !($ordinal = class_exists('NumberFormatter')) 5419 or $ordinal = new NumberFormatter(LANG, NumberFormatter::ORDINAL); 5420 5421 if ($ordinal && ($tidy || is_numeric($thing))) { 5422 $thing = $ordinal->format($value); 5423 } 5424 break; 5425 default: 5426 $thing = $dec_point != '.' ? str_replace($dec_point, '.', $value) : $value; 5427 } 5428 break; 5429 case 'tags': 5430 $thing = strip_tags($thing); 5431 break; 5432 case 'upper': case 'lower': 5433 $function = ($mb && mb_detect_encoding($thing) != 'ASCII' ? 'mb_strto' : 'strto').$attr; 5434 $thing = $function($thing); 5435 break; 5436 case 'title': 5437 $thing = $mb && mb_detect_encoding($thing) != 'ASCII' ? 5438 mb_convert_case($thing, MB_CASE_TITLE) : ucwords($thing); 5439 break; 5440 case 'trim': case 'ltrim': case 'rtrim': 5441 $filter = true; 5442 $thing = is_int($thing) ? ($thing ? $thing : '') : $attr($thing); 5443 break; 5444 case 'tidy': 5445 $thing = preg_replace('/\s+/', ' ', trim($thing)); 5446 $tidy = true; 5447 break; 5448 case 'textile': 5449 if ($textile === null) { 5450 $textile = Txp::get('\Textpattern\Textile\Parser'); 5451 } 5452 5453 $thing = $textile->parse($tidy ? ' '.$thing : $thing); 5454 !$tidy or $thing = ltrim($thing); 5455 break; 5456 case 'quote': 5457 $thing = strpos($thing, "'") === false ? "'$thing'" : "concat('".strtr($thing, $tr)."')"; 5458 break; 5459 default: 5460 $thing = preg_replace('@</?'.($tidy ? preg_quote($attr) : $attr).'\b[^<>]*>@Usi', '', $thing); 5461 } 5462 } 5463 5464 return $thing; 5465 } 5466 5467 // ------------------------------------------------------------- 5468 5469 function txp_wraptag($atts, $thing = '') 5470 { 5471 extract(lAtts(array( 5472 'label' => '', 5473 'labeltag' => '', 5474 'wraptag' => '', 5475 'class' => '', 5476 'html_id' => '', 5477 'trim' => null, 5478 'replace' => null, 5479 'default' => null, 5480 ), $atts, false)); 5481 5482 !isset($default) or trim($thing) !== '' or $thing = $default; 5483 5484 if ($replace === true) { 5485 $sep = isset($trim) && $trim !== true ? $trim : ','; 5486 $thing = isset($trim) ? do_list_unique($thing, $sep, $trim === true ? TEXTPATTERN_STRIP_EMPTY : TEXTPATTERN_STRIP_EMPTY_STRING) : array_unique(explode($sep, $thing)); 5487 $thing = implode($sep, $thing); 5488 } elseif (isset($trim)) { 5489 if ($trim === true) { 5490 $thing = isset($replace) ? preg_replace('/\s+/', $replace, trim($thing)) : trim($thing); 5491 } elseif (strlen($trim) > 2 && preg_match('/([^\\\w\s]).+\1[UsiAmuS]*$/As', $trim)) { 5492 $thing = preg_replace($trim, $replace, $thing); 5493 } else { 5494 $thing = isset($replace) ? str_replace($trim, $replace, $thing) : trim($thing, $trim); 5495 } 5496 } 5497 5498 $thing = $wraptag && trim($thing) !== '' ? doTag($thing, $wraptag, $class, '', '', $html_id) : $thing; 5499 5500 return $label && trim($thing) !== '' ? doLabel($label, $labeltag).n.$thing : $thing; 5501 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
title