Passed
Push — release-2.1 ( 0c2197...207d2d )
by Jeremy
05:47
created

News.php ➔ getXmlProfile()   F

Complexity

Conditions 31
Paths > 20000

Size

Total Lines 234

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 31
nc 28686
nop 1
dl 0
loc 234
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 http://www.simplemachines.org
10
 * @copyright 2018 Simple Machines and individual contributors
11
 * @license http://www.simplemachines.org/about/smf/license.php BSD
12
 *
13
 * @version 2.1 Beta 4
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, $forum_version, $settings;
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($modSettings['cache_enable']) && (!$user_info['is_guest'] || $modSettings['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);
0 ignored issues
show
Bug introduced by
It seems like $call can also be of type boolean; however, parameter $function of call_user_func() does only seem to accept callable, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

218
			$xml_data = call_user_func(/** @scrutinizer ignore-type */ $call, $xml_format);
Loading history...
219
220
		if (!empty($modSettings['cache_enable']) && (($user_info['is_guest'] && $modSettings['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);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $xml_data does not seem to be defined for all execution paths leading up to this point.
Loading history...
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');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for ob_start(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

287
		/** @scrutinizer ignore-unhandled */ @ob_start('ob_gzhandler');

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
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="', strtr($forum_version, array('SMF' => '')), '">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) { return ($e['tag'] == 'link'); });
393
			$link = array_pop($link);
394
395
			echo '
396
				<rdf:li rdf:resource="', $link['content'], '" />';
397
		}
398
399
		echo '
400
			</rdf:Seq>
401
		</items>
402
	</channel>
403
';
404
405
		dumpTags($xml_data, 1, null, $xml_format, $forceCdataKeys, $nsKeys);
406
407
		echo '
408
</rdf:RDF>';
409
	}
410
	// Otherwise, we're using our proprietary formats - they give more data, though.
411
	else
412
	{
413
		echo '
414
<smf:xml-feed xml:lang="', strtr($txt['lang_locale'], '_', '-'), '"', $ns_string, '>';
415
416
		// Hard to imagine anyone wanting to add these for the proprietary format, but just in case...
417
		echo $extraFeedTags_string;
418
419
		// Dump out that associative array.  Indent properly.... and use the right names for the base elements.
420
		dumpTags($xml_data, 1, $subActions[$_GET['sa']][1], $xml_format, $forceCdataKeys, $nsKeys);
421
422
		echo '
423
</smf:xml-feed>';
424
}
425
426
	obExit(false);
427
}
428
429
/**
430
 * Called from dumpTags to convert data to xml
431
 * Finds urls for local site and sanitizes them
432
 *
433
 * @param string $val A string containing a possible URL
434
 * @return string $val The string with any possible URLs sanitized
435
 */
436
function fix_possible_url($val)
437
{
438
	global $modSettings, $context, $scripturl;
439
440
	if (substr($val, 0, strlen($scripturl)) != $scripturl)
441
		return $val;
442
443
	call_integration_hook('integrate_fix_url', array(&$val));
444
445
	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']))
446
		return $val;
447
448
	$val = preg_replace_callback('~\b' . preg_quote($scripturl, '~') . '\?((?:board|topic)=[^#"]+)(#[^"]*)?$~', function($m) use ($scripturl)
449
		{
450
			return $scripturl . '/' . strtr("$m[1]", '&;=', '//,') . '.html' . (isset($m[2]) ? $m[2] : "");
451
		}, $val);
452
	return $val;
453
}
454
455
/**
456
 * Ensures supplied data is properly encapsulated in cdata xml tags
457
 * Called from getXmlProfile in News.php
458
 *
459
 * @param string $data XML data
460
 * @param string $ns A namespace prefix for the XML data elements (used by mods, maybe)
461
 * @param boolean $force If true, enclose the XML data in cdata tags no matter what (used by mods, maybe)
462
 * @return string The XML data enclosed in cdata tags when necessary
463
 */
464
function cdata_parse($data, $ns = '', $force = false)
465
{
466
	global $smcFunc;
467
468
	// Do we even need to do this?
469
	if (strpbrk($data, '<>&') == false && $force !== true)
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing strpbrk($data, '<>&') of type string to the boolean false. If you are specifically checking for an empty string, consider using the more explicit === '' instead.
Loading history...
470
		return $data;
471
472
	$cdata = '<![CDATA[';
473
474
	for ($pos = 0, $n = $smcFunc['strlen']($data); $pos < $n; null)
475
	{
476
		$positions = array(
477
			$smcFunc['strpos']($data, '&', $pos),
478
			$smcFunc['strpos']($data, ']', $pos),
479
		);
480
		if ($ns != '')
481
			$positions[] = $smcFunc['strpos']($data, '<', $pos);
482
		foreach ($positions as $k => $dummy)
483
		{
484
			if ($dummy === false)
485
				unset($positions[$k]);
486
		}
487
488
		$old = $pos;
489
		$pos = empty($positions) ? $n : min($positions);
490
491
		if ($pos - $old > 0)
492
			$cdata .= $smcFunc['substr']($data, $old, $pos - $old);
493
		if ($pos >= $n)
494
			break;
495
496
		if ($smcFunc['substr']($data, $pos, 1) == '<')
497
		{
498
			$pos2 = $smcFunc['strpos']($data, '>', $pos);
499
			if ($pos2 === false)
500
				$pos2 = $n;
501
			if ($smcFunc['substr']($data, $pos + 1, 1) == '/')
502
				$cdata .= ']]></' . $ns . ':' . $smcFunc['substr']($data, $pos + 2, $pos2 - $pos - 1) . '<![CDATA[';
503
			else
504
				$cdata .= ']]><' . $ns . ':' . $smcFunc['substr']($data, $pos + 1, $pos2 - $pos) . '<![CDATA[';
505
			$pos = $pos2 + 1;
506
		}
507
		elseif ($smcFunc['substr']($data, $pos, 1) == ']')
508
		{
509
			$cdata .= ']]>&#093;<![CDATA[';
510
			$pos++;
511
		}
512
		elseif ($smcFunc['substr']($data, $pos, 1) == '&')
513
		{
514
			$pos2 = $smcFunc['strpos']($data, ';', $pos);
515
			if ($pos2 === false)
516
				$pos2 = $n;
517
			$ent = $smcFunc['substr']($data, $pos + 1, $pos2 - $pos - 1);
518
519
			if ($smcFunc['substr']($data, $pos + 1, 1) == '#')
520
				$cdata .= ']]>' . $smcFunc['substr']($data, $pos, $pos2 - $pos + 1) . '<![CDATA[';
521
			elseif (in_array($ent, array('amp', 'lt', 'gt', 'quot')))
522
				$cdata .= ']]>' . $smcFunc['substr']($data, $pos, $pos2 - $pos + 1) . '<![CDATA[';
523
524
			$pos = $pos2 + 1;
525
		}
526
	}
527
528
	$cdata .= ']]>';
529
530
	return strtr($cdata, array('<![CDATA[]]>' => ''));
531
}
532
533
/**
534
 * Formats data retrieved in other functions into xml format.
535
 * Additionally formats data based on the specific format passed.
536
 * This function is recursively called to handle sub arrays of data.
537
 *
538
 * @param array $data The array to output as xml data
539
 * @param int $i The amount of indentation to use.
540
 * @param null|string $tag
541
 * @param string $xml_format The format to use ('atom', 'rss', 'rss2' or empty for plain XML)
542
 * @param array $forceCdataKeys A list of keys on which to force cdata wrapping (used by mods, maybe)
543
 * @param array $nsKeys Key-value pairs of namespace prefixes to pass to cdata_parse() (used by mods, maybe)
544
 */
545
function dumpTags($data, $i, $tag = null, $xml_format = '', $forceCdataKeys = array(), $nsKeys = array())
546
{
547
	// Wrap the values of these keys into CDATA tags
548
	$keysToCdata = array(
549
		'title',
550
		'name',
551
		'description',
552
		'summary',
553
		'subject',
554
		'body',
555
		'username',
556
		'signature',
557
		'position',
558
		'language',
559
		'gender',
560
		'blurb',
561
	);
562
	if ($xml_format != 'atom')
563
		$keysToCdata[] = 'category';
564
565
	if (!empty($forceCdataKeys))
566
	{
567
		$keysToCdata = array_merge($keysToCdata, $forceCdataKeys);
568
		$keysToCdata = array_unique($keysToCdata);
569
	}
570
571
	// For every array in the data...
572
	foreach ($data as $element)
573
	{
574
		// If a tag was passed, use it instead of the key.
575
		$key = isset($tag) ? $tag : (isset($element['tag']) ? $element['tag'] : null);
576
		$val = isset($element['content']) ? $element['content'] : null;
577
		$attrs = isset($element['attributes']) ? $element['attributes'] : null;
578
579
		// Skip it, it's been set to null.
580
		if ($key === null || ($val === null && $attrs === null))
581
			continue;
582
583
		$forceCdata = in_array($key, $forceCdataKeys);
584
		$ns = !empty($nsKeys[$key]) ? $nsKeys[$key] : '';
585
586
		// First let's indent!
587
		echo "\n", str_repeat("\t", $i);
588
589
		// Beginning tag.
590
		echo '<', $key;
591
592
		if (!empty($attrs))
593
		{
594
			foreach ($attrs as $attr_key => $attr_value)
595
				echo ' ', $attr_key, '="', fix_possible_url($attr_value), '"';
596
		}
597
598
		// If it's empty, simply output an empty element.
599
		if (empty($val))
600
		{
601
			echo ' />';
602
		}
603
		else
604
		{
605
			echo '>';
606
607
			// The element's value.
608
			if (is_array($val))
609
			{
610
				// An array.  Dump it, and then indent the tag.
611
				dumpTags($val, $i + 1, null, $xml_format, $forceCdataKeys, $nsKeys);
612
				echo "\n", str_repeat("\t", $i);
613
			}
614
			// A string with returns in it.... show this as a multiline element.
615
			elseif (strpos($val, "\n") !== false)
616
				echo "\n", in_array($key, $keysToCdata) ? cdata_parse(fix_possible_url($val), $ns, $forceCdata) : fix_possible_url($val), "\n", str_repeat("\t", $i);
617
			// A simple string.
618
			else
619
				echo in_array($key, $keysToCdata) ? cdata_parse(fix_possible_url($val), $ns, $forceCdata) : fix_possible_url($val);
620
621
			// Ending tag.
622
			echo '</', $key, '>';
623
		}
624
	}
625
}
626
627
/**
628
 * Retrieve the list of members from database.
629
 * The array will be generated to match the format.
630
 * @todo get the list of members from Subs-Members.
631
 *
632
 * @param string $xml_format The format to use. Can be 'atom', 'rdf', 'rss', 'rss2' or 'smf'
633
 * @return array An array of arrays of feed items. Each array has keys corresponding to the appropriate tags for the specified format.
634
 */
635
function getXmlMembers($xml_format)
636
{
637
	global $scripturl, $smcFunc;
638
639
	if (!allowedTo('view_mlist'))
640
		return array();
641
642
	// Find the most recent members.
643
	$request = $smcFunc['db_query']('', '
644
		SELECT id_member, member_name, real_name, date_registered, last_login
645
		FROM {db_prefix}members
646
		ORDER BY id_member DESC
647
		LIMIT {int:limit}',
648
		array(
649
			'limit' => $_GET['limit'],
650
		)
651
	);
652
	$data = array();
653
	while ($row = $smcFunc['db_fetch_assoc']($request))
654
	{
655
		// Create a GUID for each member using the tag URI scheme
656
		$guid = 'tag:' . parse_url($scripturl, PHP_URL_HOST) . ',' . gmdate('Y-m-d', $row['date_registered']) . ':member=' . $row['id_member'];
657
658
		// Make the data look rss-ish.
659
		if ($xml_format == 'rss' || $xml_format == 'rss2')
660
			$data[] = array(
661
				'tag' => 'item',
662
				'content' => array(
663
					array(
664
						'tag' => 'title',
665
						'content' => $row['real_name'],
666
					),
667
					array(
668
						'tag' => 'link',
669
						'content' => $scripturl . '?action=profile;u=' . $row['id_member'],
670
					),
671
					array(
672
						'tag' => 'comments',
673
						'content' => $scripturl . '?action=pm;sa=send;u=' . $row['id_member'],
674
					),
675
					array(
676
						'tag' => 'pubDate',
677
						'content' => gmdate('D, d M Y H:i:s \G\M\T', $row['date_registered']),
678
					),
679
					array(
680
						'tag' => 'guid',
681
						'content' => $guid,
682
						'attributes' => array(
683
							'isPermaLink' => 'false',
684
						),
685
					),
686
				),
687
			);
688
		elseif ($xml_format == 'rdf')
689
			$data[] = array(
690
				'tag' => 'item',
691
				'attributes' => array('rdf:about' => $scripturl . '?action=profile;u=' . $row['id_member']),
692
				'content' => array(
693
					array(
694
						'tag' => 'dc:format',
695
						'content' => 'text/html',
696
					),
697
					array(
698
						'tag' => 'title',
699
						'content' => $row['real_name'],
700
					),
701
					array(
702
						'tag' => 'link',
703
						'content' => $scripturl . '?action=profile;u=' . $row['id_member'],
704
					),
705
				),
706
			);
707
		elseif ($xml_format == 'atom')
708
			$data[] = array(
709
				'tag' => 'entry',
710
				'content' => array(
711
					array(
712
						'tag' => 'title',
713
						'content' => $row['real_name'],
714
					),
715
					array(
716
						'tag' => 'link',
717
						'attributes' => array(
718
							'rel' => 'alternate',
719
							'type' => 'text/html',
720
							'href' => $scripturl . '?action=profile;u=' . $row['id_member'],
721
						),
722
					),
723
					array(
724
						'tag' => 'published',
725
						'content' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $row['date_registered']),
726
					),
727
					array(
728
						'tag' => 'updated',
729
						'content' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $row['last_login']),
730
					),
731
					array(
732
						'tag' => 'id',
733
						'content' => $guid,
734
					),
735
				),
736
			);
737
		// More logical format for the data, but harder to apply.
738
		else
739
			$data[] = array(
740
				'tag' => 'member',
741
				'content' => array(
742
					array(
743
						'tag' => 'name',
744
						'content' => $row['real_name'],
745
					),
746
					array(
747
						'tag' => 'time',
748
						'content' => $smcFunc['htmlspecialchars'](strip_tags(timeformat($row['date_registered']))),
749
					),
750
					array(
751
						'tag' => 'id',
752
						'content' => $row['id_member'],
753
					),
754
					array(
755
						'tag' => 'link',
756
						'content' => $scripturl . '?action=profile;u=' . $row['id_member'],
757
					),
758
				),
759
			);
760
	}
