Textpattern PHP Cross Reference Content Management Systems

Source: /textpattern/publish/taghandlers.php - 5088 lines - 135493 bytes - Summary - Text - Print

Description: Collection of tag functions.

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

title

Description

title

Description

title

Description

title

title

Body