Textpattern PHP Cross Reference Content Management Systems

Source: /textpattern/publish/rss.php - 317 lines - 10806 bytes - Summary - Text - Print

Description: Handles RSS feeds.

   1  <?php
   2  
   3  /*
   4   * Textpattern Content Management System
   5   * http://textpattern.com
   6   *
   7   * Copyright (C) 2005 Dean Allen
   8   * Copyright (C) 2016 The Textpattern Development Team
   9   *
  10   * This file is part of Textpattern.
  11   *
  12   * Textpattern is free software; you can redistribute it and/or
  13   * modify it under the terms of the GNU General Public License
  14   * as published by the Free Software Foundation, version 2.
  15   *
  16   * Textpattern is distributed in the hope that it will be useful,
  17   * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19   * GNU General Public License for more details.
  20   *
  21   * You should have received a copy of the GNU General Public License
  22   * along with Textpattern. If not, see <http://www.gnu.org/licenses/>.
  23   */
  24  
  25  /**
  26   * Handles RSS feeds.
  27   *
  28   * @package XML
  29   */
  30  
  31  /**
  32   * Generates and returns an RSS feed.
  33   *
  34   * This function can only be called once on a page. It send HTTP
  35   * headers and returns an RSS feed based on the requested URL parameters.
  36   * Accepts HTTP GET parameters 'limit', 'area', 'section' and 'category'.
  37   *
  38   * @return string XML
  39   */
  40  
  41  function rss()
  42  {
  43      global $prefs;
  44      set_error_handler('feedErrorHandler');
  45      ob_clean();
  46      extract($prefs);
  47  
  48      extract(doSlash(gpsa(array(
  49          'limit',
  50          'area',
  51      ))));
  52  
  53      // Build filter criteria from a comma-separated list of sections
  54      // and categories.
  55      $feed_filter_limit = get_pref('feed_filter_limit', 10);
  56      $section = gps('section');
  57      $category = gps('category');
  58  
  59      if (!is_scalar($section) || !is_scalar($category)) {
  60          txp_die('Not Found', 404);
  61      }
  62  
  63      $section = ($section ? array_slice(do_list_unique($section), 0, $feed_filter_limit) : array());
  64      $category = ($category ? array_slice(do_list_unique($category), 0, $feed_filter_limit) : array());
  65      $st = array();
  66  
  67      foreach ($section as $s) {
  68          $st[] = fetch_section_title($s);
  69      }
  70  
  71      $ct = array();
  72  
  73      foreach ($category as $c) {
  74          $ct[] = fetch_category_title($c);
  75      }
  76  
  77      $sitename .= ($section) ? ' - '.join(' - ', $st) : '';
  78      $sitename .= ($category) ? ' - '.join(' - ', $ct) : '';
  79      $dn = explode('/', $siteurl);
  80      $mail_or_domain = ($use_mail_on_feeds_id) ? eE($blog_mail_uid) : $dn[0];
  81  
  82      // Feed header.
  83      $out[] = tag('http://textpattern.com/?v='.$version, 'generator');
  84      $out[] = tag(doSpecial($sitename), 'title');
  85      $out[] = tag(hu, 'link');
  86      $out[] = '<atom:link href="'.pagelinkurl(array(
  87          'rss'      => 1,
  88          'area'     => $area,
  89          'section'  => $section,
  90          'category' => $category,
  91          'limit'    => $limit,
  92      )).'" rel="self" type="application/rss+xml" />';
  93      $out[] = tag(doSpecial($site_slogan), 'description');
  94      $last = fetch("UNIX_TIMESTAMP(val)", 'txp_prefs', 'name', 'lastmod');
  95      $out[] = tag(safe_strftime('rfc822', $last), 'pubDate');
  96      $out[] = callback_event('rss_head');
  97  
  98      // Feed items.
  99      $articles = array();
 100      $section = doSlash($section);
 101      $category = doSlash($category);
 102  
 103      if (!$area or $area == 'article') {
 104          $sfilter = (!empty($section)) ? "AND Section IN ('".join("','", $section)."')" : '';
 105          $cfilter = (!empty($category)) ? "AND (Category1 IN ('".join("','", $category)."') OR Category2 IN ('".join("','", $category)."'))" : '';
 106          $limit = ($limit) ? $limit : $rss_how_many;
 107          $limit = intval(min($limit, max(100, $rss_how_many)));
 108  
 109          $frs = safe_column("name", 'txp_section', "in_rss != '1'");
 110  
 111          if ($frs) {
 112              foreach ($frs as $f) {
 113                  $query[] = "AND Section != '".doSlash($f)."'";
 114              }
 115          }
 116  
 117          $query[] = $sfilter;
 118          $query[] = $cfilter;
 119  
 120          $expired = ($publish_expired_articles) ? " " : " AND (".now('expires')." <= Expires OR Expires IS NULL) ";
 121          $rs = safe_rows_start(
 122              "*, UNIX_TIMESTAMP(Posted) AS uPosted, UNIX_TIMESTAMP(LastMod) AS uLastMod, UNIX_TIMESTAMP(Expires) AS uExpires, ID AS thisid",
 123              'textpattern',
 124              "Status = 4 ".join(' ', $query).
 125              "AND Posted < ".now('posted').$expired." ORDER BY Posted DESC LIMIT $limit"
 126          );
 127  
 128          if ($rs) {
 129              while ($a = nextRow($rs)) {
 130                  // In case $GLOBALS['thisarticle'] is unset
 131                  global $thisarticle;
 132                  extract($a);
 133                  populateArticleData($a);
 134  
 135                  $cb = callback_event('rss_entry');
 136  
 137                  $a['posted'] = $uPosted;
 138                  $a['expires'] = $uExpires;
 139  
 140                  $permlink = permlinkurl($a);
 141                  $summary = trim(replace_relative_urls(parse($thisarticle['excerpt']), $permlink));
 142                  $content = trim(replace_relative_urls(parse($thisarticle['body']), $permlink));
 143  
 144                  if ($syndicate_body_or_excerpt) {
 145                      // Short feed: use body as summary if there's no excerpt.
 146                      if (!trim($summary)) {
 147                          $summary = $content;
 148                      }
 149  
 150                      $content = '';
 151                  }
 152  
 153                  if ($show_comment_count_in_feed) {
 154                      $count = ($comments_count > 0) ? ' ['.$comments_count.']' : '';
 155                  } else {
 156                      $count = '';
 157                  }
 158  
 159                  $Title = escape_title(preg_replace("/&(?![#a-z0-9]+;)/i", "&amp;", html_entity_decode(strip_tags($Title), ENT_QUOTES, 'UTF-8'))).$count;
 160  
 161                  $thisauthor = get_author_name($AuthorID);
 162  
 163                  $item = tag($Title, 'title').n.
 164                      (trim($summary) ? tag(n.escape_cdata($summary).n, 'description').n : '').
 165                      (trim($content) ? tag(n.escape_cdata($content).n, 'content:encoded').n : '').
 166                      tag($permlink, 'link').n.
 167                      tag(safe_strftime('rfc822', $a['posted']), 'pubDate').n.
 168                      tag(htmlspecialchars($thisauthor), 'dc:creator').n.
 169                      tag('tag:'.$mail_or_domain.','.$feed_time.':'.$blog_uid.'/'.$uid, 'guid', ' isPermaLink="false"').n.
 170                      $cb;
 171  
 172                  $articles[$ID] = tag($item, 'item');
 173  
 174                  $etags[$ID] = strtoupper(dechex(crc32($articles[$ID])));
 175                  $dates[$ID] = $uPosted;
 176              }
 177          }
 178      } elseif ($area == 'link') {
 179          $cfilter = ($category) ? "category IN ('".join("','", $category)."')"  : '1';
 180          $limit = ($limit) ? $limit : $rss_how_many;
 181          $limit = intval(min($limit, max(100, $rss_how_many)));
 182  
 183          $rs = safe_rows_start("*, UNIX_TIMESTAMP(date) AS uDate", 'txp_link', "$cfilter ORDER BY date DESC LIMIT $limit");
 184  
 185          if ($rs) {
 186              while ($a = nextRow($rs)) {
 187                  extract($a);
 188                  $item =
 189                      tag(doSpecial($linkname), 'title').n.
 190                      tag(doSpecial($description), 'description').n.
 191                      tag(doSpecial($url), 'link').n.
 192                      tag(safe_strftime('rfc822', $uDate), 'pubDate');
 193                  $articles[$id] = tag($item, 'item');
 194  
 195                  $etags[$id] = strtoupper(dechex(crc32($articles[$id])));
 196                  $dates[$id] = $date;
 197              }
 198          }
 199      }
 200  
 201      if (!$articles) {
 202          if ($section) {
 203              if (safe_field("name", 'txp_section', "name IN ('".join("','", $section)."')") == false) {
 204                  txp_die(gTxt('404_not_found'), '404');
 205              }
 206          } elseif ($category) {
 207              switch ($area) {
 208                  case 'link':
 209                      if (safe_field("id", 'txp_category', "name = '$category' AND type = 'link'") == false) {
 210                          txp_die(gTxt('404_not_found'), '404');
 211                      }
 212                      break;
 213                  case 'article':
 214                  default:
 215                      if (safe_field("id", 'txp_category', "name IN ('".join("','", $category)."') AND type = 'article'") == false) {
 216                          txp_die(gTxt('404_not_found'), '404');
 217                      }
 218                      break;
 219              }
 220          }
 221      } else {
 222          handle_lastmod();
 223          $hims = serverset('HTTP_IF_MODIFIED_SINCE');
 224          $imsd = ($hims) ? strtotime($hims) : 0;
 225  
 226          if (is_callable('apache_request_headers')) {
 227              $headers = apache_request_headers();
 228  
 229              if (isset($headers["A-IM"])) {
 230                  $canaim = strpos($headers["A-IM"], "feed");
 231              } else {
 232                  $canaim = false;
 233              }
 234          } else {
 235              $canaim = false;
 236          }
 237  
 238          $hinm = stripslashes(serverset('HTTP_IF_NONE_MATCH'));
 239  
 240          $cutarticles = false;
 241  
 242          if ($canaim !== false) {
 243              foreach ($articles as $id => $thing) {
 244                  if (strpos($hinm, $etags[$id]) !== false) {
 245                      unset($articles[$id]);
 246                      $cutarticles = true;
 247                      $cut_etag = true;
 248                  }
 249  
 250                  if ($dates[$id] < $imsd) {
 251                      unset($articles[$id]);
 252                      $cutarticles = true;
 253                      $cut_time = true;
 254                  }
 255              }
 256          }
 257  
 258          if (isset($cut_etag) && isset($cut_time)) {
 259              header("Vary: If-None-Match, If-Modified-Since");
 260          } elseif (isset($cut_etag)) {
 261              header("Vary: If-None-Match");
 262          } elseif (isset($cut_time)) {
 263              header("Vary: If-Modified-Since");
 264          }
 265  
 266          $etag = @join("-", $etags);
 267  
 268          if (strstr($hinm, $etag)) {
 269              txp_status_header('304 Not Modified');
 270              exit(0);
 271          }
 272  
 273          if ($cutarticles) {
 274              // header("HTTP/1.1 226 IM Used");
 275              // This should be used as opposed to 200, but Apache doesn't like it.
 276              header("Cache-Control: no-store, im");
 277              header("IM: feed");
 278          }
 279      }
 280  
 281      $out = array_merge($out, $articles);
 282  
 283      header("Content-Type: application/rss+xml; charset=utf-8");
 284  
 285      if (isset($etag)) {
 286          header('ETag: "'.$etag.'"');
 287      }
 288  
 289      return
 290          '<?xml version="1.0" encoding="utf-8"?>'.n.
 291          '<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom">'.n.
 292          tag(join(n, $out), 'channel').n.
 293          '</rss>';
 294  }
 295  
 296  /**
 297   * Converts HTML entities to UTF-8 characters.
 298   *
 299   * @param      string $toUnicode
 300   * @return     string
 301   * @deprecated in 4.0.4
 302   */
 303  
 304  function rss_safe_hed($toUnicode)
 305  {
 306      if (version_compare(phpversion(), "5.0.0", ">=")) {
 307          $str =  html_entity_decode($toUnicode, ENT_QUOTES, "UTF-8");
 308      } else {
 309          $trans_tbl = get_html_translation_table(HTML_ENTITIES);
 310          foreach ($trans_tbl as $k => $v) {
 311              $ttr[$v] = utf8_encode($k);
 312          }
 313          $str = strtr($toUnicode, $ttr);
 314      }
 315  
 316      return $str;
 317  }

title

Description

title

Description

title

Description

title

title

Body