761
	$smcFunc['db_free_result']($request);
762
763
	return $data;
764
}
765
766
/**
767
 * Get the latest topics information from a specific board,
768
 * to display later.
769
 * The returned array will be generated to match the xml_format.
770
 * @todo does not belong here
771
 *
772
 * @param string $xml_format The XML format. Can be 'atom', 'rdf', 'rss', 'rss2' or 'smf'.
773
 * @return array An array of arrays of topic data for the feed. Each array has keys corresponding to the tags for the specified format.
774
 */
775
function getXmlNews($xml_format)
776
{
777
	global $scripturl, $modSettings, $board, $user_info;
778
	global $query_this_board, $smcFunc, $context, $txt;
779
780
	/* Find the latest posts that:
781
		- are the first post in their topic.
782
		- are on an any board OR in a specified board.
783
		- can be seen by this user.
784
		- are actually the latest posts. */
785
786
	$done = false;
787
	$loops = 0;
788
	while (!$done)
789
	{
790
		$optimize_msg = implode(' AND ', $context['optimize_msg']);
791
		$request = $smcFunc['db_query']('', '
792
			SELECT
793
				m.smileys_enabled, m.poster_time, m.id_msg, m.subject, m.body, m.modified_time,
794
				m.icon, t.id_topic, t.id_board, t.num_replies,
795
				b.name AS bname,
796
				COALESCE(mem.id_member, 0) AS id_member,
797
				COALESCE(mem.email_address, m.poster_email) AS poster_email,
798
				COALESCE(mem.real_name, m.poster_name) AS poster_name
799
			FROM {db_prefix}topics AS t
800
				INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
801
				INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
802
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
803
			WHERE ' . $query_this_board . (empty($optimize_msg) ? '' : '
804
				AND {raw:optimize_msg}') . (empty($board) ? '' : '
805
				AND t.id_board = {int:current_board}') . ($modSettings['postmod_active'] ? '
806
				AND t.approved = {int:is_approved}' : '') . '
807
			ORDER BY t.id_first_msg DESC
808
			LIMIT {int:limit}',
809
			array(
810
				'current_board' => $board,
811
				'is_approved' => 1,
812
				'limit' => $_GET['limit'],
813
				'optimize_msg' => $optimize_msg,
814
			)
815
		);
816
		// If we don't have $_GET['limit'] results, try again with an unoptimized version covering all rows.
817
		if ($loops < 2 && $smcFunc['db_num_rows']($request) < $_GET['limit'])
818
		{
819
			$smcFunc['db_free_result']($request);
820
			if (empty($_REQUEST['boards']) && empty($board))
821
				unset($context['optimize_msg']['lowest']);
822
			else
823
				$context['optimize_msg']['lowest'] = 'm.id_msg >= t.id_first_msg';
824
			$context['optimize_msg']['highest'] = 'm.id_msg <= t.id_last_msg';
825
			$loops++;
826
		}
827
		else
828
			$done = true;
829
	}
830
	$data = array();
831
	while ($row = $smcFunc['db_fetch_assoc']($request))
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $request does not seem to be defined for all execution paths leading up to this point.
Loading history...
832
	{
833
		// Limit the length of the message, if the option is set.
834
		if (!empty($modSettings['xmlnews_maxlen']) && $smcFunc['strlen'](str_replace('<br>', "\n", $row['body'])) > $modSettings['xmlnews_maxlen'])
835
			$row['body'] = strtr($smcFunc['substr'](str_replace('<br>', "\n", $row['body']), 0, $modSettings['xmlnews_maxlen'] - 3), array("\n" => '<br>')) . '...';
836
837
		$row['body'] = parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']);
838
839
		censorText($row['body']);
840
		censorText($row['subject']);
841
842
		// Do we want to include any attachments?
843
		if (!empty($modSettings['attachmentEnable']) && !empty($modSettings['xmlnews_attachments']) && allowedTo('view_attachments', $row['id_board']))
844
		{
845
			$attach_request = $smcFunc['db_query']('', '
846
				SELECT
847
					a.id_attach, a.filename, COALESCE(a.size, 0) AS filesize, a.mime_type, a.downloads, a.approved, m.id_topic AS topic
848
				FROM {db_prefix}attachments AS a
849
					LEFT JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg)
850
				WHERE a.attachment_type = {int:attachment_type}
851
					AND a.id_msg = {int:message_id}',
852
				array(
853
					'message_id' => $row['id_msg'],
854
					'attachment_type' => 0,
855
					'is_approved' => 1,
856
				)
857
			);
858
			$loaded_attachments = array();
859
			while ($attach = $smcFunc['db_fetch_assoc']($attach_request))
860
			{
861
				// Include approved attachments only
862
				if ($attach['approved'])
863
					$loaded_attachments['attachment_' . $attach['id_attach']] = $attach;
864
			}
865
			$smcFunc['db_free_result']($attach_request);
866
867
			// Sort the attachments by size to make things easier below
868
			if (!empty($loaded_attachments))
869
			{
870
				uasort($loaded_attachments, function($a, $b) {
871
					if ($a['filesize'] == $b['filesize'])
872
					        return 0;
873
					return ($a['filesize'] < $b['filesize']) ? -1 : 1;
874
				});
875
			}
876
			else
877
				$loaded_attachments = null;
878
		}
879
		else
880
			$loaded_attachments = null;
881
882
		// Create a GUID for this topic using the tag URI scheme
883
		$guid = 'tag:' . parse_url($scripturl, PHP_URL_HOST) . ',' . gmdate('Y-m-d', $row['poster_time']) . ':topic=' . $row['id_topic'];
884
885
		// Being news, this actually makes sense in rss format.
886
		if ($xml_format == 'rss' || $xml_format == 'rss2')
887
		{
888
			// Only one attachment allowed in RSS.
889
			if ($loaded_attachments !== null)
890
			{
891
				$attachment = array_pop($loaded_attachments);
0 ignored issues
show
Bug introduced by
$loaded_attachments of type null is incompatible with the type array expected by parameter $array of array_pop(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

891
				$attachment = array_pop(/** @scrutinizer ignore-type */ $loaded_attachments);
Loading history...
892
				$enclosure = array(
893
					'url' => fix_possible_url($scripturl . '?action=dlattach;topic=' . $attachment['topic'] . '.0;attach=' . $attachment['id_attach']),
894
					'length' => $attachment['filesize'],
895
					'type' => $attachment['mime_type'],
896
				);
897
			}
898
			else
899
				$enclosure = null;
900
901
			$data[] = array(
902
				'tag' => 'item',
903
				'content' => array(
904
					array(
905
						'tag' => 'title',
906
						'content' => $row['subject'],
907
					),
908
					array(
909
						'tag' => 'link',
910
						'content' => $scripturl . '?topic=' . $row['id_topic'] . '.0',
911
					),
912
					array(
913
						'tag' => 'description',
914
						'content' => $row['body'],
915
					),
916
					array(
917
						'tag' => 'author',
918
						'content' => (allowedTo('moderate_forum') || $row['id_member'] == $user_info['id']) ? $row['poster_email'] . ' (' . $row['poster_name'] . ')' : null,
919
					),
920
					array(
921
						'tag' => 'comments',
922
						'content' => $scripturl . '?action=post;topic=' . $row['id_topic'] . '.0',
923
					),
924
					array(
925
						'tag' => 'category',
926
						'content' => $row['bname'],
927
					),
928
					array(
929
						'tag' => 'pubDate',
930
						'content' => gmdate('D, d M Y H:i:s \G\M\T', $row['poster_time']),
931
					),
932
					array(
933
						'tag' => 'guid',
934
						'content' => $guid,
935
						'attributes' => array(
936
							'isPermaLink' => 'false',
937
						),
938
					),
939
					array(
940
						'tag' => 'enclosure',
941
						'attributes' => $enclosure,
942
					),
943
				),
944
			);
945
		}
946
		elseif ($xml_format == 'rdf')
947
		{
948
			$data[] = array(
949
				'tag' => 'item',
950
				'attributes' => array('rdf:about' => $scripturl . '?topic=' . $row['id_topic'] . '.0'),
951
				'content' => array(
952
					array(
953
						'tag' => 'dc:format',
954
						'content' => 'text/html',
955
					),
956
					array(
957
						'tag' => 'title',
958
						'content' => $row['subject'],
959
					),
960
					array(
961
						'tag' => 'link',
962
						'content' => $scripturl . '?topic=' . $row['id_topic'] . '.0',
963
					),
964
					array(
965
						'tag' => 'description',
966
						'content' => $row['body'],
967
					),
968
				),
969
			);
970
		}
971
		elseif ($xml_format == 'atom')
972
		{
973
			// Only one attachment allowed
974
			if (!empty($loaded_attachments))
975
			{
976
				$attachment = array_pop($loaded_attachments);
977
				$enclosure = array(
978
					'rel' => 'enclosure',
979
					'href' => fix_possible_url($scripturl . '?action=dlattach;topic=' . $attachment['topic'] . '.0;attach=' . $attachment['id_attach']),
980
					'length' => $attachment['filesize'],
981
					'type' => $attachment['mime_type'],
982
				);
983
			}
984
			else
985
				$enclosure = null;
986
987
			$data[] = array(
988
				'tag' => 'entry',
989
				'content' => array(
990
					array(
991
						'tag' => 'title',
992
						'content' => $row['subject'],
993
					),
994
					array(
995
						'tag' => 'link',
996
						'attributes' => array(
997
							'rel' => 'alternate',
998
							'type' => 'text/html',
999
							'href' => $scripturl . '?topic=' . $row['id_topic'] . '.0',
1000
						),
1001
					),
1002
					array(
1003
						'tag' => 'summary',
1004
						'attributes' => array('type' => 'html'),
1005
						'content' => $row['body'],
1006
					),
1007
					array(
1008
						'tag' => 'category',
1009
						'attributes' => array('term' => $row['bname']),
1010
					),
1011
					array(
1012
						'tag' => 'author',
1013
						'content' => array(
1014
							array(
1015
								'tag' => 'name',
1016
								'content' => $row['poster_name'],
1017
							),
1018
							array(
1019
								'tag' => 'email',
1020
								'content' => (allowedTo('moderate_forum') || $row['id_member'] == $user_info['id']) ? $row['poster_email'] : null,
1021
							),
1022
							array(
1023
								'tag' => 'uri',
1024
								'content' => !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : null,
1025
							),
1026
						)
1027
					),
1028
					array(
1029
						'tag' => 'published',
1030
						'content' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $row['poster_time']),
1031
					),
1032
					array(
1033
						'tag' => 'updated',
1034
						'content' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', empty($row['modified_time']) ? $row['poster_time'] : $row['modified_time']),
1035
					),
1036
					array(
1037
						'tag' => 'id',
1038
						'content' => $guid,
1039
					),
1040
					array(
1041
						'tag' => 'link',
1042
						'attributes' => $enclosure,
1043
					),
1044
				),
1045
			);
1046
		}
1047
		// The biggest difference here is more information.
1048
		else
1049
		{
1050
			$attachments = array();
1051
			if (!empty($loaded_attachments))
1052
			{
1053
				foreach ($loaded_attachments as $attachment)
1054
				{
1055
					$attachments[] = array(
1056
						'tag' => 'attachment',
1057
						'content' => array(
1058
							array(
1059
								'tag' => 'id',
1060
								'content' => $attachment['id_attach'],
1061
							),
1062
							array(
1063
								'tag' => 'name',
1064
								'content' => preg_replace('~&amp;#(\\d{1,7}|x[0-9a-fA-F]{1,6});~', '&#\\1;', $smcFunc['htmlspecialchars']($attachment['filename'])),
1065
							),
1066
							array(
1067
								'tag' => 'downloads',
1068
								'content' => $attachment['downloads'],
1069
							),
1070
							array(
1071
								'tag' => 'size',
1072
								'content' => ($attachment['filesize'] < 1024000) ? round($attachment['filesize'] / 1024, 2) . ' ' . $txt['kilobyte'] : round($attachment['filesize'] / 1024 / 1024, 2) . ' ' . $txt['megabyte'],
1073
							),
1074
							array(
1075
								'tag' => 'byte_size',
1076
								'content' => $attachment['filesize'],
1077
							),
1078
							array(
1079
								'tag' => 'link',
1080
								'content' => $scripturl . '?action=dlattach;topic=' . $attachment['topic'] . '.0;attach=' . $attachment['id_attach'],
1081
							),
1082
						)
1083
					);
1084
				}
1085
			}
1086
			else
1087
				$attachments = null;
1088
1089
			$data[] = array(
1090
				'tag' => 'article',
1091
				'content' => array(
1092
					array(
1093
						'tag' => 'time',
1094
						'content' => $smcFunc['htmlspecialchars'](strip_tags(timeformat($row['poster_time']))),
1095
					),
1096
					array(
1097
						'tag' => 'id',
1098
						'content' => $row['id_topic'],
1099
					),
1100
					array(
1101
						'tag' => 'subject',
1102
						'content' => $row['subject'],
1103
					),
1104
					array(
1105
						'tag' => 'body',
1106
						'content' => $row['body'],
1107
					),
1108
					array(
1109
						'tag' => 'poster',
1110
						'content' => array(
1111
							array(
1112
								'tag' => 'name',
1113
								'content' => $row['poster_name'],
1114
							),
1115
							array(
1116
								'tag' => 'id',
1117
								'content' => $row['id_member'],
1118
							),
1119
							array(
1120
								'tag' => 'link',
1121
								'content' => !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : '',
1122
							),
1123
						)
1124
					),
1125
					array(
1126
						'tag' => 'topic',
1127
						'content' => $row['id_topic'],
1128
					),
1129
					array(
1130
						'tag' => 'board',
1131
						'content' => array(
1132
							array(
1133
								'tag' => 'name',
1134
								'content' => $row['bname'],
1135
							),
1136
							array(
1137
								'tag' => 'id',
1138
								'content' => $row['id_board'],
1139
							),
1140
							array(
1141
								'tag' => 'link',
1142
								'content' => $scripturl . '?board=' . $row['id_board'] . '.0',
1143
							),
1144
						),
1145
					),
1146
					array(
1147
						'tag' => 'link',
1148
						'content' => $scripturl . '?topic=' . $row['id_topic'] . '.0',
1149
					),
1150
					array(
1151
						'tag' => 'attachments',
1152
						'content' => $attachments,
1153
					),
1154
				),
1155
			);
1156
		}
1157
	}
