cdata_parse()   D
last analyzed

Complexity

Conditions 18
Paths 26

Size

Total Lines 67
Code Lines 43

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 18
eloc 43
nop 3
dl 0
loc 67
rs 4.8666
c 0
b 0
f 0
nc 26

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 2019 Simple Machines and individual contributors
11
 * @license http://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);
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($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);
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');
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
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...
473
		return $data;
474
475
	$cdata = '<![CDATA[';
476
477
	for ($pos = 0, $n = $smcFunc['strlen']($data); $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) . '<![CDATA[';
508
			$pos = $pos2 + 1;
509
		}
510
		elseif ($smcFunc['substr']($data, $pos, 1) == ']')
511
		{
512
			$cdata .= ']]>&#093;<![CDATA[';
513
			$pos++;
514
		}
515
		elseif ($smcFunc['substr']($data, $pos, 1) == '&')
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))
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...
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);
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

897
				$attachment = array_pop(/** @scrutinizer ignore-type */ $loaded_attachments);
Loading history...
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('~&amp;#(\\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))
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...
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);
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

1316
				$attachment = array_pop(/** @scrutinizer ignore-type */ $loaded_attachments);
Loading history...
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('~&amp;#(\\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(
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...
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
?>