Yoshi2889 /
SMF2.1
| 1 | <?php |
||
| 2 | |||
| 3 | /** |
||
| 4 | * This file contains the files necessary to display news as an XML feed. |
||
| 5 | * |
||
| 6 | * Simple Machines Forum (SMF) |
||
| 7 | * |
||
| 8 | * @package SMF |
||
| 9 | * @author Simple Machines https://www.simplemachines.org |
||
| 10 | * @copyright 2020 Simple Machines and individual contributors |
||
| 11 | * @license https://www.simplemachines.org/about/smf/license.php BSD |
||
| 12 | * |
||
| 13 | * @version 2.1 RC2 |
||
| 14 | */ |
||
| 15 | |||
| 16 | if (!defined('SMF')) |
||
| 17 | die('No direct access...'); |
||
| 18 | |||
| 19 | /** |
||
| 20 | * Outputs xml data representing recent information or a profile. |
||
| 21 | * Can be passed 4 subactions which decide what is output: |
||
| 22 | * 'recent' for recent posts, |
||
| 23 | * 'news' for news topics, |
||
| 24 | * 'members' for recently registered members, |
||
| 25 | * 'profile' for a member's profile. |
||
| 26 | * To display a member's profile, a user id has to be given. (;u=1) |
||
| 27 | * Outputs an rss feed instead of a proprietary one if the 'type' $_GET |
||
| 28 | * parameter is 'rss' or 'rss2'. |
||
| 29 | * Accessed via ?action=.xml. |
||
| 30 | * Does not use any templates, sub templates, or template layers. |
||
| 31 | * |
||
| 32 | * @uses Stats language file. |
||
| 33 | */ |
||
| 34 | function ShowXmlFeed() |
||
| 35 | { |
||
| 36 | global $board, $board_info, $context, $scripturl, $boardurl, $txt, $modSettings, $user_info; |
||
| 37 | global $query_this_board, $smcFunc, $settings, $cache_enable; |
||
| 38 | |||
| 39 | // If it's not enabled, die. |
||
| 40 | if (empty($modSettings['xmlnews_enable'])) |
||
| 41 | obExit(false); |
||
| 42 | |||
| 43 | loadLanguage('Stats'); |
||
| 44 | |||
| 45 | // Default to latest 5. No more than 255, please. |
||
| 46 | $_GET['limit'] = empty($_GET['limit']) || (int) $_GET['limit'] < 1 ? 5 : min((int) $_GET['limit'], 255); |
||
| 47 | |||
| 48 | // Some general metadata for this feed. We'll change some of these values below. |
||
| 49 | $feed_meta = array( |
||
| 50 | 'title' => '', |
||
| 51 | 'desc' => $txt['xml_rss_desc'], |
||
| 52 | 'author' => $context['forum_name'], |
||
| 53 | 'source' => $scripturl, |
||
| 54 | 'rights' => '© ' . date('Y') . ' ' . $context['forum_name'], |
||
| 55 | 'icon' => !empty($settings['og_image']) ? $settings['og_image'] : $boardurl . '/favicon.ico', |
||
| 56 | 'language' => !empty($txt['lang_locale']) ? str_replace("_", "-", substr($txt['lang_locale'], 0, strcspn($txt['lang_locale'], "."))) : 'en', |
||
| 57 | ); |
||
| 58 | |||
| 59 | // Handle the cases where a board, boards, or category is asked for. |
||
| 60 | $query_this_board = 1; |
||
| 61 | $context['optimize_msg'] = array( |
||
| 62 | 'highest' => 'm.id_msg <= b.id_last_msg', |
||
| 63 | ); |
||
| 64 | if (!empty($_REQUEST['c']) && empty($board)) |
||
| 65 | { |
||
| 66 | $_REQUEST['c'] = explode(',', $_REQUEST['c']); |
||
| 67 | foreach ($_REQUEST['c'] as $i => $c) |
||
| 68 | $_REQUEST['c'][$i] = (int) $c; |
||
| 69 | |||
| 70 | if (count($_REQUEST['c']) == 1) |
||
| 71 | { |
||
| 72 | $request = $smcFunc['db_query']('', ' |
||
| 73 | SELECT name |
||
| 74 | FROM {db_prefix}categories |
||
| 75 | WHERE id_cat = {int:current_category}', |
||
| 76 | array( |
||
| 77 | 'current_category' => (int) $_REQUEST['c'][0], |
||
| 78 | ) |
||
| 79 | ); |
||
| 80 | list ($feed_meta['title']) = $smcFunc['db_fetch_row']($request); |
||
| 81 | $smcFunc['db_free_result']($request); |
||
| 82 | |||
| 83 | $feed_meta['title'] = ' - ' . strip_tags($feed_meta['title']); |
||
| 84 | } |
||
| 85 | |||
| 86 | $request = $smcFunc['db_query']('', ' |
||
| 87 | SELECT b.id_board, b.num_posts |
||
| 88 | FROM {db_prefix}boards AS b |
||
| 89 | WHERE b.id_cat IN ({array_int:current_category_list}) |
||
| 90 | AND {query_see_board}', |
||
| 91 | array( |
||
| 92 | 'current_category_list' => $_REQUEST['c'], |
||
| 93 | ) |
||
| 94 | ); |
||
| 95 | $total_cat_posts = 0; |
||
| 96 | $boards = array(); |
||
| 97 | while ($row = $smcFunc['db_fetch_assoc']($request)) |
||
| 98 | { |
||
| 99 | $boards[] = $row['id_board']; |
||
| 100 | $total_cat_posts += $row['num_posts']; |
||
| 101 | } |
||
| 102 | $smcFunc['db_free_result']($request); |
||
| 103 | |||
| 104 | if (!empty($boards)) |
||
| 105 | $query_this_board = 'b.id_board IN (' . implode(', ', $boards) . ')'; |
||
| 106 | |||
| 107 | // Try to limit the number of messages we look through. |
||
| 108 | if ($total_cat_posts > 100 && $total_cat_posts > $modSettings['totalMessages'] / 15) |
||
| 109 | $context['optimize_msg']['lowest'] = 'm.id_msg >= ' . max(0, $modSettings['maxMsgID'] - 400 - $_GET['limit'] * 5); |
||
| 110 | } |
||
| 111 | elseif (!empty($_REQUEST['boards'])) |
||
| 112 | { |
||
| 113 | $_REQUEST['boards'] = explode(',', $_REQUEST['boards']); |
||
| 114 | foreach ($_REQUEST['boards'] as $i => $b) |
||
| 115 | $_REQUEST['boards'][$i] = (int) $b; |
||
| 116 | |||
| 117 | $request = $smcFunc['db_query']('', ' |
||
| 118 | SELECT b.id_board, b.num_posts, b.name |
||
| 119 | FROM {db_prefix}boards AS b |
||
| 120 | WHERE b.id_board IN ({array_int:board_list}) |
||
| 121 | AND {query_see_board} |
||
| 122 | LIMIT {int:limit}', |
||
| 123 | array( |
||
| 124 | 'board_list' => $_REQUEST['boards'], |
||
| 125 | 'limit' => count($_REQUEST['boards']), |
||
| 126 | ) |
||
| 127 | ); |
||
| 128 | |||
| 129 | // Either the board specified doesn't exist or you have no access. |
||
| 130 | $num_boards = $smcFunc['db_num_rows']($request); |
||
| 131 | if ($num_boards == 0) |
||
| 132 | fatal_lang_error('no_board'); |
||
| 133 | |||
| 134 | $total_posts = 0; |
||
| 135 | $boards = array(); |
||
| 136 | while ($row = $smcFunc['db_fetch_assoc']($request)) |
||
| 137 | { |
||
| 138 | if ($num_boards == 1) |
||
| 139 | $feed_meta['title'] = ' - ' . strip_tags($row['name']); |
||
| 140 | |||
| 141 | $boards[] = $row['id_board']; |
||
| 142 | $total_posts += $row['num_posts']; |
||
| 143 | } |
||
| 144 | $smcFunc['db_free_result']($request); |
||
| 145 | |||
| 146 | if (!empty($boards)) |
||
| 147 | $query_this_board = 'b.id_board IN (' . implode(', ', $boards) . ')'; |
||
| 148 | |||
| 149 | // The more boards, the more we're going to look through... |
||
| 150 | if ($total_posts > 100 && $total_posts > $modSettings['totalMessages'] / 12) |
||
| 151 | $context['optimize_msg']['lowest'] = 'm.id_msg >= ' . max(0, $modSettings['maxMsgID'] - 500 - $_GET['limit'] * 5); |
||
| 152 | } |
||
| 153 | elseif (!empty($board)) |
||
| 154 | { |
||
| 155 | $request = $smcFunc['db_query']('', ' |
||
| 156 | SELECT num_posts |
||
| 157 | FROM {db_prefix}boards |
||
| 158 | WHERE id_board = {int:current_board} |
||
| 159 | LIMIT 1', |
||
| 160 | array( |
||
| 161 | 'current_board' => $board, |
||
| 162 | ) |
||
| 163 | ); |
||
| 164 | list ($total_posts) = $smcFunc['db_fetch_row']($request); |
||
| 165 | $smcFunc['db_free_result']($request); |
||
| 166 | |||
| 167 | $feed_meta['title'] = ' - ' . strip_tags($board_info['name']); |
||
| 168 | $feed_meta['source'] .= '?board=' . $board . '.0'; |
||
| 169 | |||
| 170 | $query_this_board = 'b.id_board = ' . $board; |
||
| 171 | |||
| 172 | // Try to look through just a few messages, if at all possible. |
||
| 173 | if ($total_posts > 80 && $total_posts > $modSettings['totalMessages'] / 10) |
||
| 174 | $context['optimize_msg']['lowest'] = 'm.id_msg >= ' . max(0, $modSettings['maxMsgID'] - 600 - $_GET['limit'] * 5); |
||
| 175 | } |
||
| 176 | else |
||
| 177 | { |
||
| 178 | $query_this_board = '{query_see_board}' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? ' |
||
| 179 | AND b.id_board != ' . $modSettings['recycle_board'] : ''); |
||
| 180 | $context['optimize_msg']['lowest'] = 'm.id_msg >= ' . max(0, $modSettings['maxMsgID'] - 100 - $_GET['limit'] * 5); |
||
| 181 | } |
||
| 182 | |||
| 183 | // Show in rss or proprietary format? |
||
| 184 | $xml_format = isset($_GET['type']) && in_array($_GET['type'], array('smf', 'rss', 'rss2', 'atom', 'rdf')) ? $_GET['type'] : 'rss2'; |
||
| 185 | |||
| 186 | // @todo Birthdays? |
||
| 187 | |||
| 188 | // List all the different types of data they can pull. |
||
| 189 | $subActions = array( |
||
| 190 | 'recent' => array('getXmlRecent', 'recent-post'), |
||
| 191 | 'news' => array('getXmlNews', 'article'), |
||
| 192 | 'members' => array('getXmlMembers', 'member'), |
||
| 193 | 'profile' => array('getXmlProfile', null), |
||
| 194 | ); |
||
| 195 | |||
| 196 | // Easy adding of sub actions |
||
| 197 | call_integration_hook('integrate_xmlfeeds', array(&$subActions)); |
||
| 198 | |||
| 199 | if (empty($_GET['sa']) || !isset($subActions[$_GET['sa']])) |
||
| 200 | $_GET['sa'] = 'recent'; |
||
| 201 | |||
| 202 | // We only want some information, not all of it. |
||
| 203 | $cachekey = array($xml_format, $_GET['action'], $_GET['limit'], $_GET['sa']); |
||
| 204 | foreach (array('board', 'boards', 'c') as $var) |
||
| 205 | if (isset($_REQUEST[$var])) |
||
| 206 | $cachekey[] = $_REQUEST[$var]; |
||
| 207 | $cachekey = md5($smcFunc['json_encode']($cachekey) . (!empty($query_this_board) ? $query_this_board : '')); |
||
| 208 | $cache_t = microtime(true); |
||
| 209 | |||
| 210 | // Get the associative array representing the xml. |
||
| 211 | if (!empty($cache_enable) && (!$user_info['is_guest'] || $cache_enable >= 3)) |
||
| 212 | $xml_data = cache_get_data('xmlfeed-' . $xml_format . ':' . ($user_info['is_guest'] ? '' : $user_info['id'] . '-') . $cachekey, 240); |
||
| 213 | if (empty($xml_data)) |
||
| 214 | { |
||
| 215 | $call = call_helper($subActions[$_GET['sa']][0], true); |
||
| 216 | |||
| 217 | if (!empty($call)) |
||
| 218 | $xml_data = call_user_func($call, $xml_format); |
||
| 219 | |||
| 220 | if (!empty($cache_enable) && (($user_info['is_guest'] && $cache_enable >= 3) |
||
| 221 | || (!$user_info['is_guest'] && (microtime(true) - $cache_t > 0.2)))) |
||
| 222 | cache_put_data('xmlfeed-' . $xml_format . ':' . ($user_info['is_guest'] ? '' : $user_info['id'] . '-') . $cachekey, $xml_data, 240); |
||
| 223 | } |
||
| 224 | |||
| 225 | $feed_meta['title'] = $smcFunc['htmlspecialchars'](strip_tags($context['forum_name'])) . (isset($feed_meta['title']) ? $feed_meta['title'] : ''); |
||
| 226 | |||
| 227 | // Allow mods to add extra namespaces and tags to the feed/channel |
||
| 228 | $namespaces = array( |
||
| 229 | 'rss' => array(), |
||
| 230 | 'rss2' => array('atom' => 'http://www.w3.org/2005/Atom'), |
||
| 231 | 'atom' => array('' => 'http://www.w3.org/2005/Atom'), |
||
| 232 | 'rdf' => array( |
||
| 233 | '' => 'http://purl.org/rss/1.0/', |
||
| 234 | 'rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', |
||
| 235 | 'dc' => 'http://purl.org/dc/elements/1.1/', |
||
| 236 | ), |
||
| 237 | 'smf' => array( |
||
| 238 | '' => 'http://www.simplemachines.org/xml/' . $_GET['sa'], |
||
| 239 | 'smf' => 'http://www.simplemachines.org/', |
||
| 240 | ), |
||
| 241 | ); |
||
| 242 | $extraFeedTags = array( |
||
| 243 | 'rss' => array(), |
||
| 244 | 'rss2' => array(), |
||
| 245 | 'atom' => array(), |
||
| 246 | 'rdf' => array(), |
||
| 247 | 'smf' => array(), |
||
| 248 | ); |
||
| 249 | |||
| 250 | // Allow mods to specify any keys that need special handling |
||
| 251 | $forceCdataKeys = array(); |
||
| 252 | $nsKeys = array(); |
||
| 253 | |||
| 254 | // Remember this, just in case... |
||
| 255 | $orig_feed_meta = $feed_meta; |
||
| 256 | |||
| 257 | // If mods want to do somthing with this feed, let them do that now. |
||
| 258 | // Provide the feed's data, metadata, namespaces, extra feed-level tags, keys that need special handling, the feed format, and the requested subaction |
||
| 259 | call_integration_hook('integrate_xml_data', array(&$xml_data, &$feed_meta, &$namespaces, &$extraFeedTags, &$forceCdataKeys, &$nsKeys, $xml_format, $_GET['sa'])); |
||
| 260 | |||
| 261 | // These can't be empty |
||
| 262 | foreach (array('title', 'desc', 'source') as $mkey) |
||
| 263 | $feed_meta[$mkey] = !empty($feed_meta[$mkey]) ? $feed_meta[$mkey] : $orig_feed_meta[$mkey]; |
||
| 264 | |||
| 265 | // Sanitize basic feed metadata values |
||
| 266 | foreach ($feed_meta as $mkey => $mvalue) |
||
| 267 | $feed_meta[$mkey] = cdata_parse(strip_tags(fix_possible_url($feed_meta[$mkey]))); |
||
| 268 | |||
| 269 | $ns_string = ''; |
||
| 270 | if (!empty($namespaces[$xml_format])) |
||
| 271 | { |
||
| 272 | foreach ($namespaces[$xml_format] as $nsprefix => $nsurl) |
||
| 273 | $ns_string .= ' xmlns' . ($nsprefix !== '' ? ':' : '') . $nsprefix . '="' . $nsurl . '"'; |
||
| 274 | } |
||
| 275 | |||
| 276 | $extraFeedTags_string = ''; |
||
| 277 | if (!empty($extraFeedTags[$xml_format])) |
||
| 278 | { |
||
| 279 | $indent = "\t" . ($xml_format !== 'atom' ? "\t" : ''); |
||
| 280 | foreach ($extraFeedTags[$xml_format] as $extraTag) |
||
| 281 | $extraFeedTags_string .= "\n" . $indent . $extraTag; |
||
| 282 | } |
||
| 283 | |||
| 284 | // This is an xml file.... |
||
| 285 | ob_end_clean(); |
||
| 286 | if (!empty($modSettings['enableCompressedOutput'])) |
||
| 287 | @ob_start('ob_gzhandler'); |
||
| 288 | else |
||
| 289 | ob_start(); |
||
| 290 | |||
| 291 | if ($xml_format == 'smf' || isset($_REQUEST['debug'])) |
||
| 292 | header('content-type: text/xml; charset=' . (empty($context['character_set']) ? 'ISO-8859-1' : $context['character_set'])); |
||
| 293 | elseif ($xml_format == 'rss' || $xml_format == 'rss2') |
||
| 294 | header('content-type: application/rss+xml; charset=' . (empty($context['character_set']) ? 'ISO-8859-1' : $context['character_set'])); |
||
| 295 | elseif ($xml_format == 'atom') |
||
| 296 | header('content-type: application/atom+xml; charset=' . (empty($context['character_set']) ? 'ISO-8859-1' : $context['character_set'])); |
||
| 297 | elseif ($xml_format == 'rdf') |
||
| 298 | header('content-type: ' . (isBrowser('ie') ? 'text/xml' : 'application/rdf+xml') . '; charset=' . (empty($context['character_set']) ? 'ISO-8859-1' : $context['character_set'])); |
||
| 299 | |||
| 300 | // First, output the xml header. |
||
| 301 | echo '<?xml version="1.0" encoding="', $context['character_set'], '"?' . '>'; |
||
| 302 | |||
| 303 | // Are we outputting an rss feed or one with more information? |
||
| 304 | if ($xml_format == 'rss' || $xml_format == 'rss2') |
||
| 305 | { |
||
| 306 | if ($xml_format == 'rss2') |
||
| 307 | foreach ($_REQUEST as $var => $val) |
||
| 308 | if (in_array($var, array('action', 'sa', 'type', 'board', 'boards', 'c', 'u', 'limit'))) |
||
| 309 | $url_parts[] = $var . '=' . (is_array($val) ? implode(',', $val) : $val); |
||
| 310 | |||
| 311 | // Start with an RSS 2.0 header. |
||
| 312 | echo ' |
||
| 313 | <rss version=', $xml_format == 'rss2' ? '"2.0"' : '"0.92"', ' xml:lang="', strtr($txt['lang_locale'], '_', '-'), '"', $ns_string, '> |
||
| 314 | <channel> |
||
| 315 | <title>', $feed_meta['title'], '</title> |
||
| 316 | <link>', $feed_meta['source'], '</link> |
||
| 317 | <description>', $feed_meta['desc'], '</description>', |
||
| 318 | !empty($feed_meta['icon']) ? ' |
||
| 319 | <image> |
||
| 320 | <url>' . $feed_meta['icon'] . '</url> |
||
| 321 | <title>' . $feed_meta['title'] . '</title> |
||
| 322 | <link>' . $feed_meta['source'] . '</link> |
||
| 323 | </image>' : '', |
||
| 324 | !empty($feed_meta['rights']) ? ' |
||
| 325 | <copyright>' . $feed_meta['rights'] . '</copyright>' : '', |
||
| 326 | !empty($feed_meta['language']) ? ' |
||
| 327 | <language>' . $feed_meta['language'] . '</language>' : ''; |
||
| 328 | |||
| 329 | // RSS2 calls for this. |
||
| 330 | if ($xml_format == 'rss2') |
||
| 331 | echo ' |
||
| 332 | <atom:link rel="self" type="application/rss+xml" href="', $scripturl, !empty($url_parts) ? '?' . implode(';', $url_parts) : '', '" />'; |
||
| 333 | |||
| 334 | echo $extraFeedTags_string; |
||
| 335 | |||
| 336 | // Output all of the associative array, start indenting with 2 tabs, and name everything "item". |
||
| 337 | dumpTags($xml_data, 2, null, $xml_format, $forceCdataKeys, $nsKeys); |
||
| 338 | |||
| 339 | // Output the footer of the xml. |
||
| 340 | echo ' |
||
| 341 | </channel> |
||
| 342 | </rss>'; |
||
| 343 | } |
||
| 344 | elseif ($xml_format == 'atom') |
||
| 345 | { |
||
| 346 | foreach ($_REQUEST as $var => $val) |
||
| 347 | if (in_array($var, array('action', 'sa', 'type', 'board', 'boards', 'c', 'u', 'limit'))) |
||
| 348 | $url_parts[] = $var . '=' . (is_array($val) ? implode(',', $val) : $val); |
||
| 349 | |||
| 350 | echo ' |
||
| 351 | <feed', $ns_string, !empty($feed_meta['language']) ? ' xml:lang="' . $feed_meta['language'] . '"' : '', '> |
||
| 352 | <title>', $feed_meta['title'], '</title> |
||
| 353 | <link rel="alternate" type="text/html" href="', $feed_meta['source'], '" /> |
||
| 354 | <link rel="self" type="application/atom+xml" href="', $scripturl, !empty($url_parts) ? '?' . implode(';', $url_parts) : '', '" /> |
||
| 355 | <updated>', gmstrftime('%Y-%m-%dT%H:%M:%SZ'), '</updated> |
||
| 356 | <id>', $feed_meta['source'], '</id> |
||
| 357 | <subtitle>', $feed_meta['desc'], '</subtitle> |
||
| 358 | <generator uri="https://www.simplemachines.org" version="', SMF_VERSION, '">SMF</generator>', |
||
| 359 | !empty($feed_meta['icon']) ? ' |
||
| 360 | <icon>' . $feed_meta['icon'] . '</icon>' : '', |
||
| 361 | !empty($feed_meta['author']) ? ' |
||
| 362 | <author> |
||
| 363 | <name>' . $feed_meta['author'] . '</name> |
||
| 364 | </author>' : '', |
||
| 365 | !empty($feed_meta['rights']) ? ' |
||
| 366 | <rights>' . $feed_meta['rights'] . '</rights>' : ''; |
||
| 367 | |||
| 368 | echo $extraFeedTags_string; |
||
| 369 | |||
| 370 | dumpTags($xml_data, 1, null, $xml_format, $forceCdataKeys, $nsKeys); |
||
| 371 | |||
| 372 | echo ' |
||
| 373 | </feed>'; |
||
| 374 | } |
||
| 375 | elseif ($xml_format == 'rdf') |
||
| 376 | { |
||
| 377 | echo ' |
||
| 378 | <rdf:RDF', $ns_string, '> |
||
| 379 | <channel rdf:about="', $scripturl, '"> |
||
| 380 | <title>', $feed_meta['title'], '</title> |
||
| 381 | <link>', $feed_meta['source'], '</link> |
||
| 382 | <description>', $feed_meta['desc'], '</description>'; |
||
| 383 | |||
| 384 | echo $extraFeedTags_string; |
||
| 385 | |||
| 386 | echo ' |
||
| 387 | <items> |
||
| 388 | <rdf:Seq>'; |
||
| 389 | |||
| 390 | foreach ($xml_data as $item) |
||
| 391 | { |
||
| 392 | $link = array_filter($item['content'], function($e) |
||
| 393 | { |
||
| 394 | return ($e['tag'] == 'link'); |
||
| 395 | }); |
||
| 396 | $link = array_pop($link); |
||
| 397 | |||
| 398 | echo ' |
||
| 399 | <rdf:li rdf:resource="', $link['content'], '" />'; |
||
| 400 | } |
||
| 401 | |||
| 402 | echo ' |
||
| 403 | </rdf:Seq> |
||
| 404 | </items> |
||
| 405 | </channel> |
||
| 406 | '; |
||
| 407 | |||
| 408 | dumpTags($xml_data, 1, null, $xml_format, $forceCdataKeys, $nsKeys); |
||
| 409 | |||
| 410 | echo ' |
||
| 411 | </rdf:RDF>'; |
||
| 412 | } |
||
| 413 | // Otherwise, we're using our proprietary formats - they give more data, though. |
||
| 414 | else |
||
| 415 | { |
||
| 416 | echo ' |
||
| 417 | <smf:xml-feed xml:lang="', strtr($txt['lang_locale'], '_', '-'), '"', $ns_string, '>'; |
||
| 418 | |||
| 419 | // Hard to imagine anyone wanting to add these for the proprietary format, but just in case... |
||
| 420 | echo $extraFeedTags_string; |
||
| 421 | |||
| 422 | // Dump out that associative array. Indent properly.... and use the right names for the base elements. |
||
| 423 | dumpTags($xml_data, 1, $subActions[$_GET['sa']][1], $xml_format, $forceCdataKeys, $nsKeys); |
||
| 424 | |||
| 425 | echo ' |
||
| 426 | </smf:xml-feed>'; |
||
| 427 | } |
||
| 428 | |||
| 429 | obExit(false); |
||
| 430 | } |
||
| 431 | |||
| 432 | /** |
||
| 433 | * Called from dumpTags to convert data to xml |
||
| 434 | * Finds urls for local site and sanitizes them |
||
| 435 | * |
||
| 436 | * @param string $val A string containing a possible URL |
||
| 437 | * @return string $val The string with any possible URLs sanitized |
||
| 438 | */ |
||
| 439 | function fix_possible_url($val) |
||
| 440 | { |
||
| 441 | global $modSettings, $context, $scripturl; |
||
| 442 | |||
| 443 | if (substr($val, 0, strlen($scripturl)) != $scripturl) |
||
| 444 | return $val; |
||
| 445 | |||
| 446 | call_integration_hook('integrate_fix_url', array(&$val)); |
||
| 447 | |||
| 448 | if (empty($modSettings['queryless_urls']) || ($context['server']['is_cgi'] && ini_get('cgi.fix_pathinfo') == 0 && @get_cfg_var('cgi.fix_pathinfo') == 0) || (!$context['server']['is_apache'] && !$context['server']['is_lighttpd'])) |
||
| 449 | return $val; |
||
| 450 | |||
| 451 | $val = preg_replace_callback('~\b' . preg_quote($scripturl, '~') . '\?((?:board|topic)=[^#"]+)(#[^"]*)?$~', function($m) use ($scripturl) |
||
| 452 | { |
||
| 453 | return $scripturl . '/' . strtr("$m[1]", '&;=', '//,') . '.html' . (isset($m[2]) ? $m[2] : ""); |
||
| 454 | }, $val); |
||
| 455 | return $val; |
||
| 456 | } |
||
| 457 | |||
| 458 | /** |
||
| 459 | * Ensures supplied data is properly encapsulated in cdata xml tags |
||
| 460 | * Called from getXmlProfile in News.php |
||
| 461 | * |
||
| 462 | * @param string $data XML data |
||
| 463 | * @param string $ns A namespace prefix for the XML data elements (used by mods, maybe) |
||
| 464 | * @param boolean $force If true, enclose the XML data in cdata tags no matter what (used by mods, maybe) |
||
| 465 | * @return string The XML data enclosed in cdata tags when necessary |
||
| 466 | */ |
||
| 467 | function cdata_parse($data, $ns = '', $force = false) |
||
| 468 | { |
||
| 469 | global $smcFunc; |
||
| 470 | |||
| 471 | // Do we even need to do this? |
||
| 472 | if (strpbrk($data, '<>&') == false && $force !== true) |
||
|
0 ignored issues
–
show
Bug
Best Practice
introduced
by
Loading history...
|
|||
| 473 | return $data; |
||
| 474 | |||
| 475 | $cdata = '<; $pos < $n; null) |
||
| 478 | { |
||
| 479 | $positions = array( |
||
| 480 | $smcFunc['strpos']($data, '&', $pos), |
||
| 481 | $smcFunc['strpos']($data, ']', $pos), |
||
| 482 | ); |
||
| 483 | if ($ns != '') |
||
| 484 | $positions[] = $smcFunc['strpos']($data, '<', $pos); |
||
| 485 | foreach ($positions as $k => $dummy) |
||
| 486 | { |
||
| 487 | if ($dummy === false) |
||
| 488 | unset($positions[$k]); |
||
| 489 | } |
||
| 490 | |||
| 491 | $old = $pos; |
||
| 492 | $pos = empty($positions) ? $n : min($positions); |
||
| 493 | |||
| 494 | if ($pos - $old > 0) |
||
| 495 | $cdata .= $smcFunc['substr']($data, $old, $pos - $old); |
||
| 496 | if ($pos >= $n) |
||
| 497 | break; |
||
| 498 | |||
| 499 | if ($smcFunc['substr']($data, $pos, 1) == '<') |
||
| 500 | { |
||
| 501 | $pos2 = $smcFunc['strpos']($data, '>', $pos); |
||
| 502 | if ($pos2 === false) |
||
| 503 | $pos2 = $n; |
||
| 504 | if ($smcFunc['substr']($data, $pos + 1, 1) == '/') |
||
| 505 | $cdata .= ']]></' . $ns . ':' . $smcFunc['substr']($data, $pos + 2, $pos2 - $pos - 1) . '<![CDATA['; |
||
| 506 | else |
||
| 507 | $cdata .= ']]><' . $ns . ':' . $smcFunc['substr']($data, $pos + 1, $pos2 - $pos) . '< == ']') |
||
| 511 | { |
||
| 512 | $cdata .= ']]>]< == '&') |
||
| 516 | { |
||
| 517 | $pos2 = $smcFunc['strpos']($data, ';', $pos); |
||
| 518 | if ($pos2 === false) |
||
| 519 | $pos2 = $n; |
||
| 520 | $ent = $smcFunc['substr']($data, $pos + 1, $pos2 - $pos - 1); |
||
| 521 | |||
| 522 | if ($smcFunc['substr']($data, $pos + 1, 1) == '#') |
||
| 523 | $cdata .= ']]>' . $smcFunc['substr']($data, $pos, $pos2 - $pos + 1) . '<![CDATA['; |
||
| 524 | elseif (in_array($ent, array('amp', 'lt', 'gt', 'quot'))) |
||
| 525 | $cdata .= ']]>' . $smcFunc['substr']($data, $pos, $pos2 - $pos + 1) . '<![CDATA['; |
||
| 526 | |||
| 527 | $pos = $pos2 + 1; |
||
| 528 | } |
||
| 529 | } |
||
| 530 | |||
| 531 | $cdata .= ']]>'; |
||
| 532 | |||
| 533 | return strtr($cdata, array('<![CDATA[]]>' => '')); |
||
| 534 | } |
||
| 535 | |||
| 536 | /** |
||
| 537 | * Formats data retrieved in other functions into xml format. |
||
| 538 | * Additionally formats data based on the specific format passed. |
||
| 539 | * This function is recursively called to handle sub arrays of data. |
||
| 540 | * |
||
| 541 | * @param array $data The array to output as xml data |
||
| 542 | * @param int $i The amount of indentation to use. |
||
| 543 | * @param null|string $tag |
||
| 544 | * @param string $xml_format The format to use ('atom', 'rss', 'rss2' or empty for plain XML) |
||
| 545 | * @param array $forceCdataKeys A list of keys on which to force cdata wrapping (used by mods, maybe) |
||
| 546 | * @param array $nsKeys Key-value pairs of namespace prefixes to pass to cdata_parse() (used by mods, maybe) |
||
| 547 | */ |
||
| 548 | function dumpTags($data, $i, $tag = null, $xml_format = '', $forceCdataKeys = array(), $nsKeys = array()) |
||
| 549 | { |
||
| 550 | // Wrap the values of these keys into CDATA tags |
||
| 551 | $keysToCdata = array( |
||
| 552 | 'title', |
||
| 553 | 'name', |
||
| 554 | 'description', |
||
| 555 | 'summary', |
||
| 556 | 'subject', |
||
| 557 | 'body', |
||
| 558 | 'username', |
||
| 559 | 'signature', |
||
| 560 | 'position', |
||
| 561 | 'language', |
||
| 562 | 'gender', |
||
| 563 | 'blurb', |
||
| 564 | ); |
||
| 565 | if ($xml_format != 'atom') |
||
| 566 | $keysToCdata[] = 'category'; |
||
| 567 | |||
| 568 | if (!empty($forceCdataKeys)) |
||
| 569 | { |
||
| 570 | $keysToCdata = array_merge($keysToCdata, $forceCdataKeys); |
||
| 571 | $keysToCdata = array_unique($keysToCdata); |
||
| 572 | } |
||
| 573 | |||
| 574 | // For every array in the data... |
||
| 575 | foreach ($data as $element) |
||
| 576 | { |
||
| 577 | // If a tag was passed, use it instead of the key. |
||
| 578 | $key = isset($tag) ? $tag : (isset($element['tag']) ? $element['tag'] : null); |
||
| 579 | $val = isset($element['content']) ? $element['content'] : null; |
||
| 580 | $attrs = isset($element['attributes']) ? $element['attributes'] : null; |
||
| 581 | |||
| 582 | // Skip it, it's been set to null. |
||
| 583 | if ($key === null || ($val === null && $attrs === null)) |
||
| 584 | continue; |
||
| 585 | |||
| 586 | $forceCdata = in_array($key, $forceCdataKeys); |
||
| 587 | $ns = !empty($nsKeys[$key]) ? $nsKeys[$key] : ''; |
||
| 588 | |||
| 589 | // First let's indent! |
||
| 590 | echo "\n", str_repeat("\t", $i); |
||
| 591 | |||
| 592 | // Beginning tag. |
||
| 593 | echo '<', $key; |
||
| 594 | |||
| 595 | if (!empty($attrs)) |
||
| 596 | { |
||
| 597 | foreach ($attrs as $attr_key => $attr_value) |
||
| 598 | echo ' ', $attr_key, '="', fix_possible_url($attr_value), '"'; |
||
| 599 | } |
||
| 600 | |||
| 601 | // If it's empty, simply output an empty element. |
||
| 602 | if (empty($val)) |
||
| 603 | { |
||
| 604 | echo ' />'; |
||
| 605 | } |
||
| 606 | else |
||
| 607 | { |
||
| 608 | echo '>'; |
||
| 609 | |||
| 610 | // The element's value. |
||
| 611 | if (is_array($val)) |
||
| 612 | { |
||
| 613 | // An array. Dump it, and then indent the tag. |
||
| 614 | dumpTags($val, $i + 1, null, $xml_format, $forceCdataKeys, $nsKeys); |
||
| 615 | echo "\n", str_repeat("\t", $i); |
||
| 616 | } |
||
| 617 | // A string with returns in it.... show this as a multiline element. |
||
| 618 | elseif (strpos($val, "\n") !== false) |
||
| 619 | echo "\n", in_array($key, $keysToCdata) ? cdata_parse(fix_possible_url($val), $ns, $forceCdata) : fix_possible_url($val), "\n", str_repeat("\t", $i); |
||
| 620 | // A simple string. |
||
| 621 | else |
||
| 622 | echo in_array($key, $keysToCdata) ? cdata_parse(fix_possible_url($val), $ns, $forceCdata) : fix_possible_url($val); |
||
| 623 | |||
| 624 | // Ending tag. |
||
| 625 | echo '</', $key, '>'; |
||
| 626 | } |
||
| 627 | } |
||
| 628 | } |
||
| 629 | |||
| 630 | /** |
||
| 631 | * Retrieve the list of members from database. |
||
| 632 | * The array will be generated to match the format. |
||
| 633 | * |
||
| 634 | * @todo get the list of members from Subs-Members. |
||
| 635 | * |
||
| 636 | * @param string $xml_format The format to use. Can be 'atom', 'rdf', 'rss', 'rss2' or 'smf' |
||
| 637 | * @return array An array of arrays of feed items. Each array has keys corresponding to the appropriate tags for the specified format. |
||
| 638 | */ |
||
| 639 | function getXmlMembers($xml_format) |
||
| 640 | { |
||
| 641 | global $scripturl, $smcFunc; |
||
| 642 | |||
| 643 | if (!allowedTo('view_mlist')) |
||
| 644 | return array(); |
||
| 645 | |||
| 646 | // Find the most recent members. |
||
| 647 | $request = $smcFunc['db_query']('', ' |
||
| 648 | SELECT id_member, member_name, real_name, date_registered, last_login |
||
| 649 | FROM {db_prefix}members |
||
| 650 | ORDER BY id_member DESC |
||
| 651 | LIMIT {int:limit}', |
||
| 652 | array( |
||
| 653 | 'limit' => $_GET['limit'], |
||
| 654 | ) |
||
| 655 | ); |
||
| 656 | $data = array(); |
||
| 657 | while ($row = $smcFunc['db_fetch_assoc']($request)) |
||
| 658 | { |
||
| 659 | // Create a GUID for each member using the tag URI scheme |
||
| 660 | $guid = 'tag:' . parse_url($scripturl, PHP_URL_HOST) . ',' . gmdate('Y-m-d', $row['date_registered']) . ':member=' . $row['id_member']; |
||
| 661 | |||
| 662 | // Make the data look rss-ish. |
||
| 663 | if ($xml_format == 'rss' || $xml_format == 'rss2') |
||
| 664 | $data[] = array( |
||
| 665 | 'tag' => 'item', |
||
| 666 | 'content' => array( |
||
| 667 | array( |
||
| 668 | 'tag' => 'title', |
||
| 669 | 'content' => $row['real_name'], |
||
| 670 | ), |
||
| 671 | array( |
||
| 672 | 'tag' => 'link', |
||
| 673 | 'content' => $scripturl . '?action=profile;u=' . $row['id_member'], |
||
| 674 | ), |
||
| 675 | array( |
||
| 676 | 'tag' => 'comments', |
||
| 677 | 'content' => $scripturl . '?action=pm;sa=send;u=' . $row['id_member'], |
||
| 678 | ), |
||
| 679 | array( |
||
| 680 | 'tag' => 'pubDate', |
||
| 681 | 'content' => gmdate('D, d M Y H:i:s \G\M\T', $row['date_registered']), |
||
| 682 | ), |
||
| 683 | array( |
||
| 684 | 'tag' => 'guid', |
||
| 685 | 'content' => $guid, |
||
| 686 | 'attributes' => array( |
||
| 687 | 'isPermaLink' => 'false', |
||
| 688 | ), |
||
| 689 | ), |
||
| 690 | ), |
||
| 691 | ); |
||
| 692 | elseif ($xml_format == 'rdf') |
||
| 693 | $data[] = array( |
||
| 694 | 'tag' => 'item', |
||
| 695 | 'attributes' => array('rdf:about' => $scripturl . '?action=profile;u=' . $row['id_member']), |
||
| 696 | 'content' => array( |
||
| 697 | array( |
||
| 698 | 'tag' => 'dc:format', |
||
| 699 | 'content' => 'text/html', |
||
| 700 | ), |
||
| 701 | array( |
||
| 702 | 'tag' => 'title', |
||
| 703 | 'content' => $row['real_name'], |
||
| 704 | ), |
||
| 705 | array( |
||
| 706 | 'tag' => 'link', |
||
| 707 | 'content' => $scripturl . '?action=profile;u=' . $row['id_member'], |
||
| 708 | ), |
||
| 709 | ), |
||
| 710 | ); |
||
| 711 | elseif ($xml_format == 'atom') |
||
| 712 | $data[] = array( |
||
| 713 | 'tag' => 'entry', |
||
| 714 | 'content' => array( |
||
| 715 | array( |
||
| 716 | 'tag' => 'title', |
||
| 717 | 'content' => $row['real_name'], |
||
| 718 | ), |
||
| 719 | array( |
||
| 720 | 'tag' => 'link', |
||
| 721 | 'attributes' => array( |
||
| 722 | 'rel' => 'alternate', |
||
| 723 | 'type' => 'text/html', |
||
| 724 | 'href' => $scripturl . '?action=profile;u=' . $row['id_member'], |
||
| 725 | ), |
||
| 726 | ), |
||
| 727 | array( |
||
| 728 | 'tag' => 'published', |
||
| 729 | 'content' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $row['date_registered']), |
||
| 730 | ), |
||
| 731 | array( |
||
| 732 | 'tag' => 'updated', |
||
| 733 | 'content' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $row['last_login']), |
||
| 734 | ), |
||
| 735 | array( |
||
| 736 | 'tag' => 'id', |
||
| 737 | 'content' => $guid, |
||
| 738 | ), |
||
| 739 | ), |
||
| 740 | ); |
||
| 741 | // More logical format for the data, but harder to apply. |
||
| 742 | else |
||
| 743 | $data[] = array( |
||
| 744 | 'tag' => 'member', |
||
| 745 | 'content' => array( |
||
| 746 | array( |
||
| 747 | 'tag' => 'name', |
||
| 748 | 'content' => $row['real_name'], |
||
| 749 | ), |
||
| 750 | array( |
||
| 751 | 'tag' => 'time', |
||
| 752 | 'content' => $smcFunc['htmlspecialchars'](strip_tags(timeformat($row['date_registered']))), |
||
| 753 | ), |
||
| 754 | array( |
||
| 755 | 'tag' => 'id', |
||
| 756 | 'content' => $row['id_member'], |
||
| 757 | ), |
||
| 758 | array( |
||
| 759 | 'tag' => 'link', |
||
| 760 | 'content' => $scripturl . '?action=profile;u=' . $row['id_member'], |
||
| 761 | ), |
||
| 762 | ), |
||
| 763 | ); |
||
| 764 | } |
||
| 765 | $smcFunc['db_free_result']($request); |
||
| 766 | |||
| 767 | return $data; |
||
| 768 | } |
||
| 769 | |||
| 770 | /** |
||
| 771 | * Get the latest topics information from a specific board, |
||
| 772 | * to display later. |
||
| 773 | * The returned array will be generated to match the xml_format. |
||
| 774 | * |
||
| 775 | * @todo does not belong here |
||
| 776 | * |
||
| 777 | * @param string $xml_format The XML format. Can be 'atom', 'rdf', 'rss', 'rss2' or 'smf'. |
||
| 778 | * @return array An array of arrays of topic data for the feed. Each array has keys corresponding to the tags for the specified format. |
||
| 779 | */ |
||
| 780 | function getXmlNews($xml_format) |
||
| 781 | { |
||
| 782 | global $scripturl, $modSettings, $board, $user_info; |
||
| 783 | global $query_this_board, $smcFunc, $context, $txt; |
||
| 784 | |||
| 785 | /* Find the latest posts that: |
||
| 786 | - are the first post in their topic. |
||
| 787 | - are on an any board OR in a specified board. |
||
| 788 | - can be seen by this user. |
||
| 789 | - are actually the latest posts. */ |
||
| 790 | |||
| 791 | $done = false; |
||
| 792 | $loops = 0; |
||
| 793 | while (!$done) |
||
| 794 | { |
||
| 795 | $optimize_msg = implode(' AND ', $context['optimize_msg']); |
||
| 796 | $request = $smcFunc['db_query']('', ' |
||
| 797 | SELECT |
||
| 798 | m.smileys_enabled, m.poster_time, m.id_msg, m.subject, m.body, m.modified_time, |
||
| 799 | m.icon, t.id_topic, t.id_board, t.num_replies, |
||
| 800 | b.name AS bname, |
||
| 801 | COALESCE(mem.id_member, 0) AS id_member, |
||
| 802 | COALESCE(mem.email_address, m.poster_email) AS poster_email, |
||
| 803 | COALESCE(mem.real_name, m.poster_name) AS poster_name |
||
| 804 | FROM {db_prefix}topics AS t |
||
| 805 | INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg) |
||
| 806 | INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board) |
||
| 807 | LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member) |
||
| 808 | WHERE ' . $query_this_board . (empty($optimize_msg) ? '' : ' |
||
| 809 | AND {raw:optimize_msg}') . (empty($board) ? '' : ' |
||
| 810 | AND t.id_board = {int:current_board}') . ($modSettings['postmod_active'] ? ' |
||
| 811 | AND t.approved = {int:is_approved}' : '') . ' |
||
| 812 | ORDER BY t.id_first_msg DESC |
||
| 813 | LIMIT {int:limit}', |
||
| 814 | array( |
||
| 815 | 'current_board' => $board, |
||
| 816 | 'is_approved' => 1, |
||
| 817 | 'limit' => $_GET['limit'], |
||
| 818 | 'optimize_msg' => $optimize_msg, |
||
| 819 | ) |
||
| 820 | ); |
||
| 821 | // If we don't have $_GET['limit'] results, try again with an unoptimized version covering all rows. |
||
| 822 | if ($loops < 2 && $smcFunc['db_num_rows']($request) < $_GET['limit']) |
||
| 823 | { |
||
| 824 | $smcFunc['db_free_result']($request); |
||
| 825 | if (empty($_REQUEST['boards']) && empty($board)) |
||
| 826 | unset($context['optimize_msg']['lowest']); |
||
| 827 | else |
||
| 828 | $context['optimize_msg']['lowest'] = 'm.id_msg >= t.id_first_msg'; |
||
| 829 | $context['optimize_msg']['highest'] = 'm.id_msg <= t.id_last_msg'; |
||
| 830 | $loops++; |
||
| 831 | } |
||
| 832 | else |
||
| 833 | $done = true; |
||
| 834 | } |
||
| 835 | $data = array(); |
||
| 836 | while ($row = $smcFunc['db_fetch_assoc']($request)) |
||
| 837 | { |
||
| 838 | // Limit the length of the message, if the option is set. |
||
| 839 | if (!empty($modSettings['xmlnews_maxlen']) && $smcFunc['strlen'](str_replace('<br>', "\n", $row['body'])) > $modSettings['xmlnews_maxlen']) |
||
| 840 | $row['body'] = strtr($smcFunc['substr'](str_replace('<br>', "\n", $row['body']), 0, $modSettings['xmlnews_maxlen'] - 3), array("\n" => '<br>')) . '...'; |
||
| 841 | |||
| 842 | $row['body'] = parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']); |
||
| 843 | |||
| 844 | censorText($row['body']); |
||
| 845 | censorText($row['subject']); |
||
| 846 | |||
| 847 | // Do we want to include any attachments? |
||
| 848 | if (!empty($modSettings['attachmentEnable']) && !empty($modSettings['xmlnews_attachments']) && allowedTo('view_attachments', $row['id_board'])) |
||
| 849 | { |
||
| 850 | $attach_request = $smcFunc['db_query']('', ' |
||
| 851 | SELECT |
||
| 852 | a.id_attach, a.filename, COALESCE(a.size, 0) AS filesize, a.mime_type, a.downloads, a.approved, m.id_topic AS topic |
||
| 853 | FROM {db_prefix}attachments AS a |
||
| 854 | LEFT JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg) |
||
| 855 | WHERE a.attachment_type = {int:attachment_type} |
||
| 856 | AND a.id_msg = {int:message_id}', |
||
| 857 | array( |
||
| 858 | 'message_id' => $row['id_msg'], |
||
| 859 | 'attachment_type' => 0, |
||
| 860 | 'is_approved' => 1, |
||
| 861 | ) |
||
| 862 | ); |
||
| 863 | $loaded_attachments = array(); |
||
| 864 | while ($attach = $smcFunc['db_fetch_assoc']($attach_request)) |
||
| 865 | { |
||
| 866 | // Include approved attachments only |
||
| 867 | if ($attach['approved']) |
||
| 868 | $loaded_attachments['attachment_' . $attach['id_attach']] = $attach; |
||
| 869 | } |
||
| 870 | $smcFunc['db_free_result']($attach_request); |
||
| 871 | |||
| 872 | // Sort the attachments by size to make things easier below |
||
| 873 | if (!empty($loaded_attachments)) |
||
| 874 | { |
||
| 875 | uasort($loaded_attachments, function($a, $b) |
||
| 876 | { |
||
| 877 | if ($a['filesize'] == $b['filesize']) |
||
| 878 | return 0; |
||
| 879 | return ($a['filesize'] < $b['filesize']) ? -1 : 1; |
||
| 880 | }); |
||
| 881 | } |
||
| 882 | else |
||
| 883 | $loaded_attachments = null; |
||
| 884 | } |
||
| 885 | else |
||
| 886 | $loaded_attachments = null; |
||
| 887 | |||
| 888 | // Create a GUID for this topic using the tag URI scheme |
||
| 889 | $guid = 'tag:' . parse_url($scripturl, PHP_URL_HOST) . ',' . gmdate('Y-m-d', $row['poster_time']) . ':topic=' . $row['id_topic']; |
||
| 890 | |||
| 891 | // Being news, this actually makes sense in rss format. |
||
| 892 | if ($xml_format == 'rss' || $xml_format == 'rss2') |
||
| 893 | { |
||
| 894 | // Only one attachment allowed in RSS. |
||
| 895 | if ($loaded_attachments !== null) |
||
| 896 | { |
||
| 897 | $attachment = array_pop($loaded_attachments); |
||
| 898 | $enclosure = array( |
||
| 899 | 'url' => fix_possible_url($scripturl . '?action=dlattach;topic=' . $attachment['topic'] . '.0;attach=' . $attachment['id_attach']), |
||
| 900 | 'length' => $attachment['filesize'], |
||
| 901 | 'type' => $attachment['mime_type'], |
||
| 902 | ); |
||
| 903 | } |
||
| 904 | else |
||
| 905 | $enclosure = null; |
||
| 906 | |||
| 907 | $data[] = array( |
||
| 908 | 'tag' => 'item', |
||
| 909 | 'content' => array( |
||
| 910 | array( |
||
| 911 | 'tag' => 'title', |
||
| 912 | 'content' => $row['subject'], |
||
| 913 | ), |
||
| 914 | array( |
||
| 915 | 'tag' => 'link', |
||
| 916 | 'content' => $scripturl . '?topic=' . $row['id_topic'] . '.0', |
||
| 917 | ), |
||
| 918 | array( |
||
| 919 | 'tag' => 'description', |
||
| 920 | 'content' => $row['body'], |
||
| 921 | ), |
||
| 922 | array( |
||
| 923 | 'tag' => 'author', |
||
| 924 | 'content' => (allowedTo('moderate_forum') || $row['id_member'] == $user_info['id']) ? $row['poster_email'] . ' (' . $row['poster_name'] . ')' : null, |
||
| 925 | ), |
||
| 926 | array( |
||
| 927 | 'tag' => 'comments', |
||
| 928 | 'content' => $scripturl . '?action=post;topic=' . $row['id_topic'] . '.0', |
||
| 929 | ), |
||
| 930 | array( |
||
| 931 | 'tag' => 'category', |
||
| 932 | 'content' => $row['bname'], |
||
| 933 | ), |
||
| 934 | array( |
||
| 935 | 'tag' => 'pubDate', |
||
| 936 | 'content' => gmdate('D, d M Y H:i:s \G\M\T', $row['poster_time']), |
||
| 937 | ), |
||
| 938 | array( |
||
| 939 | 'tag' => 'guid', |
||
| 940 | 'content' => $guid, |
||
| 941 | 'attributes' => array( |
||
| 942 | 'isPermaLink' => 'false', |
||
| 943 | ), |
||
| 944 | ), |
||
| 945 | array( |
||
| 946 | 'tag' => 'enclosure', |
||
| 947 | 'attributes' => $enclosure, |
||
| 948 | ), |
||
| 949 | ), |
||
| 950 | ); |
||
| 951 | } |
||
| 952 | elseif ($xml_format == 'rdf') |
||
| 953 | { |
||
| 954 | $data[] = array( |
||
| 955 | 'tag' => 'item', |
||
| 956 | 'attributes' => array('rdf:about' => $scripturl . '?topic=' . $row['id_topic'] . '.0'), |
||
| 957 | 'content' => array( |
||
| 958 | array( |
||
| 959 | 'tag' => 'dc:format', |
||
| 960 | 'content' => 'text/html', |
||
| 961 | ), |
||
| 962 | array( |
||
| 963 | 'tag' => 'title', |
||
| 964 | 'content' => $row['subject'], |
||
| 965 | ), |
||
| 966 | array( |
||
| 967 | 'tag' => 'link', |
||
| 968 | 'content' => $scripturl . '?topic=' . $row['id_topic'] . '.0', |
||
| 969 | ), |
||
| 970 | array( |
||
| 971 | 'tag' => 'description', |
||
| 972 | 'content' => $row['body'], |
||
| 973 | ), |
||
| 974 | ), |
||
| 975 | ); |
||
| 976 | } |
||
| 977 | elseif ($xml_format == 'atom') |
||
| 978 | { |
||
| 979 | // Only one attachment allowed |
||
| 980 | if (!empty($loaded_attachments)) |
||
| 981 | { |
||
| 982 | $attachment = array_pop($loaded_attachments); |
||
| 983 | $enclosure = array( |
||
| 984 | 'rel' => 'enclosure', |
||
| 985 | 'href' => fix_possible_url($scripturl . '?action=dlattach;topic=' . $attachment['topic'] . '.0;attach=' . $attachment['id_attach']), |
||
| 986 | 'length' => $attachment['filesize'], |
||
| 987 | 'type' => $attachment['mime_type'], |
||
| 988 | ); |
||
| 989 | } |
||
| 990 | else |
||
| 991 | $enclosure = null; |
||
| 992 | |||
| 993 | $data[] = array( |
||
| 994 | 'tag' => 'entry', |
||
| 995 | 'content' => array( |
||
| 996 | array( |
||
| 997 | 'tag' => 'title', |
||
| 998 | 'content' => $row['subject'], |
||
| 999 | ), |
||
| 1000 | array( |
||
| 1001 | 'tag' => 'link', |
||
| 1002 | 'attributes' => array( |
||
| 1003 | 'rel' => 'alternate', |
||
| 1004 | 'type' => 'text/html', |
||
| 1005 | 'href' => $scripturl . '?topic=' . $row['id_topic'] . '.0', |
||
| 1006 | ), |
||
| 1007 | ), |
||
| 1008 | array( |
||
| 1009 | 'tag' => 'summary', |
||
| 1010 | 'attributes' => array('type' => 'html'), |
||
| 1011 | 'content' => $row['body'], |
||
| 1012 | ), |
||
| 1013 | array( |
||
| 1014 | 'tag' => 'category', |
||
| 1015 | 'attributes' => array('term' => $row['bname']), |
||
| 1016 | ), |
||
| 1017 | array( |
||
| 1018 | 'tag' => 'author', |
||
| 1019 | 'content' => array( |
||
| 1020 | array( |
||
| 1021 | 'tag' => 'name', |
||
| 1022 | 'content' => $row['poster_name'], |
||
| 1023 | ), |
||
| 1024 | array( |
||
| 1025 | 'tag' => 'email', |
||
| 1026 | 'content' => (allowedTo('moderate_forum') || $row['id_member'] == $user_info['id']) ? $row['poster_email'] : null, |
||
| 1027 | ), |
||
| 1028 | array( |
||
| 1029 | 'tag' => 'uri', |
||
| 1030 | 'content' => !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : null, |
||
| 1031 | ), |
||
| 1032 | ) |
||
| 1033 | ), |
||
| 1034 | array( |
||
| 1035 | 'tag' => 'published', |
||
| 1036 | 'content' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $row['poster_time']), |
||
| 1037 | ), |
||
| 1038 | array( |
||
| 1039 | 'tag' => 'updated', |
||
| 1040 | 'content' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', empty($row['modified_time']) ? $row['poster_time'] : $row['modified_time']), |
||
| 1041 | ), |
||
| 1042 | array( |
||
| 1043 | 'tag' => 'id', |
||
| 1044 | 'content' => $guid, |
||
| 1045 | ), |
||
| 1046 | array( |
||
| 1047 | 'tag' => 'link', |
||
| 1048 | 'attributes' => $enclosure, |
||
| 1049 | ), |
||
| 1050 | ), |
||
| 1051 | ); |
||
| 1052 | } |
||
| 1053 | // The biggest difference here is more information. |
||
| 1054 | else |
||
| 1055 | { |
||
| 1056 | $attachments = array(); |
||
| 1057 | if (!empty($loaded_attachments)) |
||
| 1058 | { |
||
| 1059 | foreach ($loaded_attachments as $attachment) |
||
| 1060 | { |
||
| 1061 | $attachments[] = array( |
||
| 1062 | 'tag' => 'attachment', |
||
| 1063 | 'content' => array( |
||
| 1064 | array( |
||
| 1065 | 'tag' => 'id', |
||
| 1066 | 'content' => $attachment['id_attach'], |
||
| 1067 | ), |
||
| 1068 | array( |
||
| 1069 | 'tag' => 'name', |
||
| 1070 | 'content' => preg_replace('~&#(\\d{1,7}|x[0-9a-fA-F]{1,6});~', '&#\\1;', $smcFunc['htmlspecialchars']($attachment['filename'])), |
||
| 1071 | ), |
||
| 1072 | array( |
||
| 1073 | 'tag' => 'downloads', |
||
| 1074 | 'content' => $attachment['downloads'], |
||
| 1075 | ), |
||
| 1076 | array( |
||
| 1077 | 'tag' => 'size', |
||
| 1078 | 'content' => ($attachment['filesize'] < 1024000) ? round($attachment['filesize'] / 1024, 2) . ' ' . $txt['kilobyte'] : round($attachment['filesize'] / 1024 / 1024, 2) . ' ' . $txt['megabyte'], |
||
| 1079 | ), |
||
| 1080 | array( |
||
| 1081 | 'tag' => 'byte_size', |
||
| 1082 | 'content' => $attachment['filesize'], |
||
| 1083 | ), |
||
| 1084 | array( |
||
| 1085 | 'tag' => 'link', |
||
| 1086 | 'content' => $scripturl . '?action=dlattach;topic=' . $attachment['topic'] . '.0;attach=' . $attachment['id_attach'], |
||
| 1087 | ), |
||
| 1088 | ) |
||
| 1089 | ); |
||
| 1090 | } |
||
| 1091 | } |
||
| 1092 | else |
||
| 1093 | $attachments = null; |
||
| 1094 | |||
| 1095 | $data[] = array( |
||
| 1096 | 'tag' => 'article', |
||
| 1097 | 'content' => array( |
||
| 1098 | array( |
||
| 1099 | 'tag' => 'time', |
||
| 1100 | 'content' => $smcFunc['htmlspecialchars'](strip_tags(timeformat($row['poster_time']))), |
||
| 1101 | ), |
||
| 1102 | array( |
||
| 1103 | 'tag' => 'id', |
||
| 1104 | 'content' => $row['id_topic'], |
||
| 1105 | ), |
||
| 1106 | array( |
||
| 1107 | 'tag' => 'subject', |
||
| 1108 | 'content' => $row['subject'], |
||
| 1109 | ), |
||
| 1110 | array( |
||
| 1111 | 'tag' => 'body', |
||
| 1112 | 'content' => $row['body'], |
||
| 1113 | ), |
||
| 1114 | array( |
||
| 1115 | 'tag' => 'poster', |
||
| 1116 | 'content' => array( |
||
| 1117 | array( |
||
| 1118 | 'tag' => 'name', |
||
| 1119 | 'content' => $row['poster_name'], |
||
| 1120 | ), |
||
| 1121 | array( |
||
| 1122 | 'tag' => 'id', |
||
| 1123 | 'content' => $row['id_member'], |
||
| 1124 | ), |
||
| 1125 | array( |
||
| 1126 | 'tag' => 'link', |
||
| 1127 | 'content' => !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : '', |
||
| 1128 | ), |
||
| 1129 | ) |
||
| 1130 | ), |
||
| 1131 | array( |
||
| 1132 | 'tag' => 'topic', |
||
| 1133 | 'content' => $row['id_topic'], |
||
| 1134 | ), |
||
| 1135 | array( |
||
| 1136 | 'tag' => 'board', |
||
| 1137 | 'content' => array( |
||
| 1138 | array( |
||
| 1139 | 'tag' => 'name', |
||
| 1140 | 'content' => $row['bname'], |
||
| 1141 | ), |
||
| 1142 | array( |
||
| 1143 | 'tag' => 'id', |
||
| 1144 | 'content' => $row['id_board'], |
||
| 1145 | ), |
||
| 1146 | array( |
||
| 1147 | 'tag' => 'link', |
||
| 1148 | 'content' => $scripturl . '?board=' . $row['id_board'] . '.0', |
||
| 1149 | ), |
||
| 1150 | ), |
||
| 1151 | ), |
||
| 1152 | array( |
||
| 1153 | 'tag' => 'link', |
||
| 1154 | 'content' => $scripturl . '?topic=' . $row['id_topic'] . '.0', |
||
| 1155 | ), |
||
| 1156 | array( |
||
| 1157 | 'tag' => 'attachments', |
||
| 1158 | 'content' => $attachments, |
||
| 1159 | ), |
||
| 1160 | ), |
||
| 1161 | ); |
||
| 1162 | } |
||
| 1163 | } |
||
| 1164 | $smcFunc['db_free_result']($request); |
||
| 1165 | |||
| 1166 | return $data; |
||
| 1167 | } |
||
| 1168 | |||
| 1169 | /** |
||
| 1170 | * Get the recent topics to display. |
||
| 1171 | * The returned array will be generated to match the xml_format. |
||
| 1172 | * |
||
| 1173 | * @todo does not belong here. |
||
| 1174 | * |
||
| 1175 | * @param string $xml_format The XML format. Can be 'atom', 'rdf', 'rss', 'rss2' or 'smf' |
||
| 1176 | * @return array An array of arrays containing data for the feed. Each array has keys corresponding to the appropriate tags for the specified format. |
||
| 1177 | */ |
||
| 1178 | function getXmlRecent($xml_format) |
||
| 1179 | { |
||
| 1180 | global $scripturl, $modSettings, $board, $txt; |
||
| 1181 | global $query_this_board, $smcFunc, $context, $user_info, $sourcedir; |
||
| 1182 | |||
| 1183 | require_once($sourcedir . '/Subs-Attachments.php'); |
||
| 1184 | |||
| 1185 | $done = false; |
||
| 1186 | $loops = 0; |
||
| 1187 | while (!$done) |
||
| 1188 | { |
||
| 1189 | $optimize_msg = implode(' AND ', $context['optimize_msg']); |
||
| 1190 | $request = $smcFunc['db_query']('', ' |
||
| 1191 | SELECT m.id_msg |
||
| 1192 | FROM {db_prefix}messages AS m |
||
| 1193 | INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board) |
||
| 1194 | INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic) |
||
| 1195 | WHERE ' . $query_this_board . (empty($optimize_msg) ? '' : ' |
||
| 1196 | AND {raw:optimize_msg}') . (empty($board) ? '' : ' |
||
| 1197 | AND m.id_board = {int:current_board}') . ($modSettings['postmod_active'] ? ' |
||
| 1198 | AND m.approved = {int:is_approved}' : '') . ' |
||
| 1199 | ORDER BY m.id_msg DESC |
||
| 1200 | LIMIT {int:limit}', |
||
| 1201 | array( |
||
| 1202 | 'limit' => $_GET['limit'], |
||
| 1203 | 'current_board' => $board, |
||
| 1204 | 'is_approved' => 1, |
||
| 1205 | 'optimize_msg' => $optimize_msg, |
||
| 1206 | ) |
||
| 1207 | ); |
||
| 1208 | // If we don't have $_GET['limit'] results, try again with an unoptimized version covering all rows. |
||
| 1209 | if ($loops < 2 && $smcFunc['db_num_rows']($request) < $_GET['limit']) |
||
| 1210 | { |
||
| 1211 | $smcFunc['db_free_result']($request); |
||
| 1212 | if (empty($_REQUEST['boards']) && empty($board)) |
||
| 1213 | unset($context['optimize_msg']['lowest']); |
||
| 1214 | else |
||
| 1215 | $context['optimize_msg']['lowest'] = $loops ? 'm.id_msg >= t.id_first_msg' : 'm.id_msg >= (t.id_last_msg - t.id_first_msg) / 2'; |
||
| 1216 | $loops++; |
||
| 1217 | } |
||
| 1218 | else |
||
| 1219 | $done = true; |
||
| 1220 | } |
||
| 1221 | $messages = array(); |
||
| 1222 | while ($row = $smcFunc['db_fetch_assoc']($request)) |
||
| 1223 | $messages[] = $row['id_msg']; |
||
| 1224 | $smcFunc['db_free_result']($request); |
||
| 1225 | |||
| 1226 | if (empty($messages)) |
||
| 1227 | return array(); |
||
| 1228 | |||
| 1229 | // Find the most recent posts this user can see. |
||
| 1230 | $request = $smcFunc['db_query']('', ' |
||
| 1231 | SELECT |
||
| 1232 | m.smileys_enabled, m.poster_time, m.id_msg, m.subject, m.body, m.id_topic, t.id_board, |
||
| 1233 | b.name AS bname, t.num_replies, m.id_member, m.icon, mf.id_member AS id_first_member, |
||
| 1234 | COALESCE(mem.real_name, m.poster_name) AS poster_name, mf.subject AS first_subject, |
||
| 1235 | COALESCE(memf.real_name, mf.poster_name) AS first_poster_name, |
||
| 1236 | COALESCE(mem.email_address, m.poster_email) AS poster_email, m.modified_time |
||
| 1237 | FROM {db_prefix}messages AS m |
||
| 1238 | INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic) |
||
| 1239 | INNER JOIN {db_prefix}messages AS mf ON (mf.id_msg = t.id_first_msg) |
||
| 1240 | INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board) |
||
| 1241 | LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member) |
||
| 1242 | LEFT JOIN {db_prefix}members AS memf ON (memf.id_member = mf.id_member) |
||
| 1243 | WHERE m.id_msg IN ({array_int:message_list}) |
||
| 1244 | ' . (empty($board) ? '' : 'AND t.id_board = {int:current_board}') . ' |
||
| 1245 | ORDER BY m.id_msg DESC |
||
| 1246 | LIMIT {int:limit}', |
||
| 1247 | array( |
||
| 1248 | 'limit' => $_GET['limit'], |
||
| 1249 | 'current_board' => $board, |
||
| 1250 | 'message_list' => $messages, |
||
| 1251 | ) |
||
| 1252 | ); |
||
| 1253 | $data = array(); |
||
| 1254 | while ($row = $smcFunc['db_fetch_assoc']($request)) |
||
| 1255 | { |
||
| 1256 | // Limit the length of the message, if the option is set. |
||
| 1257 | if (!empty($modSettings['xmlnews_maxlen']) && $smcFunc['strlen'](str_replace('<br>', "\n", $row['body'])) > $modSettings['xmlnews_maxlen']) |
||
| 1258 | $row['body'] = strtr($smcFunc['substr'](str_replace('<br>', "\n", $row['body']), 0, $modSettings['xmlnews_maxlen'] - 3), array("\n" => '<br>')) . '...'; |
||
| 1259 | |||
| 1260 | $row['body'] = parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']); |
||
| 1261 | |||
| 1262 | censorText($row['body']); |
||
| 1263 | censorText($row['subject']); |
||
| 1264 | |||
| 1265 | // Do we want to include any attachments? |
||
| 1266 | if (!empty($modSettings['attachmentEnable']) && !empty($modSettings['xmlnews_attachments']) && allowedTo('view_attachments', $row['id_board'])) |
||
| 1267 | { |
||
| 1268 | $attach_request = $smcFunc['db_query']('', ' |
||
| 1269 | SELECT |
||
| 1270 | a.id_attach, a.filename, COALESCE(a.size, 0) AS filesize, a.mime_type, a.downloads, a.approved, m.id_topic AS topic |
||
| 1271 | FROM {db_prefix}attachments AS a |
||
| 1272 | LEFT JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg) |
||
| 1273 | WHERE a.attachment_type = {int:attachment_type} |
||
| 1274 | AND a.id_msg = {int:message_id}', |
||
| 1275 | array( |
||
| 1276 | 'message_id' => $row['id_msg'], |
||
| 1277 | 'attachment_type' => 0, |
||
| 1278 | 'is_approved' => 1, |
||
| 1279 | ) |
||
| 1280 | ); |
||
| 1281 | $loaded_attachments = array(); |
||
| 1282 | while ($attach = $smcFunc['db_fetch_assoc']($attach_request)) |
||
| 1283 | { |
||
| 1284 | // Include approved attachments only |
||
| 1285 | if ($attach['approved']) |
||
| 1286 | $loaded_attachments['attachment_' . $attach['id_attach']] = $attach; |
||
| 1287 | } |
||
| 1288 | $smcFunc['db_free_result']($attach_request); |
||
| 1289 | |||
| 1290 | // Sort the attachments by size to make things easier below |
||
| 1291 | if (!empty($loaded_attachments)) |
||
| 1292 | { |
||
| 1293 | uasort($loaded_attachments, function($a, $b) |
||
| 1294 | { |
||
| 1295 | if ($a['filesize'] == $b['filesize']) |
||
| 1296 | return 0; |
||
| 1297 | |||
| 1298 | return ($a['filesize'] < $b['filesize']) ? -1 : 1; |
||
| 1299 | }); |
||
| 1300 | } |
||
| 1301 | else |
||
| 1302 | $loaded_attachments = null; |
||
| 1303 | } |
||
| 1304 | else |
||
| 1305 | $loaded_attachments = null; |
||
| 1306 | |||
| 1307 | // Create a GUID for this post using the tag URI scheme |
||
| 1308 | $guid = 'tag:' . parse_url($scripturl, PHP_URL_HOST) . ',' . gmdate('Y-m-d', $row['poster_time']) . ':msg=' . $row['id_msg']; |
||
| 1309 | |||
| 1310 | // Doesn't work as well as news, but it kinda does.. |
||
| 1311 | if ($xml_format == 'rss' || $xml_format == 'rss2') |
||
| 1312 | { |
||
| 1313 | // Only one attachment allowed in RSS. |
||
| 1314 | if ($loaded_attachments !== null) |
||
| 1315 | { |
||
| 1316 | $attachment = array_pop($loaded_attachments); |
||
| 1317 | $enclosure = array( |
||
| 1318 | 'url' => fix_possible_url($scripturl . '?action=dlattach;topic=' . $attachment['topic'] . '.0;attach=' . $attachment['id_attach']), |
||
| 1319 | 'length' => $attachment['filesize'], |
||
| 1320 | 'type' => $attachment['mime_type'], |
||
| 1321 | ); |
||
| 1322 | } |
||
| 1323 | else |
||
| 1324 | $enclosure = null; |
||
| 1325 | |||
| 1326 | $data[] = array( |
||
| 1327 | 'tag' => 'item', |
||
| 1328 | 'content' => array( |
||
| 1329 | array( |
||
| 1330 | 'tag' => 'title', |
||
| 1331 | 'content' => $row['subject'], |
||
| 1332 | ), |
||
| 1333 | array( |
||
| 1334 | 'tag' => 'link', |
||
| 1335 | 'content' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'], |
||
| 1336 | ), |
||
| 1337 | array( |
||
| 1338 | 'tag' => 'description', |
||
| 1339 | 'content' => $row['body'], |
||
| 1340 | ), |
||
| 1341 | array( |
||
| 1342 | 'tag' => 'author', |
||
| 1343 | 'content' => (allowedTo('moderate_forum') || (!empty($row['id_member']) && $row['id_member'] == $user_info['id'])) ? $row['poster_email'] : null, |
||
| 1344 | ), |
||
| 1345 | array( |
||
| 1346 | 'tag' => 'category', |
||
| 1347 | 'content' => $row['bname'], |
||
| 1348 | ), |
||
| 1349 | array( |
||
| 1350 | 'tag' => 'comments', |
||
| 1351 | 'content' => $scripturl . '?action=post;topic=' . $row['id_topic'] . '.0', |
||
| 1352 | ), |
||
| 1353 | array( |
||
| 1354 | 'tag' => 'pubDate', |
||
| 1355 | 'content' => gmdate('D, d M Y H:i:s \G\M\T', $row['poster_time']), |
||
| 1356 | ), |
||
| 1357 | array( |
||
| 1358 | 'tag' => 'guid', |
||
| 1359 | 'content' => $guid, |
||
| 1360 | 'attributes' => array( |
||
| 1361 | 'isPermaLink' => 'false', |
||
| 1362 | ), |
||
| 1363 | ), |
||
| 1364 | array( |
||
| 1365 | 'tag' => 'enclosure', |
||
| 1366 | 'attributes' => $enclosure, |
||
| 1367 | ), |
||
| 1368 | ), |
||
| 1369 | ); |
||
| 1370 | } |
||
| 1371 | elseif ($xml_format == 'rdf') |
||
| 1372 | { |
||
| 1373 | $data[] = array( |
||
| 1374 | 'tag' => 'item', |
||
| 1375 | 'attributes' => array('rdf:about' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg']), |
||
| 1376 | 'content' => array( |
||
| 1377 | array( |
||
| 1378 | 'tag' => 'dc:format', |
||
| 1379 | 'content' => 'text/html', |
||
| 1380 | ), |
||
| 1381 | array( |
||
| 1382 | 'tag' => 'title', |
||
| 1383 | 'content' => $row['subject'], |
||
| 1384 | ), |
||
| 1385 | array( |
||
| 1386 | 'tag' => 'link', |
||
| 1387 | 'content' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'], |
||
| 1388 | ), |
||
| 1389 | array( |
||
| 1390 | 'tag' => 'description', |
||
| 1391 | 'content' => $row['body'], |
||
| 1392 | ), |
||
| 1393 | ), |
||
| 1394 | ); |
||
| 1395 | } |
||
| 1396 | elseif ($xml_format == 'atom') |
||
| 1397 | { |
||
| 1398 | // Only one attachment allowed |
||
| 1399 | if (!empty($loaded_attachments)) |
||
| 1400 | { |
||
| 1401 | $attachment = array_pop($loaded_attachments); |
||
| 1402 | $enclosure = array( |
||
| 1403 | 'rel' => 'enclosure', |
||
| 1404 | 'href' => fix_possible_url($scripturl . '?action=dlattach;topic=' . $attachment['topic'] . '.0;attach=' . $attachment['id_attach']), |
||
| 1405 | 'length' => $attachment['filesize'], |
||
| 1406 | 'type' => $attachment['mime_type'], |
||
| 1407 | ); |
||
| 1408 | } |
||
| 1409 | else |
||
| 1410 | $enclosure = null; |
||
| 1411 | |||
| 1412 | $data[] = array( |
||
| 1413 | 'tag' => 'entry', |
||
| 1414 | 'content' => array( |
||
| 1415 | array( |
||
| 1416 | 'tag' => 'title', |
||
| 1417 | 'content' => $row['subject'], |
||
| 1418 | ), |
||
| 1419 | array( |
||
| 1420 | 'tag' => 'link', |
||
| 1421 | 'attributes' => array( |
||
| 1422 | 'rel' => 'alternate', |
||
| 1423 | 'type' => 'text/html', |
||
| 1424 | 'href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'], |
||
| 1425 | ), |
||
| 1426 | ), |
||
| 1427 | array( |
||
| 1428 | 'tag' => 'summary', |
||
| 1429 | 'attributes' => array('type' => 'html'), |
||
| 1430 | 'content' => $row['body'], |
||
| 1431 | ), |
||
| 1432 | array( |
||
| 1433 | 'tag' => 'category', |
||
| 1434 | 'attributes' => array('term' => $row['bname']), |
||
| 1435 | ), |
||
| 1436 | array( |
||
| 1437 | 'tag' => 'author', |
||
| 1438 | 'content' => array( |
||
| 1439 | array( |
||
| 1440 | 'tag' => 'name', |
||
| 1441 | 'content' => $row['poster_name'], |
||
| 1442 | ), |
||
| 1443 | array( |
||
| 1444 | 'tag' => 'email', |
||
| 1445 | 'content' => (allowedTo('moderate_forum') || (!empty($row['id_member']) && $row['id_member'] == $user_info['id'])) ? $row['poster_email'] : null, |
||
| 1446 | ), |
||
| 1447 | array( |
||
| 1448 | 'tag' => 'uri', |
||
| 1449 | 'content' => !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : null, |
||
| 1450 | ), |
||
| 1451 | ), |
||
| 1452 | ), |
||
| 1453 | array( |
||
| 1454 | 'tag' => 'published', |
||
| 1455 | 'content' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $row['poster_time']), |
||
| 1456 | ), |
||
| 1457 | array( |
||
| 1458 | 'tag' => 'updated', |
||
| 1459 | 'content' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', empty($row['modified_time']) ? $row['poster_time'] : $row['modified_time']), |
||
| 1460 | ), |
||
| 1461 | array( |
||
| 1462 | 'tag' => 'id', |
||
| 1463 | 'content' => $guid, |
||
| 1464 | ), |
||
| 1465 | array( |
||
| 1466 | 'tag' => 'link', |
||
| 1467 | 'attributes' => $enclosure, |
||
| 1468 | ), |
||
| 1469 | ), |
||
| 1470 | ); |
||
| 1471 | } |
||
| 1472 | // A lot of information here. Should be enough to please the rss-ers. |
||
| 1473 | else |
||
| 1474 | { |
||
| 1475 | $attachments = array(); |
||
| 1476 | if (!empty($loaded_attachments)) |
||
| 1477 | { |
||
| 1478 | foreach ($loaded_attachments as $attachment) |
||
| 1479 | { |
||
| 1480 | $attachments[] = array( |
||
| 1481 | 'tag' => 'attachment', |
||
| 1482 | 'content' => array( |
||
| 1483 | array( |
||
| 1484 | 'tag' => 'id', |
||
| 1485 | 'content' => $attachment['id_attach'], |
||
| 1486 | ), |
||
| 1487 | array( |
||
| 1488 | 'tag' => 'name', |
||
| 1489 | 'content' => preg_replace('~&#(\\d{1,7}|x[0-9a-fA-F]{1,6});~', '&#\\1;', $smcFunc['htmlspecialchars']($attachment['filename'])), |
||
| 1490 | ), |
||
| 1491 | array( |
||
| 1492 | 'tag' => 'downloads', |
||
| 1493 | 'content' => $attachment['downloads'], |
||
| 1494 | ), |
||
| 1495 | array( |
||
| 1496 | 'tag' => 'size', |
||
| 1497 | 'content' => ($attachment['filesize'] < 1024000) ? round($attachment['filesize'] / 1024, 2) . ' ' . $txt['kilobyte'] : round($attachment['filesize'] / 1024 / 1024, 2) . ' ' . $txt['megabyte'], |
||
| 1498 | ), |
||
| 1499 | array( |
||
| 1500 | 'tag' => 'byte_size', |
||
| 1501 | 'content' => $attachment['filesize'], |
||
| 1502 | ), |
||
| 1503 | array( |
||
| 1504 | 'tag' => 'link', |
||
| 1505 | 'content' => $scripturl . '?action=dlattach;topic=' . $attachment['topic'] . '.0;attach=' . $attachment['id_attach'], |
||
| 1506 | ), |
||
| 1507 | ) |
||
| 1508 | ); |
||
| 1509 | } |
||
| 1510 | } |
||
| 1511 | else |
||
| 1512 | $attachments = null; |
||
| 1513 | |||
| 1514 | $data[] = array( |
||
| 1515 | 'tag' => 'recent-post', |
||
| 1516 | 'content' => array( |
||
| 1517 | array( |
||
| 1518 | 'tag' => 'time', |
||
| 1519 | 'content' => $smcFunc['htmlspecialchars'](strip_tags(timeformat($row['poster_time']))), |
||
| 1520 | ), |
||
| 1521 | array( |
||
| 1522 | 'tag' => 'id', |
||
| 1523 | 'content' => $row['id_msg'], |
||
| 1524 | ), |
||
| 1525 | array( |
||
| 1526 | 'tag' => 'subject', |
||
| 1527 | 'content' => $row['subject'], |
||
| 1528 | ), |
||
| 1529 | array( |
||
| 1530 | 'tag' => 'body', |
||
| 1531 | 'content' => $row['body'], |
||
| 1532 | ), |
||
| 1533 | array( |
||
| 1534 | 'tag' => 'starter', |
||
| 1535 | 'content' => array( |
||
| 1536 | array( |
||
| 1537 | 'tag' => 'name', |
||
| 1538 | 'content' => $row['first_poster_name'], |
||
| 1539 | ), |
||
| 1540 | array( |
||
| 1541 | 'tag' => 'id', |
||
| 1542 | 'content' => $row['id_first_member'], |
||
| 1543 | ), |
||
| 1544 | array( |
||
| 1545 | 'tag' => 'link', |
||
| 1546 | 'content' => !empty($row['id_first_member']) ? $scripturl . '?action=profile;u=' . $row['id_first_member'] : '', |
||
| 1547 | ), |
||
| 1548 | ), |
||
| 1549 | ), |
||
| 1550 | array( |
||
| 1551 | 'tag' => 'poster', |
||
| 1552 | 'content' => array( |
||
| 1553 | array( |
||
| 1554 | 'tag' => 'name', |
||
| 1555 | 'content' => $row['poster_name'], |
||
| 1556 | ), |
||
| 1557 | array( |
||
| 1558 | 'tag' => 'id', |
||
| 1559 | 'content' => $row['id_member'], |
||
| 1560 | ), |
||
| 1561 | array( |
||
| 1562 | 'tag' => 'link', |
||
| 1563 | 'content' => !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : '', |
||
| 1564 | ), |
||
| 1565 | ), |
||
| 1566 | ), |
||
| 1567 | array( |
||
| 1568 | 'tag' => 'topic', |
||
| 1569 | 'content' => array( |
||
| 1570 | array( |
||
| 1571 | 'tag' => 'subject', |
||
| 1572 | 'content' => $row['first_subject'], |
||
| 1573 | ), |
||
| 1574 | array( |
||
| 1575 | 'tag' => 'id', |
||
| 1576 | 'content' => $row['id_topic'], |
||
| 1577 | ), |
||
| 1578 | array( |
||
| 1579 | 'tag' => 'link', |
||
| 1580 | 'content' => $scripturl . '?topic=' . $row['id_topic'] . '.new#new', |
||
| 1581 | ), |
||
| 1582 | ), |
||
| 1583 | ), |
||
| 1584 | array( |
||
| 1585 | 'tag' => 'board', |
||
| 1586 | 'content' => array( |
||
| 1587 | array( |
||
| 1588 | 'tag' => 'name', |
||
| 1589 | 'content' => $row['bname'], |
||
| 1590 | ), |
||
| 1591 | array( |
||
| 1592 | 'tag' => 'id', |
||
| 1593 | 'content' => $row['id_board'], |
||
| 1594 | ), |
||
| 1595 | array( |
||
| 1596 | 'tag' => 'link', |
||
| 1597 | 'content' => $scripturl . '?board=' . $row['id_board'] . '.0', |
||
| 1598 | ), |
||
| 1599 | ), |
||
| 1600 | ), |
||
| 1601 | array( |
||
| 1602 | 'tag' => 'link', |
||
| 1603 | 'content' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'], |
||
| 1604 | ), |
||
| 1605 | array( |
||
| 1606 | 'tag' => 'attachments', |
||
| 1607 | 'content' => $attachments, |
||
| 1608 | ), |
||
| 1609 | ), |
||
| 1610 | ); |
||
| 1611 | } |
||
| 1612 | } |
||
| 1613 | $smcFunc['db_free_result']($request); |
||
| 1614 | |||
| 1615 | return $data; |
||
| 1616 | } |
||
| 1617 | |||
| 1618 | /** |
||
| 1619 | * Get the profile information for member into an array, |
||
| 1620 | * which will be generated to match the xml_format. |
||
| 1621 | * |
||
| 1622 | * @todo refactor. |
||
| 1623 | * |
||
| 1624 | * @param string $xml_format The XML format. Can be 'atom', 'rdf', 'rss', 'rss2' or 'smf' |
||
| 1625 | * @return array An array profile data |
||
| 1626 | */ |
||
| 1627 | function getXmlProfile($xml_format) |
||
| 1628 | { |
||
| 1629 | global $scripturl, $memberContext, $user_profile, $user_info; |
||
| 1630 | |||
| 1631 | // You must input a valid user.... |
||
| 1632 | if (empty($_GET['u']) || !loadMemberData((int) $_GET['u'])) |
||
| 1633 | return array(); |
||
| 1634 | |||
| 1635 | // Make sure the id is a number and not "I like trying to hack the database". |
||
| 1636 | $_GET['u'] = (int) $_GET['u']; |
||
| 1637 | // Load the member's contextual information! |
||
| 1638 | if (!loadMemberContext($_GET['u']) || !allowedTo('profile_view')) |
||
| 1639 | return array(); |
||
| 1640 | |||
| 1641 | // Okay, I admit it, I'm lazy. Stupid $_GET['u'] is long and hard to type. |
||
| 1642 | $profile = &$memberContext[$_GET['u']]; |
||
| 1643 | |||
| 1644 | // Create a GUID for this member using the tag URI scheme |
||
| 1645 | $guid = 'tag:' . parse_url($scripturl, PHP_URL_HOST) . ',' . gmdate('Y-m-d', $user_profile[$profile['id']]['date_registered']) . ':member=' . $profile['id']; |
||
| 1646 | |||
| 1647 | if ($xml_format == 'rss' || $xml_format == 'rss2') |
||
| 1648 | { |
||
| 1649 | $data[] = array( |
||
| 1650 | 'tag' => 'item', |
||
| 1651 | 'content' => array( |
||
| 1652 | array( |
||
| 1653 | 'tag' => 'title', |
||
| 1654 | 'content' => $profile['name'], |
||
| 1655 | ), |
||
| 1656 | array( |
||
| 1657 | 'tag' => 'link', |
||
| 1658 | 'content' => $scripturl . '?action=profile;u=' . $profile['id'], |
||
| 1659 | ), |
||
| 1660 | array( |
||
| 1661 | 'tag' => 'description', |
||
| 1662 | 'content' => isset($profile['group']) ? $profile['group'] : $profile['post_group'], |
||
| 1663 | ), |
||
| 1664 | array( |
||
| 1665 | 'tag' => 'comments', |
||
| 1666 | 'content' => $scripturl . '?action=pm;sa=send;u=' . $profile['id'], |
||
| 1667 | ), |
||
| 1668 | array( |
||
| 1669 | 'tag' => 'pubDate', |
||
| 1670 | 'content' => gmdate('D, d M Y H:i:s \G\M\T', $user_profile[$profile['id']]['date_registered']), |
||
| 1671 | ), |
||
| 1672 | array( |
||
| 1673 | 'tag' => 'guid', |
||
| 1674 | 'content' => $guid, |
||
| 1675 | 'attributes' => array( |
||
| 1676 | 'isPermaLink' => 'false', |
||
| 1677 | ), |
||
| 1678 | ), |
||
| 1679 | ) |
||
| 1680 | ); |
||
| 1681 | } |
||
| 1682 | elseif ($xml_format == 'rdf') |
||
| 1683 | { |
||
| 1684 | $data[] = array( |
||
| 1685 | 'tag' => 'item', |
||
| 1686 | 'attributes' => array('rdf:about' => $scripturl . '?action=profile;u=' . $profile['id']), |
||
| 1687 | 'content' => array( |
||
| 1688 | array( |
||
| 1689 | 'tag' => 'dc:format', |
||
| 1690 | 'content' => 'text/html', |
||
| 1691 | ), |
||
| 1692 | array( |
||
| 1693 | 'tag' => 'title', |
||
| 1694 | 'content' => $profile['name'], |
||
| 1695 | ), |
||
| 1696 | array( |
||
| 1697 | 'tag' => 'link', |
||
| 1698 | 'content' => $scripturl . '?action=profile;u=' . $profile['id'], |
||
| 1699 | ), |
||
| 1700 | array( |
||
| 1701 | 'tag' => 'description', |
||
| 1702 | 'content' => isset($profile['group']) ? $profile['group'] : $profile['post_group'], |
||
| 1703 | ), |
||
| 1704 | ) |
||
| 1705 | ); |
||
| 1706 | } |
||
| 1707 | elseif ($xml_format == 'atom') |
||
| 1708 | { |
||
| 1709 | $data[] = array( |
||
| 1710 | 'tag' => 'entry', |
||
| 1711 | 'content' => array( |
||
| 1712 | array( |
||
| 1713 | 'tag' => 'title', |
||
| 1714 | 'content' => $profile['name'], |
||
| 1715 | ), |
||
| 1716 | array( |
||
| 1717 | 'tag' => 'link', |
||
| 1718 | 'attributes' => array( |
||
| 1719 | 'rel' => 'alternate', |
||
| 1720 | 'type' => 'text/html', |
||
| 1721 | 'href' => $scripturl . '?action=profile;u=' . $profile['id'], |
||
| 1722 | ), |
||
| 1723 | ), |
||
| 1724 | array( |
||
| 1725 | 'tag' => 'summary', |
||
| 1726 | 'attributes' => array('type' => 'html'), |
||
| 1727 | 'content' => isset($profile['group']) ? $profile['group'] : $profile['post_group'], |
||
| 1728 | ), |
||
| 1729 | array( |
||
| 1730 | 'tag' => 'author', |
||
| 1731 | 'content' => array( |
||
| 1732 | array( |
||
| 1733 | 'tag' => 'name', |
||
| 1734 | 'content' => $profile['name'], |
||
| 1735 | ), |
||
| 1736 | array( |
||
| 1737 | 'tag' => 'email', |
||
| 1738 | 'content' => $profile['show_email'] ? $profile['email'] : null, |
||
| 1739 | ), |
||
| 1740 | array( |
||
| 1741 | 'tag' => 'uri', |
||
| 1742 | 'content' => !empty($profile['website']['url']) ? $profile['website']['url'] : null, |
||
| 1743 | ), |
||
| 1744 | ), |
||
| 1745 | ), |
||
| 1746 | array( |
||
| 1747 | 'tag' => 'published', |
||
| 1748 | 'content' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $user_profile[$profile['id']]['date_registered']), |
||
| 1749 | ), |
||
| 1750 | array( |
||
| 1751 | 'tag' => 'updated', |
||
| 1752 | 'content' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $user_profile[$profile['id']]['last_login']), |
||
| 1753 | ), |
||
| 1754 | array( |
||
| 1755 | 'tag' => 'id', |
||
| 1756 | 'content' => $guid, |
||
| 1757 | ), |
||
| 1758 | ) |
||
| 1759 | ); |
||
| 1760 | } |
||
| 1761 | else |
||
| 1762 | { |
||
| 1763 | $data = array( |
||
| 1764 | array( |
||
| 1765 | 'tag' => 'username', |
||
| 1766 | 'content' => $user_info['is_admin'] || $user_info['id'] == $profile['id'] ? $profile['username'] : null, |
||
| 1767 | ), |
||
| 1768 | array( |
||
| 1769 | 'tag' => 'name', |
||
| 1770 | 'content' => $profile['name'], |
||
| 1771 | ), |
||
| 1772 | array( |
||
| 1773 | 'tag' => 'link', |
||
| 1774 | 'content' => $scripturl . '?action=profile;u=' . $profile['id'], |
||
| 1775 | ), |
||
| 1776 | array( |
||
| 1777 | 'tag' => 'posts', |
||
| 1778 | 'content' => $profile['posts'], |
||
| 1779 | ), |
||
| 1780 | array( |
||
| 1781 | 'tag' => 'post-group', |
||
| 1782 | 'content' => $profile['post_group'], |
||
| 1783 | ), |
||
| 1784 | array( |
||
| 1785 | 'tag' => 'language', |
||
| 1786 | 'content' => $profile['language'], |
||
| 1787 | ), |
||
| 1788 | array( |
||
| 1789 | 'tag' => 'last-login', |
||
| 1790 | 'content' => gmdate('D, d M Y H:i:s \G\M\T', $user_profile[$profile['id']]['last_login']), |
||
| 1791 | ), |
||
| 1792 | array( |
||
| 1793 | 'tag' => 'registered', |
||
| 1794 | 'content' => gmdate('D, d M Y H:i:s \G\M\T', $user_profile[$profile['id']]['date_registered']), |
||
| 1795 | ), |
||
| 1796 | array( |
||
| 1797 | 'tag' => 'gender', |
||
| 1798 | 'content' => !empty($profile['gender']['name']) ? $profile['gender']['name'] : null, |
||
| 1799 | ), |
||
| 1800 | array( |
||
| 1801 | 'tag' => 'avatar', |
||
| 1802 | 'content' => !empty($profile['avatar']['url']) ? $profile['avatar']['url'] : null, |
||
| 1803 | ), |
||
| 1804 | array( |
||
| 1805 | 'tag' => 'online', |
||
| 1806 | 'content' => !empty($profile['online']['is_online']) ? '' : null, |
||
| 1807 | ), |
||
| 1808 | array( |
||
| 1809 | 'tag' => 'signature', |
||
| 1810 | 'content' => !empty($profile['signature']) ? $profile['signature'] : null, |
||
| 1811 | ), |
||
| 1812 | array( |
||
| 1813 | 'tag' => 'blurb', |
||
| 1814 | 'content' => !empty($profile['blurb']) ? $profile['blurb'] : null, |
||
| 1815 | ), |
||
| 1816 | array( |
||
| 1817 | 'tag' => 'title', |
||
| 1818 | 'content' => !empty($profile['title']) ? $profile['title'] : null, |
||
| 1819 | ), |
||
| 1820 | array( |
||
| 1821 | 'tag' => 'position', |
||
| 1822 | 'content' => !empty($profile['group']) ? $profile['group'] : null, |
||
| 1823 | ), |
||
| 1824 | array( |
||
| 1825 | 'tag' => 'email', |
||
| 1826 | 'content' => !empty($profile['show_email']) ? $profile['show_email'] : null, |
||
| 1827 | ), |
||
| 1828 | array( |
||
| 1829 | 'tag' => 'website', |
||
| 1830 | 'content' => empty($profile['website']['url']) ? null : array( |
||
| 1831 | array( |
||
| 1832 | 'tag' => 'title', |
||
| 1833 | 'content' => !empty($profile['website']['title']) ? $profile['website']['title'] : null, |
||
| 1834 | ), |
||
| 1835 | array( |
||
| 1836 | 'tag' => 'link', |
||
| 1837 | 'content' => $profile['website']['url'], |
||
| 1838 | ), |
||
| 1839 | ), |
||
| 1840 | ), |
||
| 1841 | ); |
||
| 1842 | |||
| 1843 | if (!empty($profile['birth_date']) && substr($profile['birth_date'], 0, 4) != '0000') |
||
| 1844 | { |
||
| 1845 | list ($birth_year, $birth_month, $birth_day) = sscanf($profile['birth_date'], '%d-%d-%d'); |
||
| 1846 | $datearray = getdate(forum_time()); |
||
| 1847 | $age = $datearray['year'] - $birth_year - (($datearray['mon'] > $birth_month || ($datearray['mon'] == $birth_month && $datearray['mday'] >= $birth_day)) ? 0 : 1); |
||
| 1848 | |||
| 1849 | $data[] = array( |
||
| 1850 | 'tag' => 'age', |
||
| 1851 | 'content' => $age, |
||
| 1852 | ); |
||
| 1853 | } |
||
| 1854 | } |
||
| 1855 | |||
| 1856 | // Save some memory. |
||
| 1857 | unset($profile, $memberContext[$_GET['u']]); |
||
| 1858 | |||
| 1859 | return $data; |
||
| 1860 | } |
||
| 1861 | |||
| 1862 | ?> |