1158
	$smcFunc['db_free_result']($request);
1159
1160
	return $data;
1161
}
1162
1163
/**
1164
 * Get the recent topics to display.
1165
 * The returned array will be generated to match the xml_format.
1166
 * @todo does not belong here.
1167
 *
1168
 * @param string $xml_format The XML format. Can be 'atom', 'rdf', 'rss', 'rss2' or 'smf'
1169
 * @return array An array of arrays containing data for the feed. Each array has keys corresponding to the appropriate tags for the specified format.
1170
 */
1171
function getXmlRecent($xml_format)
1172
{
1173
	global $scripturl, $modSettings, $board, $txt;
1174
	global $query_this_board, $smcFunc, $context, $user_info, $sourcedir;
1175
1176
	require_once($sourcedir . '/Subs-Attachments.php');
1177
1178
	$done = false;
1179
	$loops = 0;
1180
	while (!$done)
1181
	{
1182
		$optimize_msg = implode(' AND ', $context['optimize_msg']);
1183
		$request = $smcFunc['db_query']('', '
1184
			SELECT m.id_msg
1185
			FROM {db_prefix}messages AS m
1186
				INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
1187
				INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
1188
			WHERE ' . $query_this_board . (empty($optimize_msg) ? '' : '
1189
				AND {raw:optimize_msg}') . (empty($board) ? '' : '
1190
				AND m.id_board = {int:current_board}') . ($modSettings['postmod_active'] ? '
1191
				AND m.approved = {int:is_approved}' : '') . '
1192
			ORDER BY m.id_msg DESC
1193
			LIMIT {int:limit}',
1194
			array(
1195
				'limit' => $_GET['limit'],
1196
				'current_board' => $board,
1197
				'is_approved' => 1,
1198
				'optimize_msg' => $optimize_msg,
1199
			)
1200
		);
1201
		// If we don't have $_GET['limit'] results, try again with an unoptimized version covering all rows.
1202
		if ($loops < 2 && $smcFunc['db_num_rows']($request) < $_GET['limit'])
1203
		{
1204
			$smcFunc['db_free_result']($request);
1205
			if (empty($_REQUEST['boards']) && empty($board))
1206
				unset($context['optimize_msg']['lowest']);
1207
			else
1208
				$context['optimize_msg']['lowest'] = $loops ? 'm.id_msg >= t.id_first_msg' : 'm.id_msg >= (t.id_last_msg - t.id_first_msg) / 2';
1209
			$loops++;
1210
		}
1211
		else
1212
			$done = true;
1213
	}
1214
	$messages = array();
1215
	while ($row = $smcFunc['db_fetch_assoc']($request))
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $request does not seem to be defined for all execution paths leading up to this point.
Loading history...
1216
		$messages[] = $row['id_msg'];
1217
	$smcFunc['db_free_result']($request);
1218
1219
	if (empty($messages))
1220
		return array();
1221
1222
	// Find the most recent posts this user can see.
1223
	$request = $smcFunc['db_query']('', '
1224
		SELECT
1225
			m.smileys_enabled, m.poster_time, m.id_msg, m.subject, m.body, m.id_topic, t.id_board,
1226
			b.name AS bname, t.num_replies, m.id_member, m.icon, mf.id_member AS id_first_member,
1227
			COALESCE(mem.real_name, m.poster_name) AS poster_name, mf.subject AS first_subject,
1228
			COALESCE(memf.real_name, mf.poster_name) AS first_poster_name,
1229
			COALESCE(mem.email_address, m.poster_email) AS poster_email, m.modified_time
1230
		FROM {db_prefix}messages AS m
1231
			INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
1232
			INNER JOIN {db_prefix}messages AS mf ON (mf.id_msg = t.id_first_msg)
1233
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
1234
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
1235
			LEFT JOIN {db_prefix}members AS memf ON (memf.id_member = mf.id_member)
1236
		WHERE m.id_msg IN ({array_int:message_list})
1237
			' . (empty($board) ? '' : 'AND t.id_board = {int:current_board}') . '
1238
		ORDER BY m.id_msg DESC
1239
		LIMIT {int:limit}',
1240
		array(
1241
			'limit' => $_GET['limit'],
1242
			'current_board' => $board,
1243
			'message_list' => $messages,
1244
		)
1245
	);
1246
	$data = array();
1247
	while ($row = $smcFunc['db_fetch_assoc']($request))
1248
	{
1249
		// Limit the length of the message, if the option is set.
1250
		if (!empty($modSettings['xmlnews_maxlen']) && $smcFunc['strlen'](str_replace('<br>', "\n", $row['body'])) > $modSettings['xmlnews_maxlen'])
1251
			$row['body'] = strtr($smcFunc['substr'](str_replace('<br>', "\n", $row['body']), 0, $modSettings['xmlnews_maxlen'] - 3), array("\n" => '<br>')) . '...';
1252
1253
		$row['body'] = parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']);
1254
1255
		censorText($row['body']);
1256
		censorText($row['subject']);
1257
1258
		// Do we want to include any attachments?
1259
		if (!empty($modSettings['attachmentEnable']) && !empty($modSettings['xmlnews_attachments']) && allowedTo('view_attachments', $row['id_board']))
1260
		{
1261
			$attach_request = $smcFunc['db_query']('', '
1262
				SELECT
1263
					a.id_attach, a.filename, COALESCE(a.size, 0) AS filesize, a.mime_type, a.downloads, a.approved, m.id_topic AS topic
1264
				FROM {db_prefix}attachments AS a
1265
					LEFT JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg)
1266
				WHERE a.attachment_type = {int:attachment_type}
1267
					AND a.id_msg = {int:message_id}',
1268
				array(
1269
					'message_id' => $row['id_msg'],
1270
					'attachment_type' => 0,
1271
					'is_approved' => 1,
1272
				)
1273
			);
1274
			$loaded_attachments = array();
1275
			while ($attach = $smcFunc['db_fetch_assoc']($attach_request))
1276
			{
1277
				// Include approved attachments only
1278
				if ($attach['approved'])
1279
					$loaded_attachments['attachment_' . $attach['id_attach']] = $attach;
1280
			}
1281
			$smcFunc['db_free_result']($attach_request);
1282
1283
			// Sort the attachments by size to make things easier below
1284
			if (!empty($loaded_attachments))
1285
			{
1286
				uasort($loaded_attachments, function($a, $b) {
1287
					if ($a['filesize'] == $b['filesize'])
1288
					        return 0;
1289
					return ($a['filesize'] < $b['filesize']) ? -1 : 1;
1290
				});
1291
			}
1292
			else
1293
				$loaded_attachments = null;
1294
		}
1295
		else
1296
			$loaded_attachments = null;
1297
1298
		// Create a GUID for this post using the tag URI scheme
1299
		$guid = 'tag:' . parse_url($scripturl, PHP_URL_HOST) . ',' . gmdate('Y-m-d', $row['poster_time']) . ':msg=' . $row['id_msg'];
1300
1301
		// Doesn't work as well as news, but it kinda does..
1302
		if ($xml_format == 'rss' || $xml_format == 'rss2')
1303
		{
1304
			// Only one attachment allowed in RSS.
1305
			if ($loaded_attachments !== null)
1306
			{
1307
				$attachment = array_pop($loaded_attachments);
0 ignored issues
show
Bug introduced by
$loaded_attachments of type null is incompatible with the type array expected by parameter $array of array_pop(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1307
				$attachment = array_pop(/** @scrutinizer ignore-type */ $loaded_attachments);
Loading history...
1308
				$enclosure = array(
1309
					'url' => fix_possible_url($scripturl . '?action=dlattach;topic=' . $attachment['topic'] . '.0;attach=' . $attachment['id_attach']),
1310
					'length' => $attachment['filesize'],
1311
					'type' => $attachment['mime_type'],
1312
				);
1313
			}
1314
			else
1315
				$enclosure = null;
1316
1317
			$data[] = array(
1318
				'tag' => 'item',
1319
				'content' => array(
1320
					array(
1321
						'tag' => 'title',
1322
						'content' => $row['subject'],
1323
					),
1324
					array(
1325
						'tag' => 'link',
1326
						'content' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'],
1327
					),
1328
					array(
1329
						'tag' => 'description',
1330
						'content' => $row['body'],
1331
					),
1332
					array(
1333
						'tag' => 'author',
1334
						'content' => (allowedTo('moderate_forum') || (!empty($row['id_member']) && $row['id_member'] == $user_info['id'])) ? $row['poster_email'] : null,
1335
					),
1336
					array(
1337
						'tag' => 'category',
1338
						'content' => $row['bname'],
1339
					),
1340
					array(
1341
						'tag' => 'comments',
1342
						'content' => $scripturl . '?action=post;topic=' . $row['id_topic'] . '.0',
1343
					),
1344
					array(
1345
						'tag' => 'pubDate',
1346
						'content' => gmdate('D, d M Y H:i:s \G\M\T', $row['poster_time']),
1347
					),
1348
					array(
1349
						'tag' => 'guid',
1350
						'content' => $guid,
1351
						'attributes' => array(
1352
							'isPermaLink' => 'false',
1353
						),
1354
					),
1355
					array(
1356
						'tag' => 'enclosure',
1357
						'attributes' => $enclosure,
1358
					),
1359
				),
1360
			);
1361
		}
1362
		elseif ($xml_format == 'rdf')
1363
		{
1364
			$data[] = array(
1365
				'tag' => 'item',
1366
				'attributes' => array('rdf:about' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg']),
1367
				'content' => array(
1368
					array(
1369
						'tag' => 'dc:format',
1370
						'content' => 'text/html',
1371
					),
1372
					array(
1373
						'tag' => 'title',
1374
						'content' => $row['subject'],
1375
					),
1376
					array(
1377
						'tag' => 'link',
1378
						'content' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'],
1379
					),
1380
					array(
1381
						'tag' => 'description',
1382
						'content' => $row['body'],
1383
					),
1384
				),
1385
			);
1386
		}
1387
		elseif ($xml_format == 'atom')
1388
		{
1389
			// Only one attachment allowed
1390
			if (!empty($loaded_attachments))
1391
			{
1392
				$attachment = array_pop($loaded_attachments);
1393
				$enclosure = array(
1394
					'rel' => 'enclosure',
1395
					'href' => fix_possible_url($scripturl . '?action=dlattach;topic=' . $attachment['topic'] . '.0;attach=' . $attachment['id_attach']),
1396
					'length' => $attachment['filesize'],
1397
					'type' => $attachment['mime_type'],
1398
				);
1399
			}
1400
			else
1401
				$enclosure = null;
1402
1403
			$data[] = array(
1404
				'tag' => 'entry',
1405
				'content' => array(
1406
					array(
1407
						'tag' => 'title',
1408
						'content' => $row['subject'],
1409
					),
1410
					array(
1411
						'tag' => 'link',
1412
						'attributes' => array(
1413
							'rel' => 'alternate',
1414
							'type' => 'text/html',
1415
							'href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'],
1416
						),
1417
					),
1418
					array(
1419
						'tag' => 'summary',
1420
						'attributes' => array('type' => 'html'),
1421
						'content' => $row['body'],
1422
					),
1423
					array(
1424
						'tag' => 'category',
1425
						'attributes' => array('term' => $row['bname']),
1426
					),
1427
					array(
1428
						'tag' => 'author',
1429
						'content' => array(
1430
							array(
1431
								'tag' => 'name',
1432
								'content' => $row['poster_name'],
1433
							),
1434
							array(
1435
								'tag' => 'email',
1436
								'content' => (allowedTo('moderate_forum') || (!empty($row['id_member']) && $row['id_member'] == $user_info['id'])) ? $row['poster_email'] : null,
1437
							),
1438
							array(
1439
								'tag' => 'uri',
1440
								'content' => !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : null,
1441
							),
1442
						),
1443
					),
1444
					array(
1445
						'tag' => 'published',
1446
						'content' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $row['poster_time']),
1447
					),
1448
					array(
1449
						'tag' => 'updated',
1450
						'content' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', empty($row['modified_time']) ? $row['poster_time'] : $row['modified_time']),
1451
					),
1452
					array(
1453
						'tag' => 'id',
1454
						'content' => $guid,
1455
					),
1456
					array(
1457
						'tag' => 'link',
1458
						'attributes' => $enclosure,
1459
					),
1460
				),
1461
			);
1462
		}
1463
		// A lot of information here.  Should be enough to please the rss-ers.
1464
		else
1465
		{
1466
			$attachments = array();
1467
			if (!empty($loaded_attachments))
1468
			{
1469
				foreach ($loaded_attachments as $attachment)
1470
				{
1471
					$attachments[] = array(
1472
						'tag' => 'attachment',
1473
						'content' => array(
1474
							array(
1475
								'tag' => 'id',
1476
								'content' => $attachment['id_attach'],
1477
							),
1478
							array(
1479
								'tag' => 'name',
1480
								'content' => preg_replace('~&amp;#(\\d{1,7}|x[0-9a-fA-F]{1,6});~', '&#\\1;', $smcFunc['htmlspecialchars']($attachment['filename'])),
1481
							),
1482
							array(
1483
								'tag' => 'downloads',
1484
								'content' => $attachment['downloads'],
1485
							),
1486
							array(
1487
								'tag' => 'size',
1488
								'content' => ($attachment['filesize'] < 1024000) ? round($attachment['filesize'] / 1024, 2) . ' ' . $txt['kilobyte'] : round($attachment['filesize'] / 1024 / 1024, 2) . ' ' . $txt['megabyte'],
1489
							),
1490
							array(
1491
								'tag' => 'byte_size',
1492
								'content' => $attachment['filesize'],
1493
							),
1494
							array(
1495
								'tag' => 'link',
1496
								'content' => $scripturl . '?action=dlattach;topic=' . $attachment['topic'] . '.0;attach=' . $attachment['id_attach'],
1497
							),
1498
						)
1499
					);
1500
				}
1501
			}
1502
			else
1503
				$attachments = null;
1504
1505
			$data[] = array(
1506
				'tag' => 'recent-post',
1507
				'content' => array(
1508
					array(
1509
						'tag' => 'time',
1510
						'content' => $smcFunc['htmlspecialchars'](strip_tags(timeformat($row['poster_time']))),
1511
					),
1512
					array(
1513
						'tag' => 'id',
1514
						'content' => $row['id_msg'],
1515
					),
1516
					array(
1517
						'tag' => 'subject',
1518
						'content' => $row['subject'],
1519
					),
1520
					array(
1521
						'tag' => 'body',
1522
						'content' => $row['body'],
1523
					),
1524
					array(
1525
						'tag' => 'starter',
1526
						'content' => array(
1527
							array(
1528
								'tag' => 'name',
1529
								'content' => $row['first_poster_name'],
1530
							),
1531
							array(
1532
								'tag' => 'id',
1533
								'content' => $row['id_first_member'],
1534
							),
1535
							array(
1536
								'tag' => 'link',
1537
								'content' => !empty($row['id_first_member']) ? $scripturl . '?action=profile;u=' . $row['id_first_member'] : '',
1538
							),
1539
						),
1540
					),
1541
					array(
1542
						'tag' => 'poster',
1543
						'content' => array(
1544
							array(
1545
								'tag' => 'name',
1546
								'content' => $row['poster_name'],
1547
							),
1548
							array(
1549
								'tag' => 'id',
1550
								'content' => $row['id_member'],
1551
							),
1552
							array(
1553
								'tag' => 'link',
1554
								'content' => !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : '',
1555
							),
1556
						),
1557
					),
1558
					array(
1559
						'tag' => 'topic',
1560
						'content' => array(
1561
							array(
1562
								'tag' => 'subject',
1563
								'content' => $row['first_subject'],
1564
							),
1565
							array(
1566
								'tag' => 'id',
1567
								'content' => $row['id_topic'],
1568
							),
1569
							array(
1570
								'tag' => 'link',
1571
								'content' => $scripturl . '?topic=' . $row['id_topic'] . '.new#new',
1572
							),
1573
						),
1574
					),
1575
					array(
1576
						'tag' => 'board',
1577
						'content' => array(
1578
							array(
1579
								'tag' => 'name',
1580
								'content' => $row['bname'],
1581
							),
1582
							array(
1583
								'tag' => 'id',
1584
								'content' => $row['id_board'],
1585
							),
1586
							array(
1587
								'tag' => 'link',
1588
								'content' => $scripturl . '?board=' . $row['id_board'] . '.0',
1589
							),
1590
						),
1591
					),
1592
					array(
1593
						'tag' => 'link',
1594
						'content' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'],
1595
					),
1596
					array(
1597
						'tag' => 'attachments',
1598
						'content' => $attachments,
1599
					),
1600
				),
1601
			);
1602
		}
1603
	}
