PageRenderTime 63ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 1ms

/textpattern/publish/rss.php

https://github.com/nope/textpattern
PHP | 273 lines | 201 code | 49 blank | 23 comment | 47 complexity | c9392a8d2ec886635f9f7a1de814551f MD5 | raw file
  1. <?php
  2. /*
  3. This is Textpattern
  4. Copyright 2005 by Dean Allen - all rights reserved.
  5. Use of this software denotes acceptance of the Textpattern license agreement
  6. $HeadURL$
  7. $LastChangedRevision$
  8. */
  9. // -------------------------------------------------------------
  10. function rss()
  11. {
  12. global $prefs,$thisarticle;
  13. set_error_handler('feedErrorHandler');
  14. ob_clean();
  15. extract($prefs);
  16. extract(doSlash(gpsa(array('limit','area'))));
  17. // build filter criteria from a comma-separated list of sections and categories
  18. $feed_filter_limit = get_pref('feed_filter_limit', 10);
  19. $section = gps('section');
  20. $section = ($section ? array_slice(array_unique(do_list($section)), 0, $feed_filter_limit) : array());
  21. $category = gps('category');
  22. $category = ($category ? array_slice(array_unique(do_list($category)), 0, $feed_filter_limit) : array());
  23. $st = array();
  24. foreach ($section as $s)
  25. {
  26. $st[] = fetch_section_title($s);
  27. }
  28. $ct = array();
  29. foreach ($category as $c)
  30. {
  31. $ct[] = fetch_category_title($c);
  32. }
  33. $sitename .= ($section) ? ' - '.join(' - ', $st) : '';
  34. $sitename .= ($category) ? ' - '.join(' - ', $ct) : '';
  35. $dn = explode('/',$siteurl);
  36. $mail_or_domain = ($use_mail_on_feeds_id)? eE($blog_mail_uid):$dn[0];
  37. // feed header
  38. $out[] = tag('http://textpattern.com/?v='.$version, 'generator');
  39. $out[] = tag(doSpecial($sitename),'title');
  40. $out[] = tag(hu,'link');
  41. $out[] = '<atom:link href="'.pagelinkurl(array('rss'=>1,'area'=>$area,'section'=>$section,'category'=>$category,'limit'=>$limit)).'" rel="self" type="application/rss+xml" />';
  42. $out[] = tag(doSpecial($site_slogan),'description');
  43. $last = fetch('unix_timestamp(val)','txp_prefs','name','lastmod');
  44. $out[] = tag(safe_strftime('rfc822',$last),'pubDate');
  45. $out[] = callback_event('rss_head');
  46. // feed items
  47. $articles = array();
  48. $section = doSlash($section);
  49. $category = doSlash($category);
  50. if (!$area or $area=='article') {
  51. $sfilter = (!empty($section)) ? "and Section in ('".join("','", $section)."')" : '';
  52. $cfilter = (!empty($category))? "and (Category1 in ('".join("','", $category)."') or Category2 in ('".join("','", $category)."'))" : '';
  53. $limit = ($limit) ? $limit : $rss_how_many;
  54. $limit = intval(min($limit,max(100,$rss_how_many)));
  55. $frs = safe_column("name", "txp_section", "in_rss != '1'");
  56. if ($frs) foreach($frs as $f) $query[] = "and Section != '".doSlash($f)."'";
  57. $query[] = $sfilter;
  58. $query[] = $cfilter;
  59. $expired = ($publish_expired_articles) ? '' : ' and (now() <= Expires or Expires = '.NULLDATETIME.') ';
  60. $rs = safe_rows_start(
  61. "*, unix_timestamp(Posted) as uPosted, unix_timestamp(LastMod) as uLastMod, unix_timestamp(Expires) as uExpires, ID as thisid",
  62. "textpattern",
  63. "Status = 4 ".join(' ',$query).
  64. "and Posted < now()".$expired."order by Posted desc limit $limit"
  65. );
  66. if($rs) {
  67. while ($a = nextRow($rs)) {
  68. extract($a);
  69. populateArticleData($a);
  70. $cb = callback_event('rss_entry');
  71. $a['posted'] = $uPosted;
  72. $permlink = permlinkurl($a);
  73. $summary = trim(replace_relative_urls(parse($thisarticle['excerpt']), $permlink));
  74. $content = trim(replace_relative_urls(parse($thisarticle['body']), $permlink));
  75. if ($syndicate_body_or_excerpt) {
  76. # short feed: use body as summary if there's no excerpt
  77. if (!trim($summary))
  78. $summary = $content;
  79. $content = '';
  80. }
  81. if ($show_comment_count_in_feed) {
  82. $count = ($comments_count > 0) ? ' ['.$comments_count.']' : '';
  83. } else $count = '';
  84. $Title = escape_title(strip_tags($Title)).$count;
  85. $thisauthor = get_author_name($AuthorID);
  86. $item = tag($Title,'title').n.
  87. (trim($summary) ? tag(n.escape_cdata($summary).n,'description').n : '').
  88. (trim($content) ? tag(n.escape_cdata($content).n,'content:encoded').n : '').
  89. tag($permlink,'link').n.
  90. tag(safe_strftime('rfc822',$a['posted']),'pubDate').n.
  91. tag(htmlspecialchars($thisauthor),'dc:creator').n.
  92. tag('tag:'.$mail_or_domain.','.$feed_time.':'.$blog_uid.'/'.$uid,'guid', ' isPermaLink="false"').n.
  93. $cb;
  94. $articles[$ID] = tag($item,'item');
  95. $etags[$ID] = strtoupper(dechex(crc32($articles[$ID])));
  96. $dates[$ID] = $uPosted;
  97. }
  98. }
  99. } elseif ($area=='link') {
  100. $cfilter = ($category) ? "category='$category'" : '1';
  101. $limit = ($limit) ? $limit : $rss_how_many;
  102. $limit = intval(min($limit,max(100,$rss_how_many)));
  103. $rs = safe_rows_start("*, unix_timestamp(date) as uDate", "txp_link", "$cfilter order by date desc limit $limit");
  104. if ($rs) {
  105. while ($a = nextRow($rs)) {
  106. extract($a);
  107. $item =
  108. tag(doSpecial($linkname),'title').n.
  109. tag(doSpecial($description),'description').n.
  110. tag(doSpecial($url),'link').n.
  111. tag(safe_strftime('rfc822',$uDate),'pubDate');
  112. $articles[$id] = tag($item,'item');
  113. $etags[$id] = strtoupper(dechex(crc32($articles[$id])));
  114. $dates[$id] = $date;
  115. }
  116. }
  117. }
  118. if (!$articles) {
  119. if ($section) {
  120. if (safe_field('name', 'txp_section', "name in ('".join("','", $section)."')") == false) {
  121. txp_die(gTxt('404_not_found'), '404');
  122. }
  123. } elseif ($category) {
  124. switch ($area) {
  125. case 'link':
  126. if (safe_field('id', 'txp_category', "name = '$category' and type = 'link'") == false) {
  127. txp_die(gTxt('404_not_found'), '404');
  128. }
  129. break;
  130. case 'article':
  131. default:
  132. if (safe_field('id', 'txp_category', "name in ('".join("','", $category)."') and type = 'article'") == false) {
  133. txp_die(gTxt('404_not_found'), '404');
  134. }
  135. break;
  136. }
  137. }
  138. } else {
  139. //turn on compression if we aren't using it already
  140. if (extension_loaded('zlib') && ini_get("zlib.output_compression") == 0 && ini_get('output_handler') != 'ob_gzhandler' && !headers_sent()) {
  141. // make sure notices/warnings/errors don't fudge up the feed
  142. // when compression is used
  143. $buf = '';
  144. while ($b = @ob_get_clean())
  145. $buf .= $b;
  146. @ob_start('ob_gzhandler');
  147. echo $buf;
  148. }
  149. handle_lastmod();
  150. $hims = serverset('HTTP_IF_MODIFIED_SINCE');
  151. $imsd = ($hims) ? strtotime($hims) : 0;
  152. if (is_callable('apache_request_headers')) {
  153. $headers = apache_request_headers();
  154. if (isset($headers["A-IM"])) {
  155. $canaim = strpos($headers["A-IM"], "feed");
  156. } else {
  157. $canaim = false;
  158. }
  159. } else {
  160. $canaim = false;
  161. }
  162. $hinm = stripslashes(serverset('HTTP_IF_NONE_MATCH'));
  163. $cutarticles = false;
  164. if ($canaim !== false) {
  165. foreach($articles as $id=>$thing) {
  166. if (strpos($hinm, $etags[$id]) !== false) {
  167. unset($articles[$id]);
  168. $cutarticles = true;
  169. $cut_etag = true;
  170. }
  171. if ($dates[$id] < $imsd) {
  172. unset($articles[$id]);
  173. $cutarticles = true;
  174. $cut_time = true;
  175. }
  176. }
  177. }
  178. if (isset($cut_etag) && isset($cut_time)) {
  179. header("Vary: If-None-Match, If-Modified-Since");
  180. } else if (isset($cut_etag)) {
  181. header("Vary: If-None-Match");
  182. } else if (isset($cut_time)) {
  183. header("Vary: If-Modified-Since");
  184. }
  185. $etag = @join("-",$etags);
  186. if (strstr($hinm, $etag)) {
  187. txp_status_header('304 Not Modified');
  188. exit(0);
  189. }
  190. if ($cutarticles) {
  191. //header("HTTP/1.1 226 IM Used");
  192. //This should be used as opposed to 200, but Apache doesn't like it.
  193. //http://intertwingly.net/blog/2004/09/11/Vary-ETag/ says that the status code should be 200.
  194. header("Cache-Control: no-store, im");
  195. header("IM: feed");
  196. }
  197. }
  198. $out = array_merge($out, $articles);
  199. header("Content-Type: application/rss+xml; charset=utf-8");
  200. if (isset($etag)) header('ETag: "'.$etag.'"');
  201. return
  202. '<?xml version="1.0" encoding="utf-8"?>'.n.
  203. '<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.
  204. tag(join(n,$out),'channel').n.
  205. '</rss>';
  206. }
  207. // DEPRECATED FUNCTIONS
  208. // included for backwards compatibility with older plugins only
  209. function rss_safe_hed($toUnicode) {
  210. if (version_compare(phpversion(), "5.0.0", ">=")) {
  211. $str = html_entity_decode($toUnicode, ENT_QUOTES, "UTF-8");
  212. } else {
  213. $trans_tbl = get_html_translation_table(HTML_ENTITIES);
  214. foreach($trans_tbl as $k => $v) {
  215. $ttr[$v] = utf8_encode($k);
  216. }
  217. $str = strtr($toUnicode, $ttr);
  218. }
  219. return $str;
  220. }
  221. ?>