function ShowXmlFeed() { global $board, $board_info, $context, $scripturl, $txt, $modSettings, $user_info; global $query_this_board, $smcFunc, $forum_version, $cdata_override; // If it's not enabled, die. if (empty($modSettings['xmlnews_enable'])) { obExit(false); } loadLanguage('Stats'); // Default to latest 5. No more than 255, please. $_GET['limit'] = empty($_GET['limit']) || (int) $_GET['limit'] < 1 ? 5 : min((int) $_GET['limit'], 255); // Handle the cases where a board, boards, or category is asked for. $query_this_board = 1; $context['optimize_msg'] = array('highest' => 'm.id_msg <= b.id_last_msg'); if (!empty($_REQUEST['c']) && empty($board)) { $_REQUEST['c'] = explode(',', $_REQUEST['c']); foreach ($_REQUEST['c'] as $i => $c) { $_REQUEST['c'][$i] = (int) $c; } if (count($_REQUEST['c']) == 1) { $request = smf_db_query(' SELECT name FROM {db_prefix}categories WHERE id_cat = {int:current_category}', array('current_category' => (int) $_REQUEST['c'][0])); list($feed_title) = mysql_fetch_row($request); mysql_free_result($request); $feed_title = ' - ' . strip_tags($feed_title); } $request = smf_db_query(' SELECT b.id_board, b.num_posts FROM {db_prefix}boards AS b WHERE b.id_cat IN ({array_int:current_category_list}) AND {query_see_board}', array('current_category_list' => $_REQUEST['c'])); $total_cat_posts = 0; $boards = array(); while ($row = mysql_fetch_assoc($request)) { $boards[] = $row['id_board']; $total_cat_posts += $row['num_posts']; } mysql_free_result($request); if (!empty($boards)) { $query_this_board = 'b.id_board IN (' . implode(', ', $boards) . ')'; } // Try to limit the number of messages we look through. if ($total_cat_posts > 100 && $total_cat_posts > $modSettings['totalMessages'] / 15) { $context['optimize_msg']['lowest'] = 'm.id_msg >= ' . max(0, $modSettings['maxMsgID'] - 400 - $_GET['limit'] * 5); } } elseif (!empty($_REQUEST['boards'])) { $_REQUEST['boards'] = explode(',', $_REQUEST['boards']); foreach ($_REQUEST['boards'] as $i => $b) { $_REQUEST['boards'][$i] = (int) $b; } $request = smf_db_query(' SELECT b.id_board, b.num_posts, b.name FROM {db_prefix}boards AS b WHERE b.id_board IN ({array_int:board_list}) AND {query_see_board} LIMIT ' . count($_REQUEST['boards']), array('board_list' => $_REQUEST['boards'])); // Either the board specified doesn't exist or you have no access. $num_boards = mysql_num_rows($request); if ($num_boards == 0) { fatal_lang_error('no_board'); } $total_posts = 0; $boards = array(); while ($row = mysql_fetch_assoc($request)) { if ($num_boards == 1) { $feed_title = ' - ' . strip_tags($row['name']); } $boards[] = $row['id_board']; $total_posts += $row['num_posts']; } mysql_free_result($request); if (!empty($boards)) { $query_this_board = 'b.id_board IN (' . implode(', ', $boards) . ')'; } // The more boards, the more we're going to look through... if ($total_posts > 100 && $total_posts > $modSettings['totalMessages'] / 12) { $context['optimize_msg']['lowest'] = 'm.id_msg >= ' . max(0, $modSettings['maxMsgID'] - 500 - $_GET['limit'] * 5); } } elseif (!empty($board)) { $request = smf_db_query(' SELECT num_posts FROM {db_prefix}boards WHERE id_board = {int:current_board} LIMIT 1', array('current_board' => $board)); list($total_posts) = mysql_fetch_row($request); mysql_free_result($request); $feed_title = ' - ' . strip_tags($board_info['name']); $query_this_board = 'b.id_board = ' . $board; // Try to look through just a few messages, if at all possible. if ($total_posts > 80 && $total_posts > $modSettings['totalMessages'] / 10) { $context['optimize_msg']['lowest'] = 'm.id_msg >= ' . max(0, $modSettings['maxMsgID'] - 600 - $_GET['limit'] * 5); } } else { $query_this_board = '{query_see_board}' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? ' AND b.id_board != ' . $modSettings['recycle_board'] : ''); $context['optimize_msg']['lowest'] = 'm.id_msg >= ' . max(0, $modSettings['maxMsgID'] - 100 - $_GET['limit'] * 5); } // Show in rss or proprietary format? $xml_format = isset($_GET['type']) && in_array($_GET['type'], array('smf', 'rss', 'rss2', 'atom', 'rdf', 'webslice')) ? $_GET['type'] : 'smf'; // !!! Birthdays? // List all the different types of data they can pull. $subActions = array('recent' => array('getXmlRecent', 'recent-post'), 'news' => array('getXmlNews', 'article'), 'members' => array('getXmlMembers', 'member'), 'profile' => array('getXmlProfile', null)); if (empty($_GET['sa']) || !isset($subActions[$_GET['sa']])) { $_GET['sa'] = 'recent'; } //!!! Temp - webslices doesn't do everything yet. if ($xml_format == 'webslice' && $_GET['sa'] != 'recent') { $xml_format = 'rss2'; } elseif ($xml_format == 'webslice') { $context['user'] += $user_info; $cdata_override = true; loadTemplate('Xml'); } // We only want some information, not all of it. $cachekey = array($xml_format, $_GET['action'], $_GET['limit'], $_GET['sa']); foreach (array('board', 'boards', 'c') as $var) { if (isset($_REQUEST[$var])) { $cachekey[] = $_REQUEST[$var]; } } $cachekey = md5(serialize($cachekey) . (!empty($query_this_board) ? $query_this_board : '')); $cache_t = microtime(); // Get the associative array representing the xml. if (!empty($modSettings['cache_enable']) && (!$user_info['is_guest'] || $modSettings['cache_enable'] >= 3)) { $xml = CacheAPI::getCache('xmlfeed-' . $xml_format . ':' . ($user_info['is_guest'] ? '' : $user_info['id'] . '-') . $cachekey, 240); } if (empty($xml)) { $xml = $subActions[$_GET['sa']][0]($xml_format); if (!empty($modSettings['cache_enable']) && ($user_info['is_guest'] && $modSettings['cache_enable'] >= 3 || !$user_info['is_guest'] && array_sum(explode(' ', microtime())) - array_sum(explode(' ', $cache_t)) > 0.2)) { CacheAPI::putCache('xmlfeed-' . $xml_format . ':' . ($user_info['is_guest'] ? '' : $user_info['id'] . '-') . $cachekey, $xml, 240); } } $feed_title = htmlspecialchars(strip_tags($context['forum_name'])) . (isset($feed_title) ? $feed_title : ''); // This is an xml file.... ob_end_clean(); if (!empty($modSettings['enableCompressedOutput'])) { @ob_start('ob_gzhandler'); } else { ob_start(); } if ($xml_format == 'smf' || isset($_REQUEST['debug'])) { header('Content-Type: text/xml; charset=UTF-8'); } elseif ($xml_format == 'rss' || $xml_format == 'rss2' || $xml_format == 'webslice') { header('Content-Type: application/rss+xml; charset=UTF-8'); } elseif ($xml_format == 'atom') { header('Content-Type: application/atom+xml; charset=UTF-8'); } elseif ($xml_format == 'rdf') { header('Content-Type: ' . ($context['browser']['is_ie'] ? 'text/xml' : 'application/rdf+xml') . '; charset=UTF-8'); } // First, output the xml header. echo '<?xml version="1.0" encoding="UTF-8"?' . '>'; // Are we outputting an rss feed or one with more information? if ($xml_format == 'rss' || $xml_format == 'rss2') { // Start with an RSS 2.0 header. echo ' <rss version=', $xml_format == 'rss2' ? '"2.0"' : '"0.92"', ' xml:lang="', strtr($txt['lang_locale'], '_', '-'), '"> <channel> <title>', $feed_title, '</title> <link>', $scripturl, '</link> <description><![CDATA[', strip_tags($txt['xml_rss_desc']), ']]></description>'; // Output all of the associative array, start indenting with 2 tabs, and name everything "item". dumpTags($xml, 2, 'item', $xml_format); // Output the footer of the xml. echo ' </channel> </rss>'; } elseif ($xml_format == 'webslice') { $context['recent_posts_data'] = $xml; // This always has RSS 2 echo ' <rss version="2.0" xmlns:mon="http://www.microsoft.com/schemas/rss/monitoring/2007" xml:lang="', strtr($txt['lang_locale'], '_', '-'), '"> <channel> <title>', $feed_title, ' - ', $txt['recent_posts'], '</title> <link>', $scripturl, '?action=recent</link> <description><![CDATA[', strip_tags($txt['xml_rss_desc']), ']]></description> <item> <title>', $feed_title, ' - ', $txt['recent_posts'], '</title> <link>', $scripturl, '?action=recent</link> <description><![CDATA[ ', template_webslice_header_above(), ' ', template_webslice_recent_posts(), ' ', template_webslice_header_below(), ' ]]></description> </item> </channel> </rss>'; } elseif ($xml_format == 'atom') { echo ' <feed xmlns="http://www.w3.org/2005/Atom"> <title>', $feed_title, '</title> <link rel="alternate" type="text/html" href="', $scripturl, '" /> <modified>', gmstrftime('%Y-%m-%dT%H:%M:%SZ'), '</modified> <tagline><![CDATA[', strip_tags($txt['xml_rss_desc']), ']]></tagline> <generator uri="http://www.simplemachines.org" version="', strtr($forum_version, array('SMF' => '')), '">SMF</generator> <author> <name>', strip_tags($context['forum_name']), '</name> </author>'; dumpTags($xml, 2, 'entry', $xml_format); echo ' </feed>'; } elseif ($xml_format == 'rdf') { echo ' <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns="http://purl.org/rss/1.0/"> <channel rdf:about="', $scripturl, '"> <title>', $feed_title, '</title> <link>', $scripturl, '</link> <description><![CDATA[', strip_tags($txt['xml_rss_desc']), ']]></description> <items> <rdf:Seq>'; foreach ($xml as $item) { echo ' <rdf:li rdf:resource="', $item['link'], '" />'; } echo ' </rdf:Seq> </items> </channel> '; dumpTags($xml, 1, 'item', $xml_format); echo ' </rdf:RDF>'; } else { echo ' <smf:xml-feed xmlns:smf="http://www.simplemachines.org/" xmlns="http://www.simplemachines.org/xml/', $_GET['sa'], '" xml:lang="', strtr($txt['lang_locale'], '_', '-'), '">'; // Dump out that associative array. Indent properly.... and use the right names for the base elements. dumpTags($xml, 1, $subActions[$_GET['sa']][1], $xml_format); echo ' </smf:xml-feed>'; } obExit(false); }
/** * Outputs xml data representing recent information or a profile. * * What it does: * - Can be passed 4 subactions which decide what is output: * 'recent' for recent posts, * 'news' for news topics, * 'members' for recently registered members, * 'profile' for a member's profile. * - To display a member's profile, a user id has to be given. (;u=1) e.g. ?action=.xml;sa=profile;u=1;type=atom * - Outputs an rss feed instead of a proprietary one if the 'type' $_GET * parameter is 'rss' or 'rss2'. * - Accessed via ?action=.xml * - Does not use any templates, sub templates, or template layers. * * @uses Stats language file. */ public function action_showfeed() { global $board, $board_info, $context, $scripturl, $boardurl, $txt, $modSettings, $user_info; global $forum_version, $cdata_override, $settings; // If it's not enabled, die. if (empty($modSettings['xmlnews_enable'])) { obExit(false); } loadLanguage('Stats'); $txt['xml_rss_desc'] = replaceBasicActionUrl($txt['xml_rss_desc']); // Default to latest 5. No more than whats defined in the ACP or 255 $limit = empty($modSettings['xmlnews_limit']) ? 5 : min($modSettings['xmlnews_limit'], 255); $this->_limit = empty($_GET['limit']) || (int) $_GET['limit'] < 1 ? $limit : min((int) $_GET['limit'], $limit); // Handle the cases where a board, boards, or category is asked for. $this->_query_this_board = '1=1'; $context['optimize_msg'] = array('highest' => 'm.id_msg <= b.id_last_msg'); if (!empty($_REQUEST['c']) && empty($board)) { $categories = array_map('intval', explode(',', $_REQUEST['c'])); if (count($categories) == 1) { require_once SUBSDIR . '/Categories.subs.php'; $feed_title = categoryName($categories[0]); $feed_title = ' - ' . strip_tags($feed_title); } require_once SUBSDIR . '/Boards.subs.php'; $boards_posts = boardsPosts(array(), $categories); $total_cat_posts = array_sum($boards_posts); $boards = array_keys($boards_posts); if (!empty($boards)) { $this->_query_this_board = 'b.id_board IN (' . implode(', ', $boards) . ')'; } // Try to limit the number of messages we look through. if ($total_cat_posts > 100 && $total_cat_posts > $modSettings['totalMessages'] / 15) { $context['optimize_msg']['lowest'] = 'm.id_msg >= ' . max(0, $modSettings['maxMsgID'] - 400 - $this->_limit * 5); } } elseif (!empty($_REQUEST['boards'])) { require_once SUBSDIR . '/Boards.subs.php'; $query_boards = array_map('intval', explode(',', $_REQUEST['boards'])); $boards_data = fetchBoardsInfo(array('boards' => $query_boards), array('selects' => 'detailed')); // Either the board specified doesn't exist or you have no access. $num_boards = count($boards_data); if ($num_boards == 0) { fatal_lang_error('no_board'); } $total_posts = 0; $boards = array_keys($boards_data); foreach ($boards_data as $row) { if ($num_boards == 1) { $feed_title = ' - ' . strip_tags($row['name']); } $total_posts += $row['num_posts']; } $this->_query_this_board = 'b.id_board IN (' . implode(', ', $boards) . ')'; // The more boards, the more we're going to look through... if ($total_posts > 100 && $total_posts > $modSettings['totalMessages'] / 12) { $context['optimize_msg']['lowest'] = 'm.id_msg >= ' . max(0, $modSettings['maxMsgID'] - 500 - $this->_limit * 5); } } elseif (!empty($board)) { require_once SUBSDIR . '/Boards.subs.php'; $boards_data = fetchBoardsInfo(array('boards' => $board), array('selects' => 'posts')); $feed_title = ' - ' . strip_tags($board_info['name']); $this->_query_this_board = 'b.id_board = ' . $board; // Try to look through just a few messages, if at all possible. if ($boards_data[$board]['num_posts'] > 80 && $boards_data[$board]['num_posts'] > $modSettings['totalMessages'] / 10) { $context['optimize_msg']['lowest'] = 'm.id_msg >= ' . max(0, $modSettings['maxMsgID'] - 600 - $this->_limit * 5); } } else { $this->_query_this_board = '{query_see_board}' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? ' AND b.id_board != ' . $modSettings['recycle_board'] : ''); $context['optimize_msg']['lowest'] = 'm.id_msg >= ' . max(0, $modSettings['maxMsgID'] - 100 - $this->_limit * 5); } // If format isn't set, rss2 is default $xml_format = isset($_GET['type']) && in_array($_GET['type'], array('rss', 'rss2', 'atom', 'rdf', 'webslice')) ? $_GET['type'] : 'rss2'; // List all the different types of data they can pull. $subActions = array('recent' => array('action_xmlrecent'), 'news' => array('action_xmlnews'), 'members' => array('action_xmlmembers'), 'profile' => array('action_xmlprofile')); // Easy adding of sub actions call_integration_hook('integrate_xmlfeeds', array(&$subActions)); $subAction = isset($_GET['sa']) && isset($subActions[$_GET['sa']]) ? $_GET['sa'] : 'recent'; // Webslices doesn't do everything (yet? ever?) so for now only recent posts is allowed in that format if ($xml_format == 'webslice' && $subAction != 'recent') { $xml_format = 'rss2'; } elseif ($xml_format == 'webslice') { $context['user'] += $user_info; $cdata_override = true; loadTemplate('Xml'); } // We only want some information, not all of it. $cachekey = array($xml_format, $_GET['action'], $this->_limit, $subAction); foreach (array('board', 'boards', 'c') as $var) { if (isset($_REQUEST[$var])) { $cachekey[] = $_REQUEST[$var]; } } $cachekey = md5(serialize($cachekey) . (!empty($this->_query_this_board) ? $this->_query_this_board : '')); $cache_t = microtime(true); // Get the associative array representing the xml. if (!empty($modSettings['cache_enable']) && (!$user_info['is_guest'] || $modSettings['cache_enable'] >= 3)) { $xml = cache_get_data('xmlfeed-' . $xml_format . ':' . ($user_info['is_guest'] ? '' : $user_info['id'] . '-') . $cachekey, 240); } if (empty($xml)) { $xml = $this->{$subActions[$subAction][0]}($xml_format); if (!empty($modSettings['cache_enable']) && ($user_info['is_guest'] && $modSettings['cache_enable'] >= 3 || !$user_info['is_guest'] && microtime(true) - $cache_t > 0.2)) { cache_put_data('xmlfeed-' . $xml_format . ':' . ($user_info['is_guest'] ? '' : $user_info['id'] . '-') . $cachekey, $xml, 240); } } $feed_title = encode_special(strip_tags(un_htmlspecialchars($context['forum_name']) . (isset($feed_title) ? $feed_title : ''))); // This is an xml file.... @ob_end_clean(); if (!empty($modSettings['enableCompressedOutput'])) { ob_start('ob_gzhandler'); } else { ob_start(); } if (isset($_REQUEST['debug'])) { header('Content-Type: text/xml; charset=UTF-8'); } elseif ($xml_format == 'rss' || $xml_format == 'rss2' || $xml_format == 'webslice') { header('Content-Type: application/rss+xml; charset=UTF-8'); } elseif ($xml_format == 'atom') { header('Content-Type: application/atom+xml; charset=UTF-8'); } elseif ($xml_format == 'rdf') { header('Content-Type: ' . (isBrowser('ie') ? 'text/xml' : 'application/rdf+xml') . '; charset=UTF-8'); } // First, output the xml header. echo '<?xml version="1.0" encoding="UTF-8"?' . '>'; // Are we outputting an rss feed or one with more information? if ($xml_format == 'rss' || $xml_format == 'rss2') { // Start with an RSS 2.0 header. echo ' <rss version=', $xml_format == 'rss2' ? '"2.0" xmlns:dc="http://purl.org/dc/elements/1.1/"' : '"0.92"', ' xml:lang="', strtr($txt['lang_locale'], '_', '-'), '"> <channel> <title>', $feed_title, '</title> <link>', $scripturl, '</link> <description><![CDATA[', un_htmlspecialchars(strip_tags($txt['xml_rss_desc'])), ']]></description> <generator>ElkArte</generator> <ttl>30</ttl> <image> <url>', $settings['default_theme_url'], '/images/logo.png</url> <title>', $feed_title, '</title> <link>', $scripturl, '</link> </image>'; // Output all of the associative array, start indenting with 2 tabs, and name everything "item". dumpTags($xml, 2, 'item', $xml_format); // Output the footer of the xml. echo ' </channel> </rss>'; } elseif ($xml_format == 'webslice') { // Format specification http://msdn.microsoft.com/en-us/library/cc304073%28VS.85%29.aspx // Known browsers to support webslices: IE8, IE9, Firefox with Webchunks addon. // It uses RSS 2. // We send a feed with recent posts, and alerts for PMs for logged in users $context['recent_posts_data'] = $xml; $context['can_pm_read'] = allowedTo('pm_read'); // This always has RSS 2 echo ' <rss version="2.0" xmlns:mon="http://www.microsoft.com/schemas/rss/monitoring/2007" xml:lang="', strtr($txt['lang_locale'], '_', '-'), '"> <channel> <title>', $feed_title, ' - ', $txt['recent_posts'], '</title> <link>', $scripturl, '?action=recent</link> <description><![CDATA[', strip_tags($txt['xml_rss_desc']), ']]></description> <item> <title>', $feed_title, ' - ', $txt['recent_posts'], '</title> <link>', $scripturl, '?action=recent</link> <description><![CDATA[ ', template_webslice_header_above(), ' ', template_webslice_recent_posts(), ' ]]></description> </item> </channel> </rss>'; } elseif ($xml_format == 'atom') { $url_parts = array(); foreach (array('board', 'boards', 'c') as $var) { if (isset($_REQUEST[$var])) { $url_parts[] = $var . '=' . (is_array($_REQUEST[$var]) ? implode(',', $_REQUEST[$var]) : $_REQUEST[$var]); } } echo ' <feed xmlns="http://www.w3.org/2005/Atom"> <title>', $feed_title, '</title> <link rel="alternate" type="text/html" href="', $scripturl, '" /> <link rel="self" type="application/rss+xml" href="', $scripturl, '?type=atom;action=.xml', !empty($url_parts) ? ';' . implode(';', $url_parts) : '', '" /> <id>', $scripturl, '</id> <icon>', $boardurl, '/favicon.ico</icon> <updated>', gmstrftime('%Y-%m-%dT%H:%M:%SZ'), '</updated> <subtitle><![CDATA[', strip_tags(un_htmlspecialchars($txt['xml_rss_desc'])), ']]></subtitle> <generator uri="http://www.elkarte.net" version="', strtr($forum_version, array('ElkArte' => '')), '">ElkArte</generator> <author> <name>', strip_tags(un_htmlspecialchars($context['forum_name'])), '</name> </author>'; dumpTags($xml, 2, 'entry', $xml_format); echo ' </feed>'; } else { echo ' <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns="http://purl.org/rss/1.0/"> <channel rdf:about="', $scripturl, '"> <title>', $feed_title, '</title> <link>', $scripturl, '</link> <description><![CDATA[', strip_tags($txt['xml_rss_desc']), ']]></description> <items> <rdf:Seq>'; foreach ($xml as $item) { echo ' <rdf:li rdf:resource="', $item['link'], '" />'; } echo ' </rdf:Seq> </items> </channel> '; dumpTags($xml, 1, 'item', $xml_format); echo ' </rdf:RDF>'; } obExit(false); }