1604
	$smcFunc['db_free_result']($request);
1605
1606
	return $data;
1607
}
1608
1609
/**
1610
 * Get the profile information for member into an array,
1611
 * which will be generated to match the xml_format.
1612
 * @todo refactor.
1613
 *
1614
 * @param string $xml_format The XML format. Can be 'atom', 'rdf', 'rss', 'rss2' or 'smf'
1615
 * @return array An array profile data
1616
 */
1617
function getXmlProfile($xml_format)
1618
{
1619
	global $scripturl, $memberContext, $user_profile, $user_info;
1620
1621
	// You must input a valid user....
1622
	if (empty($_GET['u']) || !loadMemberData((int) $_GET['u']))
1623
		return array();
1624
1625
	// Make sure the id is a number and not "I like trying to hack the database".
1626
	$_GET['u'] = (int) $_GET['u'];
1627
	// Load the member's contextual information!
1628
	if (!loadMemberContext($_GET['u']) || !allowedTo('profile_view'))
1629
		return array();
1630
1631
	// Okay, I admit it, I'm lazy.  Stupid $_GET['u'] is long and hard to type.
1632
	$profile = &$memberContext[$_GET['u']];
1633
1634
	// Create a GUID for this member using the tag URI scheme
1635
	$guid = 'tag:' . parse_url($scripturl, PHP_URL_HOST) . ',' . gmdate('Y-m-d', $user_profile[$profile['id']]['date_registered']) . ':member=' . $profile['id'];
1636
1637
	if ($xml_format == 'rss' || $xml_format == 'rss2')
1638
	{
1639
		$data[] = array(
0 ignored issues
show
Comprehensibility Best Practice introduced by
$data was never initialized. Although not strictly required by PHP, it is generally a good practice to add $data = array(); before regardless.
Loading history...
1640
			'tag' => 'item',
1641
			'content' => array(
1642
				array(
1643
					'tag' => 'title',
1644
					'content' => $profile['name'],
1645
				),
1646
				array(
1647
					'tag' => 'link',
1648
					'content' => $scripturl . '?action=profile;u=' . $profile['id'],
1649
				),
1650
				array(
1651
					'tag' => 'description',
1652
					'content' => isset($profile['group']) ? $profile['group'] : $profile['post_group'],
1653
				),
1654
				array(
1655
					'tag' => 'comments',
1656
					'content' => $scripturl . '?action=pm;sa=send;u=' . $profile['id'],
1657
				),
1658
				array(
1659
					'tag' => 'pubDate',
1660
					'content' => gmdate('D, d M Y H:i:s \G\M\T', $user_profile[$profile['id']]['date_registered']),
1661
				),
1662
				array(
1663
					'tag' => 'guid',
1664
					'content' => $guid,
1665
					'attributes' => array(
1666
						'isPermaLink' => 'false',
1667
					),
1668
				),
1669
			)
1670
		);
1671
	}
1672
	elseif ($xml_format == 'rdf')
1673
	{
1674
		$data[] = array(
1675
			'tag' => 'item',
1676
			'attributes' => array('rdf:about' => $scripturl . '?action=profile;u=' . $profile['id']),
1677
			'content' => array(
1678
				array(
1679
					'tag' => 'dc:format',
1680
					'content' => 'text/html',
1681
				),
1682
				array(
1683
					'tag' => 'title',
1684
					'content' => $profile['name'],
1685
				),
1686
				array(
1687
					'tag' => 'link',
1688
					'content' => $scripturl . '?action=profile;u=' . $profile['id'],
1689
				),
1690
				array(
1691
					'tag' => 'description',
1692
					'content' => isset($profile['group']) ? $profile['group'] : $profile['post_group'],
1693
				),
1694
			)
1695
		);
1696
	}
1697
	elseif ($xml_format == 'atom')
1698
	{
1699
		$data[] = array(
1700
			'tag' => 'entry',
1701
			'content' => array(
1702
				array(
1703
					'tag' => 'title',
1704
					'content' => $profile['name'],
1705
				),
1706
				array(
1707
					'tag' => 'link',
1708
					'attributes' => array(
1709
						'rel' => 'alternate',
1710
						'type' => 'text/html',
1711
						'href' => $scripturl . '?action=profile;u=' . $profile['id'],
1712
					),
1713
				),
1714
				array(
1715
					'tag' => 'summary',
1716
					'attributes' => array('type' => 'html'),
1717
					'content' => isset($profile['group']) ? $profile['group'] : $profile['post_group'],
1718
				),
1719
				array(
1720
					'tag' => 'author',
1721
					'content' => array(
1722
						array(
1723
							'tag' => 'name',
1724
							'content' => $profile['name'],
1725
						),
1726
						array(
1727
							'tag' => 'email',
1728
							'content' => $profile['show_email'] ? $profile['email'] : null,
1729
						),
1730
						array(
1731
							'tag' => 'uri',
1732
							'content' => !empty($profile['website']['url']) ? $profile['website']['url'] : null,
1733
						),
1734
					),
1735
				),
1736
				array(
1737
					'tag' => 'published',
1738
					'content' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $user_profile[$profile['id']]['date_registered']),
1739
				),
1740
				array(
1741
					'tag' => 'updated',
1742
					'content' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $user_profile[$profile['id']]['last_login']),
1743
				),
1744
				array(
1745
					'tag' => 'id',
1746
					'content' => $guid,
1747
				),
1748
			)
1749
		);
