Textpattern PHP Cross Reference Content Management Systems

Source: /textpattern/publish/taghandlers.php - 4588 lines - 109529 bytes - Summary - Text - Print

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