/** * Convenience for those who prefer "SELECT * FROM textpattern" * * @param array $rs An assoc w/ one article's data from the DB */ function article_format_info($rs) { $rs['uPosted'] = ($unix_ts = @strtotime($rs['Posted'])) > 0 ? $unix_ts : NULLDATETIME; $rs['uLastMod'] = ($unix_ts = @strtotime($rs['LastMod'])) > 0 ? $unix_ts : NULLDATETIME; $rs['uExpires'] = ($unix_ts = @strtotime($rs['Expires'])) > 0 ? $unix_ts : NULLDATETIME; populateArticleData($rs); }
function discuss($id) { $rs = safe_row('*, unix_timestamp(Posted) as uPosted, unix_timestamp(LastMod) as uLastMod, unix_timestamp(Expires) as uExpires', 'textpattern', 'ID=' . intval($id) . ' and Status >= 4'); if ($rs) { populateArticleData($rs); $result = parse_form('comments_display'); return $result; } return ''; }
function discuss($id) { $rs = safe_row('*, unix_timestamp(Posted) as uPosted', 'textpattern', "ID='" . doSlash($id) . "' and Status >= 4"); if ($rs) { populateArticleData($rs); if (ps('preview')) { $GLOBALS['comment_preview'] = 1; } $result = parse(fetch_form('comments_display')); unset($GLOBALS['comment_preview']); return $result; } return ''; }
/** * Generates and outputs an Atom feed. * * This function can only be called once on a page. It outputs an Atom feed * based on the requested URL parameters. Accepts HTTP GET parameters 'limit', * 'area', 'section' and 'category'. */ function atom() { global $thisarticle, $prefs; set_error_handler('feedErrorHandler'); ob_clean(); extract($prefs); $last = fetch("UNIX_TIMESTAMP(val)", 'txp_prefs', 'name', 'lastmod'); extract(doSlash(gpsa(array('limit', 'area')))); // Build filter criteria from a comma-separated list of sections // and categories. $feed_filter_limit = get_pref('feed_filter_limit', 10); $section = gps('section'); $category = gps('category'); if (!is_scalar($section) || !is_scalar($category)) { txp_die('Not Found', 404); } $section = $section ? array_slice(do_list_unique($section), 0, $feed_filter_limit) : array(); $category = $category ? array_slice(do_list_unique($category), 0, $feed_filter_limit) : array(); $st = array(); foreach ($section as $s) { $st[] = fetch_section_title($s); } $ct = array(); foreach ($category as $c) { $ct[] = fetch_category_title($c); } $sitename .= $section ? ' - ' . join(' - ', $st) : ''; $sitename .= $category ? ' - ' . join(' - ', $ct) : ''; $pub = safe_row("RealName, email", 'txp_users', "privs = 1"); // Feed header. $out[] = tag(htmlspecialchars($sitename), 'title', t_text); $out[] = tag(htmlspecialchars($site_slogan), 'subtitle', t_text); $out[] = '<link' . r_relself . ' href="' . pagelinkurl(array('atom' => 1, 'area' => $area, 'section' => $section, 'category' => $category, 'limit' => $limit)) . '" />'; $out[] = '<link' . r_relalt . t_texthtml . ' href="' . hu . '" />'; // Atom feeds with mail or domain name. $dn = explode('/', $siteurl); $mail_or_domain = $use_mail_on_feeds_id ? eE($blog_mail_uid) : $dn[0]; $out[] = tag('tag:' . $mail_or_domain . ',' . $blog_time_uid . ':' . $blog_uid . ($section ? '/' . join(',', $section) : '') . ($category ? '/' . join(',', $category) : ''), 'id'); $out[] = tag('Textpattern', 'generator', ' uri="http://textpattern.com/" version="' . $version . '"'); $out[] = tag(safe_strftime("w3cdtf", $last), 'updated'); $auth[] = tag($pub['RealName'], 'name'); $auth[] = $include_email_atom ? tag(eE($pub['email']), 'email') : ''; $auth[] = tag(hu, 'uri'); $out[] = tag(n . t . t . join(n . t . t, $auth) . n, 'author'); $out[] = callback_event('atom_head'); // Feed items. $articles = array(); $section = doSlash($section); $category = doSlash($category); if (!$area or $area == 'article') { $sfilter = !empty($section) ? "AND Section IN ('" . join("','", $section) . "')" : ''; $cfilter = !empty($category) ? "AND (Category1 IN ('" . join("','", $category) . "') OR Category2 IN ('" . join("','", $category) . "'))" : ''; $limit = $limit ? $limit : $rss_how_many; $limit = intval(min($limit, max(100, $rss_how_many))); $frs = safe_column("name", 'txp_section', "in_rss != '1'"); $query = array(); foreach ($frs as $f) { $query[] = "AND Section != '" . doSlash($f) . "'"; } $query[] = $sfilter; $query[] = $cfilter; $expired = $publish_expired_articles ? " " : " AND (" . now('expires') . " <= Expires OR Expires = " . NULLDATETIME . ") "; $rs = safe_rows_start("*,\n ID AS thisid,\n UNIX_TIMESTAMP(Posted) AS uPosted,\n UNIX_TIMESTAMP(Expires) AS uExpires,\n UNIX_TIMESTAMP(LastMod) AS uLastMod", 'textpattern', "Status = 4 AND Posted <= " . now('posted') . $expired . join(' ', $query) . "ORDER BY Posted DESC LIMIT {$limit}"); if ($rs) { while ($a = nextRow($rs)) { extract($a); populateArticleData($a); $cb = callback_event('atom_entry'); $e = array(); $a['posted'] = $uPosted; $a['expires'] = $uExpires; if ($show_comment_count_in_feed) { $count = $comments_count > 0 ? ' [' . $comments_count . ']' : ''; } else { $count = ''; } $thisauthor = get_author_name($AuthorID); $e['thisauthor'] = tag(n . t . t . t . tag(htmlspecialchars($thisauthor), 'name') . n . t . t, 'author'); $e['issued'] = tag(safe_strftime('w3cdtf', $uPosted), 'published'); $e['modified'] = tag(safe_strftime('w3cdtf', $uLastMod), 'updated'); $escaped_title = htmlspecialchars($Title); $e['title'] = tag($escaped_title . $count, 'title', t_html); $permlink = permlinkurl($a); $e['link'] = '<link' . r_relalt . t_texthtml . ' href="' . $permlink . '" />'; $e['id'] = tag('tag:' . $mail_or_domain . ',' . $feed_time . ':' . $blog_uid . '/' . $uid, 'id'); $e['category1'] = trim($Category1) ? '<category term="' . htmlspecialchars($Category1) . '" />' : ''; $e['category2'] = trim($Category2) ? '<category term="' . htmlspecialchars($Category2) . '" />' : ''; $summary = trim(replace_relative_urls(parse($thisarticle['excerpt']), $permlink)); $content = trim(replace_relative_urls(parse($thisarticle['body']), $permlink)); if ($syndicate_body_or_excerpt) { // Short feed: use body as summary if there's no excerpt. if (!trim($summary)) { $summary = $content; } $content = ''; } if (trim($content)) { $e['content'] = tag(n . escape_cdata($content) . n, 'content', t_html); } if (trim($summary)) { $e['summary'] = tag(n . escape_cdata($summary) . n, 'summary', t_html); } $articles[$ID] = tag(n . t . t . join(n . t . t, $e) . n . $cb, 'entry'); $etags[$ID] = strtoupper(dechex(crc32($articles[$ID]))); $dates[$ID] = $uLastMod; } } } elseif ($area == 'link') { $cfilter = $category ? "category in ('" . join("','", $category) . "')" : '1'; $limit = $limit ? $limit : $rss_how_many; $limit = intval(min($limit, max(100, $rss_how_many))); $rs = safe_rows_start("*", 'txp_link', "{$cfilter} ORDER BY date DESC, id DESC LIMIT {$limit}"); if ($rs) { while ($a = nextRow($rs)) { extract($a); $e['title'] = tag(htmlspecialchars($linkname), 'title', t_html); $e['content'] = tag(n . htmlspecialchars($description) . n, 'content', t_html); $url = preg_replace("/^\\/(.*)/", "https?://{$siteurl}/\$1", $url); $url = preg_replace("/&((?U).*)=/", "&\\1=", $url); $e['link'] = '<link' . r_relalt . t_texthtml . ' href="' . $url . '" />'; $e['issued'] = tag(safe_strftime('w3cdtf', strtotime($date)), 'published'); $e['modified'] = tag(gmdate('Y-m-d\\TH:i:s\\Z', strtotime($date)), 'updated'); $e['id'] = tag('tag:' . $mail_or_domain . ',' . safe_strftime('%Y-%m-%d', strtotime($date)) . ':' . $blog_uid . '/' . $id, 'id'); $articles[$id] = tag(n . t . t . join(n . t . t, $e) . n, 'entry'); $etags[$id] = strtoupper(dechex(crc32($articles[$id]))); $dates[$id] = $date; } } } if (!$articles) { if ($section) { if (safe_field("name", 'txp_section', "name IN ('" . join("','", $section) . "')") == false) { txp_die(gTxt('404_not_found'), '404'); } } elseif ($category) { switch ($area) { case 'link': if (safe_field("id", 'txp_category', "name = '{$category}' AND type = 'link'") == false) { txp_die(gTxt('404_not_found'), '404'); } break; case 'article': default: if (safe_field("id", 'txp_category', "name IN ('" . join("','", $category) . "') AND type = 'article'") == false) { txp_die(gTxt('404_not_found'), '404'); } break; } } } else { // Turn on compression if we aren't using it already. if (extension_loaded('zlib') && ini_get("zlib.output_compression") == 0 && ini_get('output_handler') != 'ob_gzhandler' && !headers_sent()) { // Make sure notices/warnings/errors don't fudge up the feed when // compression is used. $buf = ''; while ($b = @ob_get_clean()) { $buf .= $b; } @ob_start('ob_gzhandler'); echo $buf; } handle_lastmod(); $hims = serverset('HTTP_IF_MODIFIED_SINCE'); $imsd = $hims ? strtotime($hims) : 0; if (is_callable('apache_request_headers')) { $headers = apache_request_headers(); if (isset($headers["A-IM"])) { $canaim = strpos($headers["A-IM"], "feed"); } else { $canaim = false; } } else { $canaim = false; } $hinm = stripslashes(serverset('HTTP_IF_NONE_MATCH')); $cutarticles = false; if ($canaim !== false) { foreach ($articles as $id => $thing) { if (strpos($hinm, $etags[$id])) { unset($articles[$id]); $cutarticles = true; $cut_etag = true; } if ($dates[$id] < $imsd) { unset($articles[$id]); $cutarticles = true; $cut_time = true; } } } if (isset($cut_etag) && isset($cut_time)) { header("Vary: If-None-Match, If-Modified-Since"); } elseif (isset($cut_etag)) { header("Vary: If-None-Match"); } elseif (isset($cut_time)) { header("Vary: If-Modified-Since"); } $etag = @join("-", $etags); if (strstr($hinm, $etag)) { txp_status_header('304 Not Modified'); exit(0); } if ($etag) { header('ETag: "' . $etag . '"'); } if ($cutarticles) { // header("HTTP/1.1 226 IM Used"); // This should be used as opposed to 200, but Apache doesn't like it. // http://intertwingly.net/blog/2004/09/11/Vary-ETag/ says that the // status code should be 200. header("Cache-Control: no-store, im"); header("IM: feed"); } } $out = array_merge($out, $articles); header('Content-type: application/atom+xml; charset=utf-8'); return chr(60) . '?xml version="1.0" encoding="UTF-8"?' . chr(62) . n . '<feed xml:lang="' . txpspecialchars($language) . '" xmlns="http://www.w3.org/2005/Atom">' . join(n, $out) . '</feed>'; }
function doArticle($atts, $thing = null) { global $pretext, $prefs, $thisarticle; extract($prefs); extract($pretext); extract(gpsa(array('parentid', 'preview'))); $theAtts = lAtts(array('allowoverride' => '1', 'form' => 'default', 'status' => STATUS_LIVE, 'pgonly' => 0), $atts, 0); extract($theAtts); // Save *all* atts to get hold of the current article filter criteria. filterAtts($atts); // No output required. if ($pgonly) { return ''; } // If a form is specified, $thing is for doArticles() - hence ignore // $thing here. if (!empty($atts['form'])) { $thing = ''; } if ($status) { $status = in_array(strtolower($status), array('sticky', STATUS_STICKY)) ? STATUS_STICKY : STATUS_LIVE; } if (empty($thisarticle) or $thisarticle['thisid'] != $id) { $id = assert_int($id); $thisarticle = null; $q_status = $status ? "AND Status = " . intval($status) : "AND Status IN (" . STATUS_LIVE . "," . STATUS_STICKY . ")"; $rs = safe_row("*, UNIX_TIMESTAMP(Posted) AS uPosted, UNIX_TIMESTAMP(Expires) AS uExpires, UNIX_TIMESTAMP(LastMod) AS uLastMod", 'textpattern', "ID = {$id} {$q_status} LIMIT 1"); if ($rs) { extract($rs); populateArticleData($rs); } } if (!empty($thisarticle) and ($thisarticle['status'] == $status or gps('txpreview'))) { extract($thisarticle); $thisarticle['is_first'] = 1; $thisarticle['is_last'] = 1; if ($allowoverride and $override_form) { $article = parse_form($override_form); } else { $article = $thing ? parse($thing) : parse_form($form); } if ($use_comments and $comments_auto_append) { $article .= parse_form('comments_display'); } unset($GLOBALS['thisarticle']); return $article; } }
function doArticle($atts, $thing = NULL) { global $pretext, $prefs, $thisarticle; extract($prefs); extract($pretext); extract(gpsa(array('parentid', 'preview'))); extract(lAtts(array('allowoverride' => '1', 'form' => 'default', 'status' => '4'), $atts, 0)); // if a form is specified, $thing is for doArticles() - hence ignore $thing here. if (!empty($atts['form'])) { $thing = ''; } if ($status) { $status = in_array(strtolower($status), array('sticky', '5')) ? 5 : 4; } if (empty($thisarticle) or $thisarticle['thisid'] != $id) { $thisarticle = NULL; $q_status = $status ? 'and Status = ' . intval($status) : 'and Status in (4,5)'; $rs = safe_row("*, unix_timestamp(Posted) as uPosted, unix_timestamp(Expires) as uExpires, unix_timestamp(LastMod) as uLastMod", "textpattern", 'ID = ' . intval($id) . " {$q_status} limit 1"); if ($rs) { extract($rs); populateArticleData($rs); } } if (!empty($thisarticle) and ($thisarticle['status'] == $status or gps('txpreview'))) { extract($thisarticle); $thisarticle['is_first'] = 1; $thisarticle['is_last'] = 1; if ($allowoverride and $override_form) { $article = parse_form($override_form); } else { $article = $thing ? parse($thing) : parse_form($form); } if ($use_comments and $comments_auto_append) { $article .= parse_form('comments_display'); } unset($GLOBALS['thisarticle']); return $article; } }
function popup_comments($atts, $thing = null) { extract(lAtts(array('form' => 'comments_display'), $atts)); $rs = safe_row("*, UNIX_TIMESTAMP(Posted) AS uPosted, UNIX_TIMESTAMP(LastMod) AS uLastMod, UNIX_TIMESTAMP(Expires) AS uExpires", 'textpattern', "ID=" . intval(gps('parentid')) . " AND Status >= 4"); if ($rs) { populateArticleData($rs); return $thing === null ? parse_form($form) : parse($thing); } return ''; }
function related_articles($atts, $thing = NULL) { global $thisarticle, $prefs; assert_article(); extract(lAtts(array('break' => br, 'class' => __FUNCTION__, 'form' => '', 'label' => '', 'labeltag' => '', 'limit' => 10, 'match' => 'Category1,Category2', 'no_widow' => @$prefs['title_no_widow'], 'section' => '', 'sort' => 'Posted desc', 'wraptag' => ''), $atts)); if (empty($thisarticle['category1']) and empty($thisarticle['category2'])) { return; } $match = do_list($match); if (!in_array('Category1', $match) and !in_array('Category2', $match)) { return; } $id = $thisarticle['thisid']; $cats = array(); if ($thisarticle['category1']) { $cats[] = doSlash($thisarticle['category1']); } if ($thisarticle['category2']) { $cats[] = doSlash($thisarticle['category2']); } $cats = join("','", $cats); $categories = array(); if (in_array('Category1', $match)) { $categories[] = "Category1 in('{$cats}')"; } if (in_array('Category2', $match)) { $categories[] = "Category2 in('{$cats}')"; } $categories = 'and (' . join(' or ', $categories) . ')'; $section = $section ? " and Section IN ('" . join("','", doSlash(do_list($section))) . "')" : ''; $expired = $prefs['publish_expired_articles'] ? '' : ' and (now() <= Expires or Expires = ' . NULLDATETIME . ') '; $rs = safe_rows_start('*, unix_timestamp(Posted) as posted, unix_timestamp(LastMod) as uLastMod, unix_timestamp(Expires) as uExpires', 'textpattern', 'ID != ' . intval($id) . " and Status = 4 {$expired} and Posted <= now() {$categories} {$section} order by " . doSlash($sort) . ' limit 0,' . intval($limit)); if ($rs) { $out = array(); $old_article = $thisarticle; while ($a = nextRow($rs)) { $a['Title'] = $no_widow ? noWidow(escape_title($a['Title'])) : escape_title($a['Title']); $a['uPosted'] = $a['posted']; // populateArticleData() and permlinkurl() assume quite a bunch of posting dates... if (empty($form) && empty($thing)) { $out[] = href($a['Title'], permlinkurl($a)); } else { populateArticleData($a); $out[] = $thing ? parse($thing) : parse_form($form); } } $thisarticle = $old_article; if ($out) { return doLabel($label, $labeltag) . doWrap($out, $wraptag, $break, $class); } } return ''; }
function doArticle($atts) { global $pretext, $prefs; extract($prefs); extract($pretext); $preview = ps('preview'); $parentid = ps('parentid'); extract(lAtts(array('form' => 'default', 'status' => ''), $atts)); if ($status and !is_numeric($status)) { $status = getStatusNum($status); } $q_status = $status ? "and Status='" . doSlash($status) . "'" : 'and Status in (4,5)'; $rs = safe_row("*, unix_timestamp(Posted) as uPosted", "textpattern", "ID='" . intval($id) . "' {$q_status} limit 1"); if ($rs) { extract($rs); populateArticleData($rs); $GLOBALS['thisarticle']['is_first'] = 1; $GLOBALS['thisarticle']['is_last'] = 1; // define the article form $article = fetch_form($override_form ? $override_form : $form); if ($preview && $parentid) { $article = '<txp:comments_preview bc="1" id="' . $parentid . '" />' . $article; } $article = parse($article); if ($use_comments and $comments_auto_append) { $f = fetch_form('comments_display'); $article .= parse($f); } unset($GLOBALS['thisarticle']); return $article; } }
function atom() { global $thisarticle; extract($GLOBALS['prefs']); define("textplain", ' type="text/plain"'); define("texthtml", ' type="text/html"'); define("relalt", ' rel="alternate"'); define('appxhtml', ' type="application/xhtml+xml"'); define("divxhtml", '<div xmlns="http://www.w3.org/1999/xhtml">'); $area = doSlash(gps('area')); extract(doSlash(gpsa(array('category', 'section', 'limit')))); $last = fetch('unix_timestamp(val)', 'txp_prefs', 'name', 'lastmod'); $sitename .= $section ? ' - ' . $section : ''; $sitename .= $category ? ' - ' . $category : ''; $pub = safe_row("RealName, email", "txp_users", "privs=1"); $out[] = tag($sitename, 'title', textplain); $out[] = tag($site_slogan, 'tagline', textplain); $out[] = '<link' . relalt . texthtml . ' href="' . hu . '" />'; $articles = array(); //Atom feeds with mail or domain name $dn = explode('/', $siteurl); $mail_or_domain = $use_mail_on_feeds_id ? eE($blog_mail_uid) : $dn[0]; $out[] = tag('tag:' . $mail_or_domain . ',' . $blog_time_uid . ':' . $blog_uid . ($section ? '/' . $section : '') . ($category ? '/' . $category : ''), 'id'); $out[] = tag('Textpattern', 'generator', ' url="http://textpattern.com" version="' . $version . '"'); $out[] = tag(date("Y-m-d\\TH:i:s\\Z", $last), 'modified'); $auth[] = tag($pub['RealName'], 'name'); $auth[] = $include_email_atom ? tag(eE($pub['email']), 'email') : ''; $auth[] = tag(hu, 'url'); $out[] = tag(n . t . t . join(n . t . t, $auth) . n, 'author'); if (!$area or $area == 'article') { $sfilter = $section ? "and Section = '" . $section . "'" : ''; $cfilter = $category ? "and (Category1='" . $category . "' or Category2='" . $category . "')" : ''; $limit = $limit ? $limit : '5'; $frs = safe_column("name", "txp_section", "in_rss != '1'"); foreach ($frs as $f) { $query[] = "and Section != '" . $f . "'"; } $query[] = $sfilter; $query[] = $cfilter; $rs = safe_rows_start("*, \n\t\t\t\tID as thisid, \n\t\t\t\tunix_timestamp(Posted) as uPosted,\n\t\t\t\tunix_timestamp(LastMod) as uLastMod", "textpattern", "Status=4 and Posted <= now() " . join(' ', $query) . "order by Posted desc limit {$limit}"); if ($rs) { while ($a = nextRow($rs)) { extract($a); populateArticleData($a); $a['posted'] = $uPosted; if ($show_comment_count_in_feed) { $dc = getCount('txp_discuss', "parentid={$ID} and visible=1"); $count = $dc > 0 ? ' [' . $dc . ']' : ''; } else { $count = ''; } $thisauthor = safe_field("RealName", "txp_users", "name='{$AuthorID}'"); $e['thisauthor'] = tag(n . t . t . t . tag(htmlspecialchars($thisauthor), 'name') . n . t . t, 'author'); $e['issued'] = tag(gmdate("Y-m-d\\TH:i:s\\Z", $uPosted), 'issued'); $e['modified'] = tag(gmdate("Y-m-d\\TH:i:s\\Z", $uLastMod), 'modified'); $escaped_title = safe_hed($Title); $escaped_title = preg_replace("/&(?![#a-z0-9]+;)/i", '&', $escaped_title); $escaped_title = str_replace('<', '<', $escaped_title); $escaped_title = str_replace('>', '>', $escaped_title); $e['title'] = tag($escaped_title . $count, 'title'); $uTitle = $url_title ? $url_title : stripSpace($Title); $uTitle = htmlspecialchars($uTitle, ENT_NOQUOTES); $permlink = permlinkurl($a); $e['link'] = '<link' . relalt . texthtml . ' href="' . $permlink . '" />'; $e['id'] = tag('tag:' . $mail_or_domain . ',' . $feed_time . ':' . $blog_uid . '/' . $uid, 'id'); $e['subject'] = tag(htmlspecialchars($Category1), 'dc:subject'); // pull Body or Excerpt? $Body = !$syndicate_body_or_excerpt ? $thisarticle['body'] : $thisarticle['excerpt']; // if Excerpt is empty, switch back to Body_html $Body = !trim($Body) ? $thisarticle['body'] : $Body; // fix relative urls $Body = str_replace('href="/', 'href="' . hu, $Body); $Body = preg_replace("/href=\\\"#(.*)\"/", "href=\"" . permlinkurl($a) . "#\\1\"", $Body); $Body = safe_hed($Body); // encode and entify $Body = preg_replace(array('/</', '/>/', "/'/", '/"/'), array('<', '>', ''', '"'), $Body); // encode bare ampersands $Body = preg_replace("/&(?![#0-9]+;|\\w+;)/i", '&', $Body); $e['content'] = tag(n . $Body . n, 'content', ' type="text/html" mode="escaped"'); $articles[$ID] = tag(n . t . t . join(n . t . t, $e) . n, 'entry'); $etags[$ID] = strtoupper(dechex(crc32($articles[$ID]))); $dates[$ID] = $uLastMod; } } } elseif ($area == 'link') { $cfilter = $category ? "category='{$category}'" : '1'; $limit = $limit ? $limit : 15; $rs = safe_rows_start("*", "txp_link", "{$cfilter} order by date desc limit {$limit}"); if ($rs) { while ($a = nextRow($rs)) { extract($a); $e['title'] = tag(doSpecial($linkname), 'title'); $content = utf8_encode(htmlspecialchars($description)); $e['content'] = tag(n . $description . n, 'content', ' type="text/html" mode="escaped"'); $url = preg_replace("/^\\/(.*)/", "http://{$siteurl}/\$1", $url); $url = preg_replace("/&((?U).*)=/", "&\\1=", $url); $e['link'] = '<link' . relalt . texthtml . ' href="' . $url . '" />'; $e['issued'] = tag(gmdate("Y-m-d\\TH:i:s\\Z", $date), 'issued'); $e['modified'] = tag(gmdate("Y-m-d\\TH:i:s\\Z", $date), 'modified'); $e['id'] = tag('tag:' . $mail_or_domain . ',' . $feed_time . ':' . $id, 'id'); $articles[$id] = tag(n . t . t . join(n . t . t, $e) . n, 'entry'); $etags[$id] = strtoupper(dechex(crc32($articles[$id]))); $dates[$id] = $date; } } } if (!empty($articles)) { //turn on compression if we aren't using it already if (extension_loaded('zlib') && ini_get("zlib.output_compression") == 0 && ini_get('output_handler') != 'ob_gzhandler' && !headers_sent()) { ob_start("ob_gzhandler"); } $last = fetch('unix_timestamp(val)', 'txp_prefs', 'name', 'lastmod'); $last = gmdate("D, d M Y H:i:s \\G\\M\\T", $last); header("Last-Modified: {$last}"); $expires = gmdate('D, d M Y H:i:s \\G\\M\\T', time() + 3600 * 1); header("Expires: {$expires}"); $hims = serverset('HTTP_IF_MODIFIED_SINCE'); if ($hims == $last) { header("HTTP/1.1 304 Not Modified"); exit; } $imsd = @strtotime($hims); if (is_callable('apache_request_headers')) { $headers = apache_request_headers(); if (isset($headers["A-IM"])) { $canaim = strpos($headers["A-IM"], "feed"); } else { $canaim = false; } } else { $canaim = false; } $hinm = stripslashes(serverset('HTTP_IF_NONE_MATCH')); $cutarticles = false; if ($canaim !== false) { foreach ($articles as $id => $thing) { if (strpos($hinm, $etags[$id])) { unset($articles[$id]); $cutarticles = true; $cut_etag = true; } if ($dates[$id] < $imsd) { unset($articles[$id]); $cutarticles = true; $cut_time = true; } } } if (isset($cut_etag) && isset($cut_time)) { header("Vary: If-None-Match, If-Modified-Since"); } else { if (isset($cut_etag)) { header("Vary: If-None-Match"); } else { if (isset($cut_time)) { header("Vary: If-Modified-Since"); } } } $etag = @join("-", $etags); if (strstr($hinm, $etag)) { header("HTTP/1.1 304 Not Modified"); exit; } if ($etag) { header('ETag: "' . $etag . '"'); } if ($cutarticles) { //header("HTTP/1.1 226 IM Used"); //This should be used as opposed to 200, but Apache doesn't like it. //http://intertwingly.net/blog/2004/09/11/Vary-ETag/ says that the status code should be 200. header("Cache-Control: no-store, im"); header("IM: feed"); } $out = array_merge($out, $articles); ob_start(); header('Content-type: application/atom+xml; charset=utf-8'); return chr(60) . '?xml version="1.0" encoding="UTF-8"?' . chr(62) . n . '<feed version="0.3" xml:lang="' . $language . '" xmlns="http://purl.org/atom/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">' . join(n, $out) . '</feed>'; } }
function doHomeArticles($atts, $thing = NULL) { global $pretext, $prefs; extract($pretext); extract($prefs); $customFields = getCustomFields(); $customlAtts = array_null(array_flip($customFields)); //getting attributes $theAtts = lAtts(array('form' => 'default', 'listform' => '', 'searchform' => '', 'limit' => 10, 'category' => '', 'section' => '', 'excerpted' => '', 'author' => '', 'sort' => '', 'month' => '', 'keywords' => '', 'frontpage' => '', 'time' => 'past', 'pgonly' => 0, 'searchall' => 1, 'allowoverride' => true, 'offset' => 0, 'wraptag' => '', 'break' => '', 'label' => '', 'labeltag' => '', 'class' => '') + $customlAtts, $atts); $theAtts['category'] = $c ? $c : ''; $theAtts['section'] = $s && $s != 'default' && $s != 'home' ? $s : ''; $theAtts['author'] = !empty($author) ? $author : ''; $theAtts['month'] = !empty($month) ? $month : ''; $theAtts['frontpage'] = $s && $s == 'home' ? true : false; $theAtts['excerpted'] = ''; extract($theAtts); // if a listform is specified, $thing is for doArticle() - hence ignore here. if (!empty($listform)) { $thing = ''; } $pageby = empty($pageby) ? $limit : $pageby; $match = $search = ''; if (!$sort) { $sort = 'Posted desc'; } //Building query parts $frontpage = filterFrontPage(); $category = join("','", doSlash(do_list($category))); $category = !$category ? '' : " and (Category1 IN ('" . $category . "') or Category2 IN ('" . $category . "'))"; $section = !$section ? '' : " and Section IN ('" . join("','", doSlash(do_list($section))) . "')"; $excerpted = $excerpted == 'y' ? " and Excerpt !=''" : ''; $author = !$author ? '' : " and AuthorID IN ('" . join("','", doSlash(do_list($author))) . "')"; $month = !$month ? '' : " and Posted like '" . doSlash($month) . "%'"; $id = !$id ? '' : " and ID IN (" . join(',', array_map('intval', do_list($id))) . ")"; switch ($time) { case 'any': $time = ""; break; case 'future': $time = " and Posted > now()"; break; default: $time = " and Posted <= now()"; } if (!$publish_expired_articles) { $time .= " and (now() <= Expires or Expires = " . NULLDATETIME . ")"; } $custom = ''; if ($customFields) { foreach ($customFields as $cField) { if (isset($atts[$cField])) { $customPairs[$cField] = $atts[$cField]; } } if (!empty($customPairs)) { $custom = buildCustomSql($customFields, $customPairs); } } $statusq = ' and Status = 5'; $where = "1=1" . $statusq . $time . $search . $category . $section . $excerpted . $month . $author . $keywords . $custom . $frontpage; $rs = safe_rows_start("*, unix_timestamp(Posted) as uPosted, unix_timestamp(Expires) as uExpires, unix_timestamp(LastMod) as uLastMod" . $match, 'textpattern', $where . ' order by ' . doSlash($sort) . ' limit 0' . intval($limit)); // get the form name $fname = $listform ? $listform : $form; if ($rs) { $count = 0; $last = numRows($rs); $articles = array(); while ($a = nextRow($rs)) { ++$count; populateArticleData($a); global $thisarticle, $uPosted, $limit; $thisarticle['is_first'] = $count == 1; $thisarticle['is_last'] = $count == $last; if (@constant('txpinterface') === 'admin' and gps('Form')) { $articles[] = parse(gps('Form')); } elseif ($allowoverride and $a['override_form']) { $articles[] = parse_form($a['override_form']); } else { $articles[] = $thing ? parse($thing) : parse_form($fname); } // sending these to paging_link(); Required? $uPosted = $a['uPosted']; unset($GLOBALS['thisarticle']); } return doLabel($label, $labeltag) . doWrap($articles, $wraptag, $break, $class); } }
function _textpattern() { global $plugins_ver, $pretext, $prefs, $plugin_callback; $this->debug('Plugin: ' . $this->plugin_name . ' - ' . $plugins_ver[$this->plugin_name]); $this->debug('Function: ' . __FUNCTION__ . '()'); // URI $req = $pretext['req']; $req = preg_replace('%\\?[^\\/]+$%', '', $req); $this->debug('Request URI: ' . $req); $uri = explode('/', trim($req, '/')); // The number of components comes in useful when determining the best partial match. $uri_component_count = count($uri); // Permanent links $permlinks = $this->get_all_permlinks(1); // Force Textpattern and tags to use messy URLs - these are easier to // find in regex $this->set_permlink_mode(); if (count($permlinks)) { // We also want to match the front page of the site (for page numbers / feeds etc..). // Add a permlinks rule which will do that. $permlinks['default'] = array('components' => array(), 'settings' => array('pl_name' => 'gbp_permanent_links_default', 'pl_precedence' => '', 'pl_preview' => '/', 'con_section' => '', 'con_category' => '', 'des_section' => '', 'des_category' => '', 'des_permlink' => '', 'des_feed' => '', 'des_location' => '', 'des_page' => '')); // Extend the pretext_replacement scope outside the foreach permlink loop $pretext_replacement = NULL; foreach ($permlinks as $id => $pl) { // Extract the permlink settings $pl_settings = $pl['settings']; extract($pl_settings); $this->debug('Permlink name: ' . $pl_name); $this->debug('Permlink id: ' . $id); $this->debug('Preview: ' . $pl_preview); $pl_components = $pl['components']; // URI components $uri_components = $uri; $this->debug('PL component count: ' . count($pl_components)); $this->debug('URL component count: ' . count($uri_components)); $date = false; $title_page_feed = false; foreach ($pl_components as $pl_c) { // Are we expecting a date component? If so the number of pl and uri components won't match if ($pl_c['type'] == 'date') { $date = true; } else { if (in_array($pl_c['type'], array('title', 'page', 'feed'))) { $title_page_feed = true; } } } if (!$title_page_feed) { // If there isn't a title, page or feed component then append a special type for cleaver partial matching $pl_components[] = array('type' => 'title_page_feed', 'prefix' => '', 'suffix' => '', 'regex' => '', 'text' => ''); } // Exit early if there are more URL components than PL components, // taking into account whether there is a data component if (!$uri_components[0] || count($uri_components) > count($pl_components) + ($date ? 2 : 0)) { $this->debug('More URL components than PL components'); continue; } // Reset pretext_replacement as we are about to start another comparison $pretext_replacement = array('permlink_id' => $id); // Reset the article context string $context = array(); unset($context_str); if (!empty($des_section)) { $context[] = "`Section` = '{$des_section}'"; } if (!empty($des_category)) { $context[] = "(`Category1` = '{$des_category}' OR `Category2` = '{$des_category}')"; } $context_str = count($context) > 0 ? 'and ' . join(' and ', $context) : ''; // Assume there is no match $partial_match = false; $cleaver_partial_match = false; // Loop through the permlink components foreach ($pl_components as $pl_c_index => $pl_c) { // Assume there is no match $match = false; // Check to see if there are still URI components to be checked. if (count($uri_components)) { // Get the next component. $uri_c = array_shift($uri_components); } else { if (!$title_page_feed && count($pl_components) - 1 == $uri_component_count) { // If we appended a title_page_feed component earlier and permlink and URI components // counts are equal, we must of finished checking this permlink, and it matches so break. $match = true; break; } else { // If there are no more URI components then we have a partial match. // Store the partial match data unless there has been a preceding permlink with the // same number of components, as permlink have already been sorted by precedence. if (!array_key_exists($uri_component_count, $this->partial_matches)) { $this->partial_matches[$uri_component_count] = $pretext_replacement; } // Unset pretext_replacement as changes could of been made in a preceding component unset($pretext_replacement); // Break early form the foreach permlink components loop. $partial_match = true; break; } } // Extract the permlink components. extract($pl_c); // If it's a date, grab and combine the next two URI components. if ($type == 'date') { $uri_c .= '/' . array_shift($uri_components) . '/' . array_shift($uri_components); } // Decode the URL $uri_c = urldecode($uri_c); // Always check the type unless the prefix or suffix aren't there $check_type = true; // Check prefix if ($prefix && $this->pref('show_prefix')) { $sanitized_prefix = urldecode($this->encode_url($prefix)); if (($pos = strpos($uri_c, $sanitized_prefix)) === false || $pos != 0) { $check_type = false; $this->debug('Can\'t find prefix: ' . $prefix); } else { // Check passed, remove prefix ready for the next check $uri_c = substr_replace($uri_c, '', 0, strlen($sanitized_prefix)); } } // Check suffix if ($check_type && $suffix && $this->pref('show_suffix')) { $sanitized_suffix = urldecode($this->encode_url($suffix)); if (($pos = strrpos($uri_c, $sanitized_suffix)) === false) { $check_type = false; $this->debug('Can\'t find suffix: ' . $suffix); } else { // Check passed, remove suffix ready for the next check $uri_c = substr_replace($uri_c, '', $pos, strlen($sanitized_suffix)); } } // Both the prefix and suffix settings have passed if ($check_type) { $this->debug('Checking if "' . $uri_c . '" is of type "' . $type . '"'); $uri_c = doSlash($uri_c); // if ($prefs['permalink_title_format']) { $mt_search = array('/_/', '/\\.html$/'); $mt_replace = array('-', ''); } else { $mt_search = array('/(?:^|_)(.)/e', '/\\.html$/'); $mt_replace = array("strtoupper('\\1')", ''); } $mt_uri_c = $this->pref('redirect_mt_style_links') ? preg_replace($mt_search, $mt_replace, $uri_c) : ''; // Compare based on type switch ($type) { case 'section': if ($rs = safe_row('name', 'txp_section', "(`name` like '{$uri_c}' or `name` like '{$mt_uri_c}') limit 1")) { $this->debug('Section name: ' . $rs['name']); $pretext_replacement['s'] = $rs['name']; $context[] = "`Section` = '{$rs['name']}'"; $match = true; } break; case 'category': if ($rs = safe_row('name', 'txp_category', "(`name` like '{$uri_c}' or `name` like '{$mt_uri_c}') and `type` = 'article' limit 1")) { $this->debug('Category name: ' . $rs['name']); $pretext_replacement['c'] = $rs['name']; $context[] = "(`Category1` = '{$rs['name']}' OR `Category2` = '{$uri_c}')"; $match = true; } break; case 'title': if ($rs = safe_row('url_title', 'textpattern', "(`url_title` like '{$uri_c}' or `url_title` like '{$mt_uri_c}') {$context_str} and `Status` >= 4 limit 1")) { $this->debug('URL Title: ' . $rs['url_title']); $mt_redirect = $uri_c != $mt_uri_c; $pretext_replacement['url_title'] = $rs['url_title']; $match = true; } break; case 'id': if ($rs = safe_row('ID, Posted', 'textpattern', "`ID` = '{$uri_c}' {$context_str} and `Status` >= 4 limit 1")) { $pretext_replacement['id'] = $rs['ID']; $pretext_replacement['Posted'] = $rs['Posted']; $pretext['numPages'] = 1; $pretext['is_article_list'] = false; $match = true; } break; case 'author': if ($author = safe_field('name', 'txp_users', "RealName like '{$uri_c}' limit 1")) { $pretext_replacement['author'] = $author; $context[] = "`AuthorID` = '{$author}'"; $match = true; } break; case 'login': if ($author = safe_field('name', 'txp_users', "name like '{$uri_c}' limit 1")) { $pretext_replacement['author'] = $author; $context[] = "`AuthorID` = '{$author}'"; $match = true; } break; case 'custom': $custom_options = array_values(array_map(array($this, "encode_url"), safe_column("custom_{$custom}", 'textpattern', "custom_{$custom} != ''"))); if ($this->pref('force_lowercase_urls')) { $custom_options = array_map("strtolower", $custom_options); } if (in_array($uri_c, $custom_options)) { $match = true; } break; case 'date': if (preg_match('/^\\d{4}\\/\\d{2}\\/\\d{2}$/', $uri_c)) { $pretext_replacement['date'] = str_replace('/', '-', $uri_c); $match = true; } break; case 'year': if (preg_match('/^\\d{4}$/', $uri_c)) { $pretext_replacement['year'] = $uri_c; $match = true; } break; case 'month': case 'day': if (preg_match('/^\\d{2}$/', $uri_c)) { $pretext_replacement[$type] = $uri_c; $match = true; } break; case 'page': if (is_numeric($uri_c)) { $pretext_replacement['pg'] = $uri_c; $match = true; } break; case 'feed': if (in_array($uri_c, array('rss', 'atom'))) { $pretext_replacement[$uri_c] = 1; $match = true; } break; case 'search': $pretext_replacement['q'] = $uri_c; $match = true; break; case 'text': if ($this->encode_url($text) == $uri_c) { $match = true; $pretext_replacement["permlink_text_{$name}"] = $uri_c; } break; case 'regex': // Check to see if regex is valid without outputting error messages. ob_start(); preg_match($regex, $uri_c, $regex_matches); $is_valid_regex = !ob_get_clean(); if ($is_valid_regex && @$regex_matches[0]) { $match = true; $pretext_replacement["permlink_regex_{$name}"] = $regex_matches[0]; } break; } // switch type end // Update the article context string $context_str = count($context) > 0 ? 'and ' . join(' and ', $context) : ''; $this->debug($match == true ? 'YES' : 'NO'); if (!$match && !$cleaver_partial_match && $this->pref('use_cleaver_partial_matches')) { // There hasn't been a match or a complete cleaver partial match. Lets try to be cleaver and // check to see if this component is either a title, page or a feed. This makes it more probable // a successful match for a given permlink rule occurs. $this->debug('Checking if "' . $uri_c . '" is of type "title_page_feed"'); if ($type != 'title' && ($url_title = safe_field('url_title', 'textpattern', "`url_title` like '{$uri_c}' {$context_str} and `Status` >= 4 limit 1"))) { $pretext_replacement['url_title'] = $url_title; $pretext['numPages'] = 1; $pretext['is_article_list'] = false; $cleaver_partial_match = true; } else { if ($this->pref('clean_page_archive_links') && $type != 'page' && is_numeric($uri_c)) { $pretext_replacement['pg'] = $uri_c; $cleaver_partial_match = true; } else { if ($type != 'feed' && in_array($uri_c, array('rss', 'atom'))) { $pretext_replacement[$uri_c] = 1; $cleaver_partial_match = true; } } } $this->debug($cleaver_partial_match == true ? 'YES' : 'NO'); if ($cleaver_partial_match) { $this->cleaver_partial_match = $pretext_replacement; // Unset pretext_replacement as changes could of been made in a preceding component unset($pretext_replacement); $cleaver_partial_match = true; continue 2; } } } // check type end // Break early if the component doesn't match, as there is no point continuing if ($match == false) { // Unset pretext_replacement as changes could of been made in a preceding component unset($pretext_replacement); break; } } // foreach permlink component end if (!isset($pretext_replacement['id'])) { if (isset($pretext_replacement['url_title'])) { if (isset($pretext_replacement['date'])) { $date_val = $pretext_replacement['date']; } else { if (isset($pretext_replacement['year'])) { $date_val = $pretext_replacement['year']; if (isset($pretext_replacement['month'])) { $date_val .= '-' . $pretext_replacement['month']; if (isset($pretext_replacement['day'])) { $date_val .= '-' . $pretext_replacement['day']; } } } } if (isset($date_val)) { $context_str .= " and `Posted` like '{$date_val}%'"; } if ($rs = safe_row('ID, Posted', 'textpattern', "`url_title` like '{$pretext_replacement['url_title']}' {$context_str} and `Status` >= 4 order by `Posted` desc limit 1")) { if (isset($date_val)) { $this->debug('Found date and title-based match.'); } else { $this->debug('Found title-based match.'); } $pretext_replacement['id'] = $rs['ID']; $pretext_replacement['Posted'] = $rs['Posted']; $pretext['numPages'] = 1; $pretext['is_article_list'] = false; } else { $match = false; unset($pretext_replacement); } } } if ($match || $partial_match || $cleaver_partial_match) { // Extract the settings for this permlink @extract($permlinks[$pretext_replacement['permlink_id']]['settings']); // Check the permlink section and category conditions if (!empty($con_section) && $con_section != @$pretext_replacement['s'] || !empty($con_category) && $con_category != @$pretext_replacement['c']) { $this->debug('Permlink conditions failed'); if (@$con_section) { $this->debug('con_section = ' . $con_section); } if (@$con_category) { $this->debug('con_category = ' . $con_category); } unset($pretext_replacement); } else { if ($match && isset($pretext_replacement)) { $this->debug('We have a match!'); } else { if ($partial_match && count($this->partial_matches)) { $this->debug('We have a \'partial match\''); } else { if ($cleaver_partial_match && isset($cleaver_partial_match)) { $this->debug('We have a \'cleaver partial match\''); } else { $this->debug('Error: Can\'t determine the correct type match'); // This permlink has failed, continue execution of the foreach permlinks loop unset($pretext_replacement); } } } } } // We have a match if (@$pretext_replacement) { break; } } // foreach permlinks end // If there is no match restore the most likely partial match. Sorted by number of components and then precedence if (!@$pretext_replacement && count($this->partial_matches)) { $pt_slice = array_slice($this->partial_matches, -1); $pretext_replacement = array_shift($pt_slice); } unset($this->partial_matches); // Restore the cleaver_partial_match if there is no other matches if (!@$pretext_replacement && $this->cleaver_partial_match) { $pretext_replacement = $this->cleaver_partial_match; } unset($this->cleaver_partial_match); // Extract the settings for this permlink @extract($permlinks[$pretext_replacement['permlink_id']]['settings']); // If pretext_replacement is still set here then we have a match if (@$pretext_replacement) { $this->debug('Pretext Replacement ' . print_r($pretext_replacement, 1)); if (!empty($des_section)) { $pretext_replacement['s'] = $des_section; } if (!empty($des_category)) { $pretext_replacement['c'] = $des_category; } if (!empty($des_feed)) { $pretext_replacement[$des_feed] = 1; } if (@$pretext_replacement['id'] && @$pretext_replacement['Posted']) { if ($np = getNextPrev($pretext_replacement['id'], $pretext_replacement['Posted'], @$pretext_replacement['s'])) { $pretext_replacement = array_merge($pretext_replacement, $np); } } unset($pretext_replacement['Posted']); // If there is a match then we most set the http status correctly as txp's pretext might set it to 404 $pretext_replacement['status'] = '200'; // Store the orginial HTTP status code // We might need to log the page hit if it equals 404 $orginial_status = $pretext['status']; // Txp only looks at the month, but due to how we phase the month we can manipulate the sql to our needs if (array_key_exists('date', $pretext_replacement)) { $pretext_replacement['month'] = $pretext_replacement['date']; unset($pretext_replacement['date']); } else { if (array_key_exists('year', $pretext_replacement) || array_key_exists('month', $pretext_replacement) || array_key_exists('day', $pretext_replacement)) { $month = ''; $month .= array_key_exists('year', $pretext_replacement) ? $pretext_replacement['year'] . '-' : '____-'; $month .= array_key_exists('month', $pretext_replacement) ? $pretext_replacement['month'] . '-' : '__-'; $month .= array_key_exists('day', $pretext_replacement) ? $pretext_replacement['day'] . ' ' : '__ '; $pretext_replacement['month'] = $month; unset($pretext_replacement['year']); unset($pretext_replacement['day']); } } // Section needs to be defined so we can always get a page template. if (!array_key_exists('s', $pretext_replacement)) { if (!@$pretext_replacement['id']) { $pretext_replacement['s'] = 'default'; } else { $pretext_replacement['s'] = safe_field('Section', 'textpattern', 'ID = ' . $pretext_replacement['id']); } } // Set the css and page template, otherwise we get an unknown section $section_settings = safe_row('css, page', 'txp_section', "name = '{$pretext_replacement['s']}' limit 1"); $pretext_replacement['page'] = @$des_page ? $des_page : $section_settings['page']; $pretext_replacement['css'] = $section_settings['css']; $this->matched_permlink = $pretext_replacement; global $permlink_mode; if (in_array($prefs['permlink_mode'], array('id_title', 'section_id_title')) && @$pretext_replacement['pg'] && !@$pretext_replacement['id']) { $pretext_replacement['id'] = ''; $pretext_replacement['is_article_list'] = true; } // Merge pretext_replacement with pretext $pretext = array_merge($pretext, $pretext_replacement); if (is_numeric(@$pretext['id'])) { $a = safe_row('*, unix_timestamp(Posted) as uPosted, unix_timestamp(Expires) as uExpires, unix_timestamp(LastMod) as uLastMod', 'textpattern', 'ID=' . intval($pretext['id']) . ' and Status >= 4'); populateArticleData($a); } // Export required values to the global namespace foreach (array('id', 's', 'c', 'pg', 'is_article_list', 'prev_id', 'prev_title', 'next_id', 'next_title', 'css') as $key) { if (array_key_exists($key, $pretext)) { $GLOBALS[$key] = $pretext[$key]; } } if (count($this->matched_permlink) || @$mt_redirect) { $pl_index = $pretext['permlink_id']; if (!@$mt_redirect || !$this->pref('redirect_mt_style_links')) { $pl = $this->get_permlink($pretext['permlink_id']); $pl_index = @$pl['settings']['des_permlink']; } if (@$pretext['id'] && $pl_index) { if (count($this->get_permlink($pl_index)) > 0) { ob_clean(); global $siteurl; $rs = safe_row('*, ID as thisid, unix_timestamp(Posted) as posted', 'textpattern', "ID = '{$pretext['id']}'"); $host = rtrim(str_replace(rtrim(doStrip($pretext['subpath']), '/'), '', hu), '/'); $this->redirect($host . $this->_permlinkurl($rs, PERMLINKURL, $pl_index), $this->pref('permlink_redirect_http_status')); } } else { if ($url = @$pl['settings']['des_location']) { ob_clean(); $this->redirect($url, $this->pref('url_redirect_http_status')); } } } if (@$pretext['rss']) { if (@$pretext['s']) { $_POST['section'] = $pretext['s']; } if (@$pretext['c']) { $_POST['category'] = $pretext['c']; } ob_clean(); include txpath . '/publish/rss.php'; exit(rss()); } if (@$pretext['atom']) { if (@$pretext['s']) { $_POST['section'] = $pretext['s']; } if (@$pretext['c']) { $_POST['category'] = $pretext['c']; } ob_clean(); include txpath . '/publish/atom.php'; exit(atom()); } $this->debug('Pretext ' . print_r($pretext, 1)); } else { $this->debug('NO CHANGES MADE'); } // Log this page hit if (@$orginial_status == 404) { log_hit($pretext['status']); } // Start output buffering and pseudo callback to textpattern_end ob_start(array(&$this, '_textpattern_end_callback')); // TxP 4.0.5 (r2436) introduced the textpattern_end callback making the following redundant $version = array_sum(array_map(create_function('$line', 'if (preg_match(\'/^\\$' . 'LastChangedRevision: (\\w+) \\$/\', $line, $match)) return $match[1];'), @file(txpath . '/publish.php'))); if ($version >= '2436') { return; } // Remove the plugin callbacks which have already been called function filter_callbacks($c) { if ($c['event'] != 'textpattern') { return true; } if (@$c['function'][0]->plugin_name == 'gbp_permanent_links' && @$c['function'][1] == '_textpattern') { $GLOBALS['gbp_found_self'] = true; return false; } return @$GLOBALS['gbp_found_self']; } $plugin_callback = array_filter($plugin_callback, 'filter_callbacks'); unset($GLOBALS['gbp_found_self']); // Re-call textpattern textpattern(); // Call custom textpattern_end callback $this->_textpattern_end(); // textpattern() has run, kill the connection die; } }
function render_feed($rs, $area, $type, $feedtitle, $atom_self_ref, $atom_id_ext) { global $prefs, $thisarticle; extract($prefs); set_error_handler('tagErrorHandler'); $atom = $type == 'atom'; if ($atom) { define("t_texthtml", ' type="text/html"'); define("t_text", ' type="text"'); define("t_html", ' type="html"'); define("t_xhtml", ' type="xhtml"'); define('t_appxhtml', ' type="xhtml"'); define("r_relalt", ' rel="alternate"'); define("r_relself", ' rel="self"'); } else { define("t_texthtml", ''); define("t_text", ''); define("t_html", ''); define("t_xhtml", ''); define('t_appxhtml', ''); define("r_relalt", ''); define("r_relself", ''); } $dn = explode('/', $siteurl); $mail_or_domain = $use_mail_on_feeds_id ? eE($blog_mail_uid) : $dn[0]; $last = fetch('unix_timestamp(val)', 'txp_prefs', 'name', 'lastmod'); if ($atom) { $out[] = tag('Textpattern', 'generator', ' uri="http://textpattern.com/" version="' . $version . '"'); $out[] = tag(htmlspecialchars($feedtitle), 'title', t_text); $out[] = tag(htmlspecialchars($site_slogan), 'subtitle', t_text); $out[] = tag(safe_strftime("w3cdtf", $last), 'updated'); $out[] = '<link' . r_relself . ' href="' . $atom_self_ref . '" />'; $out[] = '<link' . r_relalt . t_texthtml . ' href="' . hu . '" />'; //Atom feeds with mail or domain name $out[] = tag('tag:' . $mail_or_domain . ',' . $blog_time_uid . ':' . $blog_uid . $atom_id_ext, 'id'); $pub = safe_row("RealName, email", "txp_users", "privs=1"); $auth[] = tag($pub['RealName'], 'name'); $auth[] = $include_email_atom ? tag(eE($pub['email']), 'email') : ''; $auth[] = tag(hu, 'uri'); $out[] = tag(n . t . t . join(n . t . t, $auth) . n, 'author'); } else { $out[] = tag('http://textpattern.com/?v=' . $version, 'generator'); $out[] = tag(doSpecial($feedtitle), 'title'); $out[] = tag(doSpecial($site_slogan), 'description'); $out[] = tag(safe_strftime('rfc822', $last), 'pubDate'); $out[] = tag(hu, 'link'); $out[] = '<atom:link href="' . $atom_self_ref . '" rel="self" type="application/rss+xml" />'; } $out[] = callback_event($atom ? 'atom_head' : 'rss_head'); $articles = array(); if (!$area or $area == 'article') { if ($rs) { while ($a = nextRow($rs)) { extract($a); populateArticleData($a); $cb = callback_event($atom ? 'atom_entry' : 'rss_entry'); $e = array(); $thisauthor = doSpecial(get_author_name($AuthorID)); $thisauthor = tag($thisauthor, $atom ? 'name' : 'dc:creator'); if ($atom) { $thisauthor = tag(n . t . t . t . $thisauthor . n . t . t, 'author'); } $e['thisauthor'] = $thisauthor; if ($atom) { $e['issued'] = tag(safe_strftime('w3cdtf', $uPosted), 'published'); } else { $e['issued'] = tag(safe_strftime('rfc822', $uPosted), 'pubDate'); } if ($atom) { $e['modified'] = tag(safe_strftime('w3cdtf', $uLastMod), 'updated'); $e['category1'] = trim($Category1) ? '<category term="' . doSpecial($Category1) . '" />' : ''; $e['category2'] = trim($Category2) ? '<category term="' . doSpecial($Category2) . '" />' : ''; } $count = ''; if ($show_comment_count_in_feed && $comments_count > 0) { $count = ' [' . $comments_count . ']'; } $escaped_title = $atom ? htmlspecialchars($Title) : htmlspecialchars(strip_tags($Title)); $e['title'] = tag($escaped_title . $count, 'title', t_html); $a['posted'] = $uPosted; $permlink = permlinkurl($a); if ($atom) { $e['link'] = '<link' . r_relalt . t_texthtml . ' href="' . $permlink . '" />'; } else { $e['link'] = tag($permlink, 'link'); } $e['id'] = tag('tag:' . $mail_or_domain . ',' . $feed_time . ':' . $blog_uid . '/' . $uid, $atom ? 'id' : 'guid', $atom ? '' : ' isPermaLink="false"'); $summary = trim(replace_relative_urls(parse($thisarticle['excerpt']), $permlink)); $content = trim(replace_relative_urls(parse($thisarticle['body']), $permlink)); if ($syndicate_body_or_excerpt) { # short feed: use body as summary if there's no excerpt if (!trim($summary)) { $summary = $content; } $content = ''; } if (trim($summary)) { $e['summary'] = tag(n . escape_cdata($summary) . n, $atom ? 'summary' : 'description', t_html); } if (trim($content)) { $e['content'] = tag(n . escape_cdata($content) . n, $atom ? 'content' : 'content:encoded', t_html); } $articles[$ID] = tag(n . t . t . join(n . t . t, $e) . n . $cb, $atom ? 'entry' : 'item'); $etags[$ID] = strtoupper(dechex(crc32($articles[$ID]))); $dates[$ID] = $atom ? $uLastMod : $uPosted; } } } elseif ($area == 'link') { if ($rs) { while ($a = nextRow($rs)) { extract($a); $e['title'] = tag(doSpecial($linkname), 'title', t_html); if ($atom) { $e['content'] = tag(n . doSpecial($description) . n, 'content', t_html); $url = preg_replace("/^\\/(.*)/", "https?://{$siteurl}/\$1", $url); $url = preg_replace("/&((?U).*)=/", "&\\1=", $url); $e['link'] = '<link' . r_relalt . t_texthtml . ' href="' . $url . '" />'; $e['issued'] = tag(safe_strftime('w3cdtf', strtotime($date)), 'published'); $e['modified'] = tag(gmdate('Y-m-d\\TH:i:s\\Z', strtotime($date)), 'updated'); $e['id'] = tag('tag:' . $mail_or_domain . ',' . safe_strftime('%Y-%m-%d', strtotime($date)) . ':' . $blog_uid . '/' . $id, 'id'); } else { $e['content'] = tag(doSpecial($description), 'description'); $e['link'] = tag(doSpecial($url), 'link'); $e['issued'] = tag(safe_strftime('rfc822', $uDate), 'pubDate'); } $articles[$id] = tag(n . t . t . join(n . t . t, $e) . n, $atom ? 'entry' : 'item'); $etags[$id] = strtoupper(dechex(crc32($articles[$id]))); $dates[$id] = $date; } } } if (!$articles) { if ($section) { if (safe_field('name', 'txp_section', "name = '{$section}'") == false) { txp_die(gTxt('404_not_found'), '404'); } } elseif ($category) { switch ($area) { case 'link': if (safe_field('id', 'txp_category', "name = '{$category}' and type = 'link'") == false) { txp_die(gTxt('404_not_found'), '404'); } break; case 'article': default: if (safe_field('id', 'txp_category', "name = '{$category}' and type = 'article'") == false) { txp_die(gTxt('404_not_found'), '404'); } break; } } } else { //turn on compression if we aren't using it already if (extension_loaded('zlib') && ini_get("zlib.output_compression") == 0 && ini_get('output_handler') != 'ob_gzhandler' && !headers_sent()) { // make sure notices/warnings/errors don't // fudge up the feed when compression is used $buf = ''; while ($b = @ob_get_clean()) { $buf .= $b; } @ob_start('ob_gzhandler'); echo $buf; } handle_lastmod(); $hims = serverset('HTTP_IF_MODIFIED_SINCE'); $imsd = $hims ? strtotime($hims) : 0; if (is_callable('apache_request_headers')) { $headers = apache_request_headers(); if (isset($headers["A-IM"])) { $canaim = strpos($headers["A-IM"], "feed"); } else { $canaim = false; } } else { $canaim = false; } $hinm = stripslashes(serverset('HTTP_IF_NONE_MATCH')); $cutarticles = false; if ($canaim !== false) { foreach ($articles as $id => $thing) { if (strpos($hinm, $etags[$id]) !== false) { unset($articles[$id]); $cutarticles = true; $cut_etag = true; } if ($dates[$id] < $imsd) { unset($articles[$id]); $cutarticles = true; $cut_time = true; } } } if (isset($cut_etag) && isset($cut_time)) { header("Vary: If-None-Match, If-Modified-Since"); } else { if (isset($cut_etag)) { header("Vary: If-None-Match"); } else { if (isset($cut_time)) { header("Vary: If-Modified-Since"); } } } $etag = @join("-", $etags); if (strstr($hinm, $etag)) { txp_status_header('304 Not Modified'); exit(0); } if ($etag) { header('ETag: "' . $etag . '"'); } if ($cutarticles) { //header("HTTP/1.1 226 IM Used"); //This should be used as opposed to 200, but Apache doesn't like it. //http://intertwingly.net/blog/2004/09/11/Vary-ETag/ says that the status code should be 200. header("Cache-Control: no-store, im"); header("IM: feed"); } } $out = array_merge($out, $articles); header('Content-type: application/' . ($atom ? 'atom' : 'rss') . '+xml; charset=utf-8'); if ($atom) { return chr(60) . '?xml version="1.0" encoding="UTF-8"?' . chr(62) . n . '<feed xml:lang="' . $language . '" xmlns="http://www.w3.org/2005/Atom">' . n . join(n, $out) . '</feed>'; } else { return '<?xml version="1.0" encoding="utf-8"?>' . n . '<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 . tag(join(n, $out), 'channel') . n . '</rss>'; } }
function rss() { global $prefs, $thisarticle; extract($prefs); extract(doSlash(gpsa(array('category', 'section', 'limit', 'area')))); $area = gps('area'); $sitename .= $section ? ' - ' . $section : ''; $sitename .= $category ? ' - ' . $category : ''; $dn = explode('/', $siteurl); $mail_or_domain = $use_mail_on_feeds_id ? eE($blog_mail_uid) : $dn[0]; $out[] = tag('http://textpattern.com/?v=' . $version, 'generator'); $out[] = tag(doSpecial($sitename), 'title'); $out[] = tag(hu, 'link'); $out[] = tag(doSpecial($site_slogan), 'description'); $last = fetch('unix_timestamp(val)', 'txp_prefs', 'name', 'lastmod'); $out[] = tag(safe_strftime('rfc822', $last), 'pubDate'); $articles = array(); if (!$area or $area == 'article') { $sfilter = $section ? "and Section = '" . $section . "'" : ''; $cfilter = $category ? "and (Category1='" . $category . "' or Category2='" . $category . "')" : ''; $limit = $limit ? $limit : $rss_how_many; $limit = intval(min($limit, max(100, $rss_how_many))); $frs = safe_column("name", "txp_section", "in_rss != '1'"); if ($frs) { foreach ($frs as $f) { $query[] = "and Section != '" . doSlash($f) . "'"; } } $query[] = $sfilter; $query[] = $cfilter; $rs = safe_rows_start("*, unix_timestamp(Posted) as uPosted, ID as thisid", "textpattern", "Status = 4 " . join(' ', $query) . "and Posted < now() order by Posted desc limit {$limit}"); if ($rs) { while ($a = nextRow($rs)) { extract($a); populateArticleData($a); $cb = callback_event('rss_entry'); $a['posted'] = $uPosted; $permlink = permlinkurl($a); $summary = trim(replace_relative_urls(parse($thisarticle['excerpt']), $permlink)); $content = trim(replace_relative_urls(parse($thisarticle['body']), $permlink)); if ($syndicate_body_or_excerpt) { # short feed: use body as summary if there's no excerpt if (!trim($summary)) { $summary = $content; } $content = ''; } if ($show_comment_count_in_feed) { $count = $comments_count > 0 ? ' [' . $comments_count . ']' : ''; } else { $count = ''; } $Title = escape_output(strip_tags($Title)) . $count; $thisauthor = get_author_name($AuthorID); $item = tag($Title, 'title') . n . (trim($summary) ? tag(n . escape_cdata($summary) . n, 'description') . n : '') . (trim($content) ? tag(n . escape_cdata($content) . n, 'content:encoded') . n : '') . tag($permlink, 'link') . n . tag(safe_strftime('rfc822', $a['posted']), 'pubDate') . n . tag(htmlspecialchars($thisauthor), 'dc:creator') . n . tag('tag:' . $mail_or_domain . ',' . $feed_time . ':' . $blog_uid . '/' . $uid, 'guid', ' isPermaLink="false"') . n . $cb; $articles[$ID] = tag($item, 'item'); $etags[$ID] = strtoupper(dechex(crc32($articles[$ID]))); $dates[$ID] = $uPosted; } } } elseif ($area == 'link') { $cfilter = $category ? "category='{$category}'" : '1'; $limit = $limit ? $limit : $rss_how_many; $limit = intval(min($limit, max(100, $rss_how_many))); $rs = safe_rows_start("*, unix_timestamp(date) as uDate", "txp_link", "{$cfilter} order by date desc limit {$limit}"); if ($rs) { while ($a = nextRow($rs)) { extract($a); $item = tag(doSpecial($linkname), 'title') . n . tag(doSpecial($description), 'description') . n . tag(doSpecial($url), 'link') . n . tag(safe_strftime('rfc822', $uDate), 'pubDate'); $articles[$id] = tag($item, 'item'); $etags[$id] = strtoupper(dechex(crc32($articles[$id]))); $dates[$id] = $date; } } } //turn on compression if we aren't using it already if (extension_loaded('zlib') && ini_get("zlib.output_compression") == 0 && ini_get('output_handler') != 'ob_gzhandler' && !headers_sent()) { @ob_start("ob_gzhandler"); } handle_lastmod(); $hims = serverset('HTTP_IF_MODIFIED_SINCE'); $imsd = $hims ? strtotime($hims) : 0; if (is_callable('apache_request_headers')) { $headers = apache_request_headers(); if (isset($headers["A-IM"])) { $canaim = strpos($headers["A-IM"], "feed"); } else { $canaim = false; } } else { $canaim = false; } $hinm = stripslashes(serverset('HTTP_IF_NONE_MATCH')); $cutarticles = false; if ($canaim !== false) { foreach ($articles as $id => $thing) { if (strpos($hinm, $etags[$id]) !== false) { unset($articles[$id]); $cutarticles = true; $cut_etag = true; } if ($dates[$id] < $imsd) { unset($articles[$id]); $cutarticles = true; $cut_time = true; } } } if (isset($cut_etag) && isset($cut_time)) { header("Vary: If-None-Match, If-Modified-Since"); } else { if (isset($cut_etag)) { header("Vary: If-None-Match"); } else { if (isset($cut_time)) { header("Vary: If-Modified-Since"); } } } $etag = @join("-", $etags); if (strstr($hinm, $etag)) { header("HTTP/1.1 304 Not Modified"); exit; } if ($cutarticles) { //header("HTTP/1.1 226 IM Used"); //This should be used as opposed to 200, but Apache doesn't like it. //http://intertwingly.net/blog/2004/09/11/Vary-ETag/ says that the status code should be 200. header("Cache-Control: no-store, im"); header("IM: feed"); } $out = array_merge($out, $articles); header("Content-Type: application/rss+xml; charset=utf-8"); if ($etag) { header('ETag: "' . $etag . '"'); } return '<?xml version="1.0" encoding="utf-8"?>' . n . '<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">' . n . tag(join(n, $out), 'channel') . n . '</rss>'; }
function rss() { global $prefs, $thisarticle; extract($prefs); ob_start(); extract(doSlash(gpsa(array('category', 'section', 'limit', 'area')))); $area = gps('area'); $sitename .= $section ? ' - ' . $section : ''; $sitename .= $category ? ' - ' . $category : ''; $out[] = tag(doSpecial($sitename), 'title'); $out[] = tag(hu, 'link'); $out[] = tag(doSpecial($site_slogan), 'description'); $articles = array(); if (!$area or $area == 'article') { $sfilter = $section ? "and Section = '" . $section . "'" : ''; $cfilter = $category ? "and (Category1='" . $category . "' or Category2='" . $category . "')" : ''; $limit = $limit ? $limit : '5'; $frs = safe_column("name", "txp_section", "in_rss != '1'"); if ($frs) { foreach ($frs as $f) { $query[] = "and Section != '" . $f . "'"; } } $query[] = $sfilter; $query[] = $cfilter; $rs = safe_rows_start("*, unix_timestamp(Posted) as uPosted, ID as thisid", "textpattern", "Status = 4 " . join(' ', $query) . "and Posted < now() order by Posted desc limit {$limit}"); if ($rs) { while ($a = nextRow($rs)) { extract($a); populateArticleData($a); $a['posted'] = $uPosted; $Body = !$syndicate_body_or_excerpt ? $thisarticle['body'] : $thisarticle['excerpt']; $Body = !trim($Body) ? $thisarticle['body'] : $Body; $Body = str_replace('href="/', 'href="' . hu, $Body); $Body = preg_replace("/href=\\\"#(.*)\"/", "href=\"" . permlinkurl($a) . "#\\1\"", $Body); $Body = safe_hed($Body); $Body = preg_replace(array('/</', '/>/', "/'/", '/"/'), array('<', '>', ''', '"'), $Body); // encode bare ampersands $Body = preg_replace("/&(?![#0-9]+;|\\w+;)/i", '&', $Body); $uTitle = $url_title ? $url_title : stripSpace($Title); $uTitle = htmlspecialchars($uTitle, ENT_NOQUOTES); if ($show_comment_count_in_feed) { $dc = getCount('txp_discuss', "parentid={$ID} and visible=1"); $count = $dc > 0 ? ' [' . $dc . ']' : ''; } else { $count = ''; } $Title = doSpecial($Title) . $count; $permlink = permlinkurl($a); $item = tag(strip_tags($Title), 'title') . n . tag($Body, 'description') . n . tag($permlink, 'link'); $articles[$ID] = tag($item, 'item'); $etags[$ID] = strtoupper(dechex(crc32($articles[$ID]))); $dates[$ID] = $uPosted; } } } elseif ($area == 'link') { $cfilter = $category ? "category='{$category}'" : '1'; $limit = $limit ? $limit : 15; $rs = safe_rows_start("*", "txp_link", "{$cfilter} order by date desc limit {$limit}"); if ($rs) { while ($a = nextRow($rs)) { extract($a); $item = tag(doSpecial($linkname), 'title') . n . tag(doSpecial($description), 'description') . n . tag(doSpecial($url), 'link'); $articles[$id] = tag($item, 'item'); $etags[$id] = strtoupper(dechex(crc32($articles[$id]))); $dates[$id] = $date; } } } //turn on compression if we aren't using it already if (extension_loaded('zlib') && ini_get("zlib.output_compression") == 0 && ini_get('output_handler') != 'ob_gzhandler' && !headers_sent()) { ob_start("ob_gzhandler"); } $last = fetch('unix_timestamp(val)', 'txp_prefs', 'name', 'lastmod'); $last = gmdate("D, d M Y H:i:s \\G\\M\\T", $last); header("Last-Modified: {$last}"); $expires = gmdate('D, d M Y H:i:s \\G\\M\\T', time() + 3600 * 1); header("Expires: {$expires}"); $hims = serverset('HTTP_IF_MODIFIED_SINCE'); if ($hims == $last) { header("HTTP/1.1 304 Not Modified"); exit; } $imsd = @strtotime($hims); if (is_callable('apache_request_headers')) { $headers = apache_request_headers(); if (isset($headers["A-IM"])) { $canaim = strpos($headers["A-IM"], "feed"); } else { $canaim = false; } } else { $canaim = false; } $hinm = stripslashes(serverset('HTTP_IF_NONE_MATCH')); $cutarticles = false; if ($canaim !== false) { foreach ($articles as $id => $thing) { if (strpos($hinm, $etags[$id]) !== false) { unset($articles[$id]); $cutarticles = true; $cut_etag = true; } if ($dates[$id] < $imsd) { unset($articles[$id]); $cutarticles = true; $cut_time = true; } } } if (isset($cut_etag) && isset($cut_time)) { header("Vary: If-None-Match, If-Modified-Since"); } else { if (isset($cut_etag)) { header("Vary: If-None-Match"); } else { if (isset($cut_time)) { header("Vary: If-Modified-Since"); } } } $etag = @join("-", $etags); if (strstr($hinm, $etag)) { header("HTTP/1.1 304 Not Modified"); exit; } if ($cutarticles) { //header("HTTP/1.1 226 IM Used"); //This should be used as opposed to 200, but Apache doesn't like it. //http://intertwingly.net/blog/2004/09/11/Vary-ETag/ says that the status code should be 200. header("Cache-Control: no-store, im"); header("IM: feed"); } $out = array_merge($out, $articles); header("Content-Type: application/rss+xml; charset=utf-8"); if ($etag) { header('ETag: "' . $etag . '"'); } return '<rss version="0.92">' . tag(join(n, $out), 'channel') . '</rss>'; }
function doArticle($atts) { global $pretext, $prefs, $thisarticle; extract($prefs); extract($pretext); extract(gpsa(array('parentid', 'preview'))); extract(lAtts(array('allowoverride' => '1', 'form' => 'default', 'status' => '4'), $atts, 0)); if ($status and !is_numeric($status)) { $status = getStatusNum($status); } if (empty($thisarticle) or $thisarticle['thisid'] != $id) { $thisarticle = NULL; $q_status = $status ? 'and Status = ' . intval($status) : 'and Status in (4,5)'; $rs = safe_row("*, unix_timestamp(Posted) as uPosted", "textpattern", 'ID = ' . intval($id) . " {$q_status} limit 1"); if ($rs) { extract($rs); populateArticleData($rs); } } if (!empty($thisarticle) and $thisarticle['status'] == $status) { extract($thisarticle); $thisarticle['is_first'] = 1; $thisarticle['is_last'] = 1; $form = ($allowoverride and $override_form) ? $override_form : $form; $article = parse_form($form); if ($use_comments and $comments_auto_append) { $article .= parse_form('comments_display'); } unset($GLOBALS['thisarticle']); return $article; } }
function rss() { global $prefs, $thisarticle; set_error_handler('feedErrorHandler'); ob_clean(); extract($prefs); extract(doSlash(gpsa(array('limit', 'area')))); // build filter criteria from a comma-separated list of sections and categories $feed_filter_limit = get_pref('feed_filter_limit', 10); $section = gps('section'); $category = gps('category'); if (!is_scalar($section) || !is_scalar($category)) { txp_die('Not Found', 404); } $section = $section ? array_slice(array_unique(do_list($section)), 0, $feed_filter_limit) : array(); $category = $category ? array_slice(array_unique(do_list($category)), 0, $feed_filter_limit) : array(); $st = array(); foreach ($section as $s) { $st[] = fetch_section_title($s); } $ct = array(); foreach ($category as $c) { $ct[] = fetch_category_title($c); } $sitename .= $section ? ' - ' . join(' - ', $st) : ''; $sitename .= $category ? ' - ' . join(' - ', $ct) : ''; $dn = explode('/', $siteurl); $mail_or_domain = $use_mail_on_feeds_id ? eE($blog_mail_uid) : $dn[0]; // feed header $out[] = tag('http://textpattern.com/?v=' . $version, 'generator'); $out[] = tag(doSpecial($sitename), 'title'); $out[] = tag(hu, 'link'); $out[] = '<atom:link href="' . pagelinkurl(array('rss' => 1, 'area' => $area, 'section' => $section, 'category' => $category, 'limit' => $limit)) . '" rel="self" type="application/rss+xml" />'; $out[] = tag(doSpecial($site_slogan), 'description'); $last = fetch('unix_timestamp(val)', 'txp_prefs', 'name', 'lastmod'); $out[] = tag(safe_strftime('rfc822', $last), 'pubDate'); $out[] = callback_event('rss_head'); // feed items $articles = array(); $section = doSlash($section); $category = doSlash($category); if (!$area or $area == 'article') { $sfilter = !empty($section) ? "and Section in ('" . join("','", $section) . "')" : ''; $cfilter = !empty($category) ? "and (Category1 in ('" . join("','", $category) . "') or Category2 in ('" . join("','", $category) . "'))" : ''; $limit = $limit ? $limit : $rss_how_many; $limit = intval(min($limit, max(100, $rss_how_many))); $frs = safe_column("name", "txp_section", "in_rss != '1'"); if ($frs) { foreach ($frs as $f) { $query[] = "and Section != '" . doSlash($f) . "'"; } } $query[] = $sfilter; $query[] = $cfilter; $expired = $publish_expired_articles ? '' : ' and (now() <= Expires or Expires = ' . NULLDATETIME . ') '; $rs = safe_rows_start("*, unix_timestamp(Posted) as uPosted, unix_timestamp(LastMod) as uLastMod, unix_timestamp(Expires) as uExpires, ID as thisid", "textpattern", "Status = 4 " . join(' ', $query) . "and Posted < now()" . $expired . "order by Posted desc limit {$limit}"); if ($rs) { while ($a = nextRow($rs)) { extract($a); populateArticleData($a); $cb = callback_event('rss_entry'); $a['posted'] = $uPosted; $permlink = permlinkurl($a); $summary = trim(replace_relative_urls(parse($thisarticle['excerpt']), $permlink)); $content = trim(replace_relative_urls(parse($thisarticle['body']), $permlink)); if ($syndicate_body_or_excerpt) { # short feed: use body as summary if there's no excerpt if (!trim($summary)) { $summary = $content; } $content = ''; } if ($show_comment_count_in_feed) { $count = $comments_count > 0 ? ' [' . $comments_count . ']' : ''; } else { $count = ''; } $Title = escape_title(strip_tags($Title)) . $count; $thisauthor = get_author_name($AuthorID); $item = tag($Title, 'title') . n . (trim($summary) ? tag(n . escape_cdata($summary) . n, 'description') . n : '') . (trim($content) ? tag(n . escape_cdata($content) . n, 'content:encoded') . n : '') . tag($permlink, 'link') . n . tag(safe_strftime('rfc822', $a['posted']), 'pubDate') . n . tag(htmlspecialchars($thisauthor), 'dc:creator') . n . tag('tag:' . $mail_or_domain . ',' . $feed_time . ':' . $blog_uid . '/' . $uid, 'guid', ' isPermaLink="false"') . n . $cb; $articles[$ID] = tag($item, 'item'); $etags[$ID] = strtoupper(dechex(crc32($articles[$ID]))); $dates[$ID] = $uPosted; } } } elseif ($area == 'link') { $cfilter = $category ? "category in ('" . join("','", $category) . "')" : '1'; $limit = $limit ? $limit : $rss_how_many; $limit = intval(min($limit, max(100, $rss_how_many))); $rs = safe_rows_start("*, unix_timestamp(date) as uDate", "txp_link", "{$cfilter} order by date desc limit {$limit}"); if ($rs) { while ($a = nextRow($rs)) { extract($a); $item = tag(doSpecial($linkname), 'title') . n . tag(doSpecial($description), 'description') . n . tag(doSpecial($url), 'link') . n . tag(safe_strftime('rfc822', $uDate), 'pubDate'); $articles[$id] = tag($item, 'item'); $etags[$id] = strtoupper(dechex(crc32($articles[$id]))); $dates[$id] = $date; } } } if (!$articles) { if ($section) { if (safe_field('name', 'txp_section', "name in ('" . join("','", $section) . "')") == false) { txp_die(gTxt('404_not_found'), '404'); } } elseif ($category) { switch ($area) { case 'link': if (safe_field('id', 'txp_category', "name = '{$category}' and type = 'link'") == false) { txp_die(gTxt('404_not_found'), '404'); } break; case 'article': default: if (safe_field('id', 'txp_category', "name in ('" . join("','", $category) . "') and type = 'article'") == false) { txp_die(gTxt('404_not_found'), '404'); } break; } } } else { //turn on compression if we aren't using it already if (extension_loaded('zlib') && ini_get("zlib.output_compression") == 0 && ini_get('output_handler') != 'ob_gzhandler' && !headers_sent()) { // make sure notices/warnings/errors don't fudge up the feed // when compression is used $buf = ''; while ($b = @ob_get_clean()) { $buf .= $b; } @ob_start('ob_gzhandler'); echo $buf; } handle_lastmod(); $hims = serverset('HTTP_IF_MODIFIED_SINCE'); $imsd = $hims ? strtotime($hims) : 0; if (is_callable('apache_request_headers')) { $headers = apache_request_headers(); if (isset($headers["A-IM"])) { $canaim = strpos($headers["A-IM"], "feed"); } else { $canaim = false; } } else { $canaim = false; } $hinm = stripslashes(serverset('HTTP_IF_NONE_MATCH')); $cutarticles = false; if ($canaim !== false) { foreach ($articles as $id => $thing) { if (strpos($hinm, $etags[$id]) !== false) { unset($articles[$id]); $cutarticles = true; $cut_etag = true; } if ($dates[$id] < $imsd) { unset($articles[$id]); $cutarticles = true; $cut_time = true; } } } if (isset($cut_etag) && isset($cut_time)) { header("Vary: If-None-Match, If-Modified-Since"); } else { if (isset($cut_etag)) { header("Vary: If-None-Match"); } else { if (isset($cut_time)) { header("Vary: If-Modified-Since"); } } } $etag = @join("-", $etags); if (strstr($hinm, $etag)) { txp_status_header('304 Not Modified'); exit(0); } if ($cutarticles) { //header("HTTP/1.1 226 IM Used"); //This should be used as opposed to 200, but Apache doesn't like it. //http://intertwingly.net/blog/2004/09/11/Vary-ETag/ says that the status code should be 200. header("Cache-Control: no-store, im"); header("IM: feed"); } } $out = array_merge($out, $articles); header("Content-Type: application/rss+xml; charset=utf-8"); if (isset($etag)) { header('ETag: "' . $etag . '"'); } return '<?xml version="1.0" encoding="utf-8"?>' . n . '<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 . tag(join(n, $out), 'channel') . n . '</rss>'; }
/** * Formats article info and populates the current article data. * * Fills members of $thisarticle global from a database row. * * Basically just converts an article's date values to UNIX timestamps. * Convenience for those who prefer doing conversion in application end instead * of in the SQL statement. * * @param array $rs An article as an assocative array * @example * article_format_info( * safe_row('*', 'textpattern', 'Status = 4 LIMIT 1') * ) */ function article_format_info($rs) { $rs['uPosted'] = ($unix_ts = @strtotime($rs['Posted'])) !== false ? $unix_ts : null; $rs['uLastMod'] = ($unix_ts = @strtotime($rs['LastMod'])) !== false ? $unix_ts : null; $rs['uExpires'] = ($unix_ts = @strtotime($rs['Expires'])) !== false ? $unix_ts : null; populateArticleData($rs); }
function atom() { global $thisarticle; extract($GLOBALS['prefs']); define("t_texthtml", ' type="text/html"'); define("t_text", ' type="text"'); define("t_html", ' type="html"'); define("t_xhtml", ' type="xhtml"'); define('t_appxhtml', ' type="xhtml"'); define("r_relalt", ' rel="alternate"'); define("r_relself", ' rel="self"'); $area = doSlash(gps('area')); extract(doSlash(gpsa(array('category', 'section', 'limit')))); $last = fetch('unix_timestamp(val)', 'txp_prefs', 'name', 'lastmod'); $sitename .= $section ? ' - ' . $section : ''; $sitename .= $category ? ' - ' . $category : ''; $pub = safe_row("RealName, email", "txp_users", "privs=1"); $out[] = tag(escape_output($sitename), 'title', t_text); $out[] = tag(escape_output($site_slogan), 'subtitle', t_text); $out[] = '<link' . r_relself . ' href="' . pagelinkurl(array('atom' => 1, 'area' => $area, 'section' => $section, 'category' => $category, 'limit' => $limit)) . '" />'; $out[] = '<link' . r_relalt . t_texthtml . ' href="' . hu . '" />'; $articles = array(); //Atom feeds with mail or domain name $dn = explode('/', $siteurl); $mail_or_domain = $use_mail_on_feeds_id ? eE($blog_mail_uid) : $dn[0]; $out[] = tag('tag:' . $mail_or_domain . ',' . $blog_time_uid . ':' . $blog_uid . ($section ? '/' . $section : '') . ($category ? '/' . $category : ''), 'id'); $out[] = tag('Textpattern', 'generator', ' uri="http://textpattern.com/" version="' . $version . '"'); $out[] = tag(safe_strftime("w3cdtf", $last), 'updated'); $auth[] = tag($pub['RealName'], 'name'); $auth[] = $include_email_atom ? tag(eE($pub['email']), 'email') : ''; $auth[] = tag(hu, 'uri'); $out[] = tag(n . t . t . join(n . t . t, $auth) . n, 'author'); if (!$area or $area == 'article') { $sfilter = $section ? "and Section = '" . $section . "'" : ''; $cfilter = $category ? "and (Category1='" . $category . "' or Category2='" . $category . "')" : ''; $limit = $limit ? $limit : $rss_how_many; $limit = intval(min($limit, max(100, $rss_how_many))); $frs = safe_column("name", "txp_section", "in_rss != '1'"); $query = array(); foreach ($frs as $f) { $query[] = "and Section != '" . doSlash($f) . "'"; } $query[] = $sfilter; $query[] = $cfilter; $rs = safe_rows_start("*, \n\t\t\t\tID as thisid, \n\t\t\t\tunix_timestamp(Posted) as uPosted,\n\t\t\t\tunix_timestamp(LastMod) as uLastMod", "textpattern", "Status=4 and Posted <= now() " . join(' ', $query) . "order by Posted desc limit {$limit}"); if ($rs) { while ($a = nextRow($rs)) { extract($a); populateArticleData($a); $cb = callback_event('atom_entry'); $e = array(); $a['posted'] = $uPosted; if ($show_comment_count_in_feed) { $count = $comments_count > 0 ? ' [' . $comments_count . ']' : ''; } else { $count = ''; } $thisauthor = get_author_name($AuthorID); $e['thisauthor'] = tag(n . t . t . t . tag(htmlspecialchars($thisauthor), 'name') . n . t . t, 'author'); $e['issued'] = tag(safe_strftime('w3cdtf', $uPosted), 'published'); $e['modified'] = tag(safe_strftime('w3cdtf', $uLastMod), 'updated'); $escaped_title = escape_output($Title); $e['title'] = tag($escaped_title . $count, 'title', t_html); $permlink = permlinkurl($a); $e['link'] = '<link' . r_relalt . t_texthtml . ' href="' . $permlink . '" />'; $e['id'] = tag('tag:' . $mail_or_domain . ',' . $feed_time . ':' . $blog_uid . '/' . $uid, 'id'); $e['category1'] = trim($Category1) ? '<category term="' . htmlspecialchars($Category1) . '" />' : ''; $e['category2'] = trim($Category2) ? '<category term="' . htmlspecialchars($Category2) . '" />' : ''; $summary = trim(replace_relative_urls(parse($thisarticle['excerpt']), $permlink)); $content = trim(replace_relative_urls(parse($thisarticle['body']), $permlink)); if ($syndicate_body_or_excerpt) { # short feed: use body as summary if there's no excerpt if (!trim($summary)) { $summary = $content; } $content = ''; } if (trim($content)) { $e['content'] = tag(n . escape_cdata($content) . n, 'content', t_html); } if (trim($summary)) { $e['summary'] = tag(n . escape_cdata($summary) . n, 'summary', t_html); } $articles[$ID] = tag(n . t . t . join(n . t . t, $e) . n . $cb, 'entry'); $etags[$ID] = strtoupper(dechex(crc32($articles[$ID]))); $dates[$ID] = $uLastMod; } } } elseif ($area == 'link') { $cfilter = $category ? "category='" . $category . "'" : '1'; $limit = $limit ? $limit : $rss_how_many; $limit = intval(min($limit, max(100, $rss_how_many))); $rs = safe_rows_start("*", "txp_link", "{$cfilter} order by date desc, id desc limit {$limit}"); if ($rs) { while ($a = nextRow($rs)) { extract($a); $e['title'] = tag(htmlspecialchars($linkname), 'title', t_html); $e['content'] = tag(n . htmlspecialchars($description) . n, 'content', t_html); $url = preg_replace("/^\\/(.*)/", "https?://{$siteurl}/\$1", $url); $url = preg_replace("/&((?U).*)=/", "&\\1=", $url); $e['link'] = '<link' . r_relalt . t_texthtml . ' href="' . $url . '" />'; $e['issued'] = tag(safe_strftime('w3cdtf', strtotime($date)), 'published'); $e['modified'] = tag(gmdate('Y-m-d\\TH:i:s\\Z', strtotime($date)), 'updated'); $e['id'] = tag('tag:' . $mail_or_domain . ',' . $feed_time . ':' . $id, 'id'); $articles[$id] = tag(n . t . t . join(n . t . t, $e) . n, 'entry'); $etags[$id] = strtoupper(dechex(crc32($articles[$id]))); $dates[$id] = $date; } } } if (!empty($articles)) { //turn on compression if we aren't using it already if (extension_loaded('zlib') && ini_get("zlib.output_compression") == 0 && ini_get('output_handler') != 'ob_gzhandler' && !headers_sent()) { @ob_start("ob_gzhandler"); } handle_lastmod(); $hims = serverset('HTTP_IF_MODIFIED_SINCE'); $imsd = $hims ? strtotime($hims) : 0; if ($imsd >= $last) { txp_status_header("304 Not Modified"); exit; } header("Last-Modified: " . gmdate('D, d M Y H:i:s \\G\\M\\T', $last)); if (is_callable('apache_request_headers')) { $headers = apache_request_headers(); if (isset($headers["A-IM"])) { $canaim = strpos($headers["A-IM"], "feed"); } else { $canaim = false; } } else { $canaim = false; } $hinm = stripslashes(serverset('HTTP_IF_NONE_MATCH')); $cutarticles = false; if ($canaim !== false) { foreach ($articles as $id => $thing) { if (strpos($hinm, $etags[$id])) { unset($articles[$id]); $cutarticles = true; $cut_etag = true; } if ($dates[$id] < $imsd) { unset($articles[$id]); $cutarticles = true; $cut_time = true; } } } if (isset($cut_etag) && isset($cut_time)) { header("Vary: If-None-Match, If-Modified-Since"); } else { if (isset($cut_etag)) { header("Vary: If-None-Match"); } else { if (isset($cut_time)) { header("Vary: If-Modified-Since"); } } } $etag = @join("-", $etags); if (strstr($hinm, $etag)) { header("HTTP/1.1 304 Not Modified"); exit; } if ($etag) { header('ETag: "' . $etag . '"'); } if ($cutarticles) { //header("HTTP/1.1 226 IM Used"); //This should be used as opposed to 200, but Apache doesn't like it. //http://intertwingly.net/blog/2004/09/11/Vary-ETag/ says that the status code should be 200. header("Cache-Control: no-store, im"); header("IM: feed"); } $out = array_merge($out, $articles); header('Content-type: application/atom+xml; charset=utf-8'); return chr(60) . '?xml version="1.0" encoding="UTF-8"?' . chr(62) . n . '<feed xml:lang="' . $language . '" xmlns="http://www.w3.org/2005/Atom">' . join(n, $out) . '</feed>'; } }
function zem_event_list($atts, $thing = NULL) { global $zem_thiseventcal, $pretext; extract(lAtts(array('wraptag' => '', 'class' => __FUNCTION__, 'break' => '', 'breakclass' => '', 'form' => 'zem_event_display', 'sort' => safe_pfx('zem_event_date') . '.event_date asc, ' . safe_pfx('zem_event_date') . '.event_time asc', 'date_from' => gps('date_from') ? gps('date_from') : 'today', 'date_to' => gps('date_to') ? gps('date_to') : '', 'date' => gps('date'), 'label' => '', 'labeltag' => '', 'limit' => '', 'category' => gps('c') ? gps('c') : @$pretext['c'], 'section' => '', 'all_categories' => gps('all_categories'), 'location' => gps('location'), 'all_locations' => gps('all_locations'), 'debug' => 0), $atts)); if ($thing === NULL) { $thing = fetch_form($form); } $where = safe_pfx('zem_event_calendar') . '.id=' . safe_pfx('zem_event_date') . '.event_id and ' . safe_pfx('zem_event_calendar') . '.article_id = ' . safe_pfx('textpattern') . '.ID and ' . safe_pfx('textpattern') . '.Status >= 4 and ' . safe_pfx('textpattern') . '.Posted <= now()'; if ($date) { @(list($y, $m, $d) = explode('-', $date)); if ($y and $m and $d) { $date_from = $date_to = "{$y}-{$m}-{$d}"; } elseif ($y and $m) { $date_from = "{$y}-{$m}-01"; $date_to = strftime('%Y-%m-%d', strtotime('-1 day', strtotime('+1 month', strtotime($date_from)))); } elseif ($y) { $date_from = "{$y}-01-01"; $date_to = "{$y}-12-31"; } elseif ($t = zem_strtotime($date)) { $date_from = strftime('%Y-%m-%d', $t); $date_to = strftime('%Y-%m-%d', $t); } } $w = zem_event_timeq($date_from, $date_to); if ($w) { $where .= ' and ' . join(' and ', $w); } if ($section) { $sections = do_list($section); $where .= " and textpattern.Section IN (" . join(',', quote_list($sections)) . ")"; } if ($location and !$all_locations) { // location could be an array if it came from gps() if (is_array($location)) { $locs = $location; } else { $locs = do_list($location); } $where .= " and location IN (" . join(',', quote_list($locs)) . ")"; } if ($q = gps('q')) { $where .= " and (name rlike '" . doSlash($q) . "' or description rlike '" . doSlash($q) . "')"; } if ($category and !$all_categories) { // category could be an array if it came from gps() if (is_array($category)) { $cats = $category; } else { $cats = do_list($category); } $cats_id = safe_column('id', 'txp_category', "type='event' and name IN (" . join(',', quote_list($cats)) . ")"); if (!$cats_id) { $cats_id = array(0); } $where = safe_pfx('zem_event_calendar') . ".id=" . safe_pfx('zem_event_category') . ".k1 and " . safe_pfx('zem_event_category') . ".k2 IN (" . join(',', quote_list($cats_id)) . ") and " . $where; $grand_total = safe_count('zem_event_calendar,zem_event_date,textpattern,zem_event_category', $where . ' group by ' . safe_pfx('zem_event_calendar') . '.id order by ' . $sort, $debug); $lim = zem_event_paginate($limit, $grand_total); $rs = safe_rows_start(safe_pfx('zem_event_calendar') . '.*, ' . safe_pfx('zem_event_date') . '.*, ' . safe_pfx('textpattern') . '.*, unix_timestamp(Posted) as uPosted', 'zem_event_calendar,zem_event_date,textpattern,zem_event_category', $where . ' group by ' . safe_pfx('zem_event_calendar') . '.id order by ' . $sort . $lim, $debug); } else { $grand_total = safe_count('zem_event_calendar,zem_event_date,textpattern', $where . ' order by ' . $sort, $debug); $lim = zem_event_paginate($limit, $grand_total); $rs = safe_rows_start('*, unix_timestamp(Posted) as uPosted', 'zem_event_calendar,zem_event_date,textpattern', $where . ' order by ' . $sort . $lim, $debug); } $out = array(); while ($row = nextRow($rs)) { article_push(); $zem_thiseventcal = $row; populateArticleData($row); $out[] = parse($thing); $zem_thiseventcal = NULL; article_pop(); } return doTag($label, $labeltag, $class) . doWrap($out, $wraptag, $break, $class, $breakclass); }