1750
	}
1751
	else
1752
	{
1753
		$data = array(
1754
			array(
1755
				'tag' => 'username',
1756
				'content' => $user_info['is_admin'] || $user_info['id'] == $profile['id'] ? $profile['username'] : null,
1757
			),
1758
			array(
1759
				'tag' => 'name',
1760
				'content' => $profile['name'],
1761
			),
1762
			array(
1763
				'tag' => 'link',
1764
				'content' => $scripturl . '?action=profile;u=' . $profile['id'],
1765
			),
1766
			array(
1767
				'tag' => 'posts',
1768
				'content' => $profile['posts'],
1769
			),
1770
			array(
1771
				'tag' => 'post-group',
1772
				'content' => $profile['post_group'],
1773
			),
1774
			array(
1775
				'tag' => 'language',
1776
				'content' => $profile['language'],
1777
			),
1778
			array(
1779
				'tag' => 'last-login',
1780
				'content' => gmdate('D, d M Y H:i:s \G\M\T', $user_profile[$profile['id']]['last_login']),
1781
			),
1782
			array(
1783
				'tag' => 'registered',
1784
				'content' => gmdate('D, d M Y H:i:s \G\M\T', $user_profile[$profile['id']]['date_registered']),
1785
			),
1786
			array(
1787
				'tag' => 'gender',
1788
				'content' => !empty($profile['gender']['name']) ? $profile['gender']['name'] : null,
1789
			),
1790
			array(
1791
				'tag' => 'avatar',
1792
				'content' => !empty($profile['avatar']['url']) ? $profile['avatar']['url'] : null,
1793
			),
1794
			array(
1795
				'tag' => 'online',
1796
				'content' => !empty($profile['online']['is_online']) ? '' : null,
1797
			),
1798
			array(
1799
				'tag' => 'signature',
1800
				'content' => !empty($profile['signature']) ? $profile['signature'] : null,
1801
			),
1802
			array(
1803
				'tag' => 'blurb',
1804
				'content' => !empty($profile['blurb']) ? $profile['blurb'] : null,
1805
			),
1806
			array(
1807
				'tag' => 'title',
1808
				'content' => !empty($profile['title']) ? $profile['title'] : null,
1809
			),
1810
			array(
1811
				'tag' => 'position',
1812
				'content' => !empty($profile['group']) ? $profile['group'] : null,
1813
			),
1814
			array(
1815
				'tag' => 'email',
1816
				'content' => !empty($profile['show_email']) ? $profile['show_email'] : null,
1817
			),
1818
			array(
1819
				'tag' => 'website',
1820
				'content' => empty($profile['website']['url']) ? null : array(
1821
					array(
1822
						'tag' => 'title',
1823
						'content' => !empty($profile['website']['title']) ? $profile['website']['title'] : null,
1824
					),
1825
					array(
1826
						'tag' => 'link',
1827
						'content' => $profile['website']['url'],
1828
					),
1829
				),
1830
			),
1831
		);
1832
1833
		if (!empty($profile['birth_date']) && substr($profile['birth_date'], 0, 4) != '0000')
1834
		{
1835
			list ($birth_year, $birth_month, $birth_day) = sscanf($profile['birth_date'], '%d-%d-%d');
1836
			$datearray = getdate(forum_time());
1837
			$age = $datearray['year'] - $birth_year - (($datearray['mon'] > $birth_month || ($datearray['mon'] == $birth_month && $datearray['mday'] >= $birth_day)) ? 0 : 1);
1838
1839
			$data[] = array(
1840
				'tag' => 'age',
1841
				'content' => $age,
1842
			);
1843
		}
1844
	}
1845
1846
	// Save some memory.
1847
	unset($profile, $memberContext[$_GET['u']]);
1848
1849
	return $data;
1850
}
1851
1852
?>