Completed
Push — release-2.1 ( 4c82a0...64d581 )
by Rick
09:29
created

News.php ➔ ShowXmlFeed()   F

Complexity

Conditions 69
Paths > 20000

Size

Total Lines 294
Code Lines 155

Duplication

Lines 25
Ratio 8.5 %

Importance

Changes 0
Metric Value
cc 69
eloc 155
nc 429496.7295
nop 0
dl 25
loc 294
rs 2
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 2016 Simple Machines and individual contributors
11
 * @license http://www.simplemachines.org/about/smf/license.php BSD
12
 *
13
 * @version 2.1 Beta 3
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;
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
	// Handle the cases where a board, boards, or category is asked for.
49
	$query_this_board = 1;
50
	$context['optimize_msg'] = array(
51
		'highest' => 'm.id_msg <= b.id_last_msg',
52
	);
53
	if (!empty($_REQUEST['c']) && empty($board))
54
	{
55
		$_REQUEST['c'] = explode(',', $_REQUEST['c']);
56
		foreach ($_REQUEST['c'] as $i => $c)
57
			$_REQUEST['c'][$i] = (int) $c;
58
59 View Code Duplication
		if (count($_REQUEST['c']) == 1)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
60
		{
61
			$request = $smcFunc['db_query']('', '
62
				SELECT name
63
				FROM {db_prefix}categories
64
				WHERE id_cat = {int:current_category}',
65
				array(
66
					'current_category' => (int) $_REQUEST['c'][0],
67
				)
68
			);
69
			list ($feed_title) = $smcFunc['db_fetch_row']($request);
70
			$smcFunc['db_free_result']($request);
71
72
			$feed_title = ' - ' . strip_tags($feed_title);
73
		}
74
75
		$request = $smcFunc['db_query']('', '
76
			SELECT b.id_board, b.num_posts
77
			FROM {db_prefix}boards AS b
78
			WHERE b.id_cat IN ({array_int:current_category_list})
79
				AND {query_see_board}',
80
			array(
81
				'current_category_list' => $_REQUEST['c'],
82
			)
83
		);
84
		$total_cat_posts = 0;
85
		$boards = array();
86
		while ($row = $smcFunc['db_fetch_assoc']($request))
87
		{
88
			$boards[] = $row['id_board'];
89
			$total_cat_posts += $row['num_posts'];
90
		}
91
		$smcFunc['db_free_result']($request);
92
93
		if (!empty($boards))
94
			$query_this_board = 'b.id_board IN (' . implode(', ', $boards) . ')';
95
96
		// Try to limit the number of messages we look through.
97 View Code Duplication
		if ($total_cat_posts > 100 && $total_cat_posts > $modSettings['totalMessages'] / 15)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
98
			$context['optimize_msg']['lowest'] = 'm.id_msg >= ' . max(0, $modSettings['maxMsgID'] - 400 - $_GET['limit'] * 5);
99
	}
100
	elseif (!empty($_REQUEST['boards']))
101
	{
102
		$_REQUEST['boards'] = explode(',', $_REQUEST['boards']);
103
		foreach ($_REQUEST['boards'] as $i => $b)
104
			$_REQUEST['boards'][$i] = (int) $b;
105
106
		$request = $smcFunc['db_query']('', '
107
			SELECT b.id_board, b.num_posts, b.name
108
			FROM {db_prefix}boards AS b
109
			WHERE b.id_board IN ({array_int:board_list})
110
				AND {query_see_board}
111
			LIMIT {int:limit}',
112
			array(
113
				'board_list' => $_REQUEST['boards'],
114
				'limit' => count($_REQUEST['boards']),
115
			)
116
		);
117
118
		// Either the board specified doesn't exist or you have no access.
119
		$num_boards = $smcFunc['db_num_rows']($request);
120
		if ($num_boards == 0)
121
			fatal_lang_error('no_board');
122
123
		$total_posts = 0;
124
		$boards = array();
125
		while ($row = $smcFunc['db_fetch_assoc']($request))
126
		{
127
			if ($num_boards == 1)
128
				$feed_title = ' - ' . strip_tags($row['name']);
129
130
			$boards[] = $row['id_board'];
131
			$total_posts += $row['num_posts'];
132
		}
133
		$smcFunc['db_free_result']($request);
134
135
		if (!empty($boards))
136
			$query_this_board = 'b.id_board IN (' . implode(', ', $boards) . ')';
137
138
		// The more boards, the more we're going to look through...
139 View Code Duplication
		if ($total_posts > 100 && $total_posts > $modSettings['totalMessages'] / 12)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
140
			$context['optimize_msg']['lowest'] = 'm.id_msg >= ' . max(0, $modSettings['maxMsgID'] - 500 - $_GET['limit'] * 5);
141
	}
142
	elseif (!empty($board))
143
	{
144
		$request = $smcFunc['db_query']('', '
145
			SELECT num_posts
146
			FROM {db_prefix}boards
147
			WHERE id_board = {int:current_board}
148
			LIMIT 1',
149
			array(
150
				'current_board' => $board,
151
			)
152
		);
153
		list ($total_posts) = $smcFunc['db_fetch_row']($request);
154
		$smcFunc['db_free_result']($request);
155
156
		$feed_title = ' - ' . strip_tags($board_info['name']);
157
158
		$query_this_board = 'b.id_board = ' . $board;
159
160
		// Try to look through just a few messages, if at all possible.
161 View Code Duplication
		if ($total_posts > 80 && $total_posts > $modSettings['totalMessages'] / 10)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
162
			$context['optimize_msg']['lowest'] = 'm.id_msg >= ' . max(0, $modSettings['maxMsgID'] - 600 - $_GET['limit'] * 5);
163
	}
164
	else
165
	{
166
		$query_this_board = '{query_see_board}' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? '
167
			AND b.id_board != ' . $modSettings['recycle_board'] : '');
168
		$context['optimize_msg']['lowest'] = 'm.id_msg >= ' . max(0, $modSettings['maxMsgID'] - 100 - $_GET['limit'] * 5);
169
	}
170
171
	// Show in rss or proprietary format?
172
	$xml_format = isset($_GET['type']) && in_array($_GET['type'], array('smf', 'rss', 'rss2', 'atom', 'rdf')) ? $_GET['type'] : 'smf';
173
174
	// @todo Birthdays?
175
176
	// List all the different types of data they can pull.
177
	$subActions = array(
178
		'recent' => array('getXmlRecent', 'recent-post'),
179
		'news' => array('getXmlNews', 'article'),
180
		'members' => array('getXmlMembers', 'member'),
181
		'profile' => array('getXmlProfile', null),
182
	);
183
184
	// Easy adding of sub actions
185
 	call_integration_hook('integrate_xmlfeeds', array(&$subActions));
186
187
	if (empty($_GET['sa']) || !isset($subActions[$_GET['sa']]))
188
		$_GET['sa'] = 'recent';
189
190
	// We only want some information, not all of it.
191
	$cachekey = array($xml_format, $_GET['action'], $_GET['limit'], $_GET['sa']);
192
	foreach (array('board', 'boards', 'c') as $var)
193
		if (isset($_REQUEST[$var]))
194
			$cachekey[] = $_REQUEST[$var];
195
	$cachekey = md5(json_encode($cachekey) . (!empty($query_this_board) ? $query_this_board : ''));
196
	$cache_t = microtime();
197
198
	// Get the associative array representing the xml.
199
	if (!empty($modSettings['cache_enable']) && (!$user_info['is_guest'] || $modSettings['cache_enable'] >= 3))
200
		$xml = cache_get_data('xmlfeed-' . $xml_format . ':' . ($user_info['is_guest'] ? '' : $user_info['id'] . '-') . $cachekey, 240);
201
	if (empty($xml))
202
	{
203
		$call = call_helper($subActions[$_GET['sa']][0], true);
204
205
		if (!empty($call))
206
			$xml = call_user_func($call, $xml_format);
207
208
		if (!empty($modSettings['cache_enable']) && (($user_info['is_guest'] && $modSettings['cache_enable'] >= 3)
209
		|| (!$user_info['is_guest'] && (array_sum(explode(' ', microtime())) - array_sum(explode(' ', $cache_t)) > 0.2))))
210
			cache_put_data('xmlfeed-' . $xml_format . ':' . ($user_info['is_guest'] ? '' : $user_info['id'] . '-') . $cachekey, $xml, 240);
0 ignored issues
show
Bug introduced by
The variable $xml does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
211
	}
212
213
	$feed_title = $smcFunc['htmlspecialchars'](strip_tags($context['forum_name'])) . (isset($feed_title) ? $feed_title : '');
214
215
	// If mods want to do somthing with this feed, let them do that now.
216
	// Provide the feed's data, title, format, and content type.
217
	call_integration_hook('integrate_xml_data', array(&$xml, &$feed_title, $xml_format, $_GET['sa']));
218
219
	// This is an xml file....
220
	ob_end_clean();
221
	if (!empty($modSettings['enableCompressedOutput']))
222
		@ob_start('ob_gzhandler');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
223
	else
224
		ob_start();
225
226
	if ($xml_format == 'smf' || isset($_REQUEST['debug']))
227
		header('Content-Type: text/xml; charset=' . (empty($context['character_set']) ? 'ISO-8859-1' : $context['character_set']));
228 View Code Duplication
	elseif ($xml_format == 'rss' || $xml_format == 'rss2')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
229
		header('Content-Type: application/rss+xml; charset=' . (empty($context['character_set']) ? 'ISO-8859-1' : $context['character_set']));
230 View Code Duplication
	elseif ($xml_format == 'atom')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
231
		header('Content-Type: application/atom+xml; charset=' . (empty($context['character_set']) ? 'ISO-8859-1' : $context['character_set']));
232
	elseif ($xml_format == 'rdf')
233
		header('Content-Type: ' . (isBrowser('ie') ? 'text/xml' : 'application/rdf+xml') . '; charset=' . (empty($context['character_set']) ? 'ISO-8859-1' : $context['character_set']));
234
235
	// First, output the xml header.
236
	echo '<?xml version="1.0" encoding="', $context['character_set'], '"?' . '>';
237
238
	// Are we outputting an rss feed or one with more information?
239
	if ($xml_format == 'rss' || $xml_format == 'rss2')
240
	{
241
		// Start with an RSS 2.0 header.
242
		echo '
243
<rss version=', $xml_format == 'rss2' ? '"2.0" xmlns:atom="http://www.w3.org/2005/Atom"' : '"0.92"', ' xml:lang="', strtr($txt['lang_locale'], '_', '-'), '">
244
	<channel>
245
		<title>', $feed_title, '</title>
246
		<link>', $scripturl, '</link>
247
		<description>', cdata_parse(strip_tags($txt['xml_rss_desc'])), '</description>';
248
249
		// RSS2 calls for this.
250
		if ($xml_format == 'rss2')
251
			echo '<atom:link rel="self" type="application/rss+xml" href="', $scripturl, '?action=.xml', !empty($_GET['sa']) ? ';sa=' . $_GET['sa'] : '', ';type=rss2" />';
252
253
		// Output all of the associative array, start indenting with 2 tabs, and name everything "item".
254
		dumpTags($xml, 2, 'item', $xml_format);
255
256
		// Output the footer of the xml.
257
		echo '
258
	</channel>
259
</rss>';
260
	}
261
	elseif ($xml_format == 'atom')
262
	{
263
		foreach ($_REQUEST as $var => $val)
264
			if (in_array($var, array('action', 'sa', 'type', 'board', 'boards', 'c', 'u', 'limit')))
265
				$url_parts[] = $var . '=' . (is_array($_REQUEST[$var]) ? implode(',', $_REQUEST[$var]) : $_REQUEST[$var]);
0 ignored issues
show
Coding Style Comprehensibility introduced by
$url_parts was never initialized. Although not strictly required by PHP, it is generally a good practice to add $url_parts = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
266
267
		echo '
268
<feed xmlns="http://www.w3.org/2005/Atom">
269
	<title>', $feed_title, '</title>
270
	<link rel="alternate" type="text/html" href="', $scripturl, '" />
271
	<link rel="self" type="application/atom+xml" href="', $scripturl, '?', !empty($url_parts) ? implode(';', $url_parts) : '', '" />
272
	<id>', $scripturl, '</id>
273
	<icon>', $boardurl, '/favicon.ico</icon>
274
275
	<updated>', gmstrftime('%Y-%m-%dT%H:%M:%SZ'), '</updated>
276
	<subtitle>', cdata_parse(strip_tags($txt['xml_rss_desc'])), '</subtitle>
277
	<generator uri="http://www.simplemachines.org" version="', strtr($forum_version, array('SMF' => '')), '">SMF</generator>
278
	<author>
279
		<name>', cdata_parse(strip_tags($context['forum_name'])), '</name>
280
	</author>';
281
282
		dumpTags($xml, 2, 'entry', $xml_format);
283
284
		echo '
285
</feed>';
286
	}
287
	elseif ($xml_format == 'rdf')
288
	{
289
		echo '
290
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns="http://purl.org/rss/1.0/">
291
	<channel rdf:about="', $scripturl, '">
292
		<title>', $feed_title, '</title>
293
		<link>', $scripturl, '</link>
294
		<description>', cdata_parse(strip_tags($txt['xml_rss_desc'])), '</description>
295
		<items>
296
			<rdf:Seq>';
297
298
		foreach ($xml as $item)
299
			echo '
300
				<rdf:li rdf:resource="', $item['link'], '" />';
301
302
		echo '
303
			</rdf:Seq>
304
		</items>
305
	</channel>
306
';
307
308
		dumpTags($xml, 1, 'item', $xml_format);
309
310
		echo '
311
</rdf:RDF>';
312
	}
313
	// Otherwise, we're using our proprietary formats - they give more data, though.
314
	else
315
	{
316
		echo '
317
<smf:xml-feed xmlns:smf="http://www.simplemachines.org/" xmlns="http://www.simplemachines.org/xml/', $_GET['sa'], '" xml:lang="', strtr($txt['lang_locale'], '_', '-'), '">';
318
319
		// Dump out that associative array.  Indent properly.... and use the right names for the base elements.
320
		dumpTags($xml, 1, $subActions[$_GET['sa']][1], $xml_format);
321
322
		echo '
323
</smf:xml-feed>';
324
}
325
326
	obExit(false);
327
}
328
329
/**
330
 * Called from dumpTags to convert data to xml
331
 * Finds urls for local site and sanitizes them
332
 *
333
 * @param string $val A string containing a possible URL
334
 * @return string $val The string with any possible URLs sanitized
335
 */
336
function fix_possible_url($val)
337
{
338
	global $modSettings, $context, $scripturl;
339
340
	if (substr($val, 0, strlen($scripturl)) != $scripturl)
341
		return $val;
342
343
	call_integration_hook('integrate_fix_url', array(&$val));
344
345
	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']))
346
		return $val;
347
348
	$val = preg_replace_callback('~^' . preg_quote($scripturl, '/') . '\?((?:board|topic)=[^#"]+)(#[^"]*)?$~', function ($m) use ($scripturl)
349
		{
350
			return $scripturl . '/' . strtr("$m[1]", '&;=', '//,') . '.html' . (isset($m[2]) ? $m[2] : "");
351
		}, $val);
352
	return $val;
353
}
354
355
/**
356
 * Ensures supplied data is properly encapsulated in cdata xml tags
357
 * Called from getXmlProfile in News.php
358
 *
359
 * @param string $data XML data
360
 * @param string $ns
361
 * @return string The XML data enclosed in cdata tags
362
 */
363
function cdata_parse($data, $ns = '')
364
{
365
	global $smcFunc;
366
367
	// Do we even need to do this?
368
	if (strpbrk($data, '<>&') == false)
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...
369
		return $data;
370
371
	$cdata = '<![CDATA[';
372
373
	for ($pos = 0, $n = $smcFunc['strlen']($data); $pos < $n; null)
374
	{
375
		$positions = array(
376
			$smcFunc['strpos']($data, '&', $pos),
377
			$smcFunc['strpos']($data, ']', $pos),
378
		);
379
		if ($ns != '')
380
			$positions[] = $smcFunc['strpos']($data, '<', $pos);
381
		foreach ($positions as $k => $dummy)
382
		{
383
			if ($dummy === false)
384
				unset($positions[$k]);
385
		}
386
387
		$old = $pos;
388
		$pos = empty($positions) ? $n : min($positions);
389
390
		if ($pos - $old > 0)
391
			$cdata .= $smcFunc['substr']($data, $old, $pos - $old);
392
		if ($pos >= $n)
393
			break;
394
395
		if ($smcFunc['substr']($data, $pos, 1) == '<')
396
		{
397
			$pos2 = $smcFunc['strpos']($data, '>', $pos);
398
			if ($pos2 === false)
399
				$pos2 = $n;
400
			if ($smcFunc['substr']($data, $pos + 1, 1) == '/')
401
				$cdata .= ']]></' . $ns . ':' . $smcFunc['substr']($data, $pos + 2, $pos2 - $pos - 1) . '<![CDATA[';
402
			else
403
				$cdata .= ']]><' . $ns . ':' . $smcFunc['substr']($data, $pos + 1, $pos2 - $pos) . '<![CDATA[';
404
			$pos = $pos2 + 1;
405
		}
406
		elseif ($smcFunc['substr']($data, $pos, 1) == ']')
407
		{
408
			$cdata .= ']]>&#093;<![CDATA[';
409
			$pos++;
410
		}
411
		elseif ($smcFunc['substr']($data, $pos, 1) == '&')
412
		{
413
			$pos2 = $smcFunc['strpos']($data, ';', $pos);
414
			if ($pos2 === false)
415
				$pos2 = $n;
416
			$ent = $smcFunc['substr']($data, $pos + 1, $pos2 - $pos - 1);
417
418
			if ($smcFunc['substr']($data, $pos + 1, 1) == '#')
419
				$cdata .= ']]>' . $smcFunc['substr']($data, $pos, $pos2 - $pos + 1) . '<![CDATA[';
420
			elseif (in_array($ent, array('amp', 'lt', 'gt', 'quot')))
421
				$cdata .= ']]>' . $smcFunc['substr']($data, $pos, $pos2 - $pos + 1) . '<![CDATA[';
422
423
			$pos = $pos2 + 1;
424
		}
425
	}
426
427
	$cdata .= ']]>';
428
429
	return strtr($cdata, array('<![CDATA[]]>' => ''));
430
}
431
432
/**
433
 * Formats data retrieved in other functions into xml format.
434
 * Additionally formats data based on the specific format passed.
435
 * This function is recursively called to handle sub arrays of data.
436
437
 * @param array $data The array to output as xml data
438
 * @param int $i The amount of indentation to use.
439
 * @param null|string $tag If specified, it will be used instead of the keys of data.
440
 * @param string $xml_format The format to use ('atom', 'rss', 'rss2' or empty for plain XML)
441
 */
442
function dumpTags($data, $i, $tag = null, $xml_format = '')
443
{
444
	// Wrap the values of these keys into CDATA tags
445
	$keysToCdata = array(
446
		'title',
447
		'name',
448
		'description',
449
		'summary',
450
		'subject',
451
		'body',
452
		'username',
453
		'signature',
454
		'position',
455
		'language',
456
		'gender',
457
		'blurb',
458
	);
459
	if ($xml_format != 'atom')
460
		$keysToCdata[] = 'category';
461
462
	// For every array in the data...
463
	foreach ($data as $key => $val)
464
	{
465
		// Skip it, it's been set to null.
466
		if ($val === null)
467
			continue;
468
469
		// If the value should maybe be CDATA, do that now.
470
		if (!is_array($val) && in_array($key, $keysToCdata))
471
			$val = cdata_parse($val);
472
473
		// If a tag was passed, use it instead of the key.
474
		$key = isset($tag) ? $tag : $key;
475
476
		// First let's indent!
477
		echo "\n", str_repeat("\t", $i);
478
479
		// Grr, I hate kludges... almost worth doing it properly, here, but not quite.
480
		if ($xml_format == 'atom' && $key == 'link')
481
		{
482
			echo '<link rel="alternate" type="text/html" href="', fix_possible_url($val), '" />';
483
			continue;
484
		}
485
486
		// If it's empty/0/nothing simply output an empty tag.
487
		if ($val == '')
488
			echo '<', $key, ' />';
489
		elseif ($xml_format == 'atom' && $key == 'category')
490
			echo '<', $key, ' term="', $val, '" />';
491
		else
492
		{
493
			// Beginning tag.
494
			if ($xml_format == 'rdf' && $key == 'item' && isset($val['link']))
495
			{
496
				echo '<', $key, ' rdf:about="', fix_possible_url($val['link']), '">';
497
				echo "\n", str_repeat("\t", $i + 1);
498
				echo '<dc:format>text/html</dc:format>';
499
			}
500
			elseif ($xml_format == 'atom' && $key == 'summary')
501
				echo '<', $key, ' type="html">';
502
			else
503
				echo '<', $key, '>';
504
505
			// The element's value.
506
			if (is_array($val))
507
			{
508
				// An array.  Dump it, and then indent the tag.
509
				dumpTags($val, $i + 1, null, $xml_format);
510
				echo "\n", str_repeat("\t", $i);
511
			}
512
			// A string with returns in it.... show this as a multiline element.
513
			elseif (strpos($val, "\n") !== false || preg_match('~<br ?/?' . '>~', $val) !== false)
514
				echo "\n", fix_possible_url($val), "\n", str_repeat("\t", $i);
515
			// A simple string.
516
			else
517
				echo fix_possible_url($val);
518
519
			// Ending tag.
520
			echo '</', $key, '>';
521
		}
522
	}
523
}
524
525
/**
526
 * Retrieve the list of members from database.
527
 * The array will be generated to match the format.
528
 * @todo get the list of members from Subs-Members.
529
 *
530
 * @param string $xml_format The format to use. Can be 'atom', 'rdf', 'rss', 'rss2' or 'xml'
531
 * @return array An array of arrays of feed items. Each array has keys corresponding to the appropriate tags for the specified format.
532
 */
533
function getXmlMembers($xml_format)
534
{
535
	global $scripturl, $smcFunc;
536
537
	if (!allowedTo('view_mlist'))
538
		return array();
539
540
	// Find the most recent members.
541
	$request = $smcFunc['db_query']('', '
542
		SELECT id_member, member_name, real_name, date_registered, last_login
543
		FROM {db_prefix}members
544
		ORDER BY id_member DESC
545
		LIMIT {int:limit}',
546
		array(
547
			'limit' => $_GET['limit'],
548
		)
549
	);
550
	$data = array();
551
	while ($row = $smcFunc['db_fetch_assoc']($request))
552
	{
553
		// Make the data look rss-ish.
554
		if ($xml_format == 'rss' || $xml_format == 'rss2')
555
			$data[] = array(
556
				'title' => $row['real_name'],
557
				'link' => $scripturl . '?action=profile;u=' . $row['id_member'],
558
				'comments' => $scripturl . '?action=pm;sa=send;u=' . $row['id_member'],
559
				'pubDate' => gmdate('D, d M Y H:i:s \G\M\T', $row['date_registered']),
560
				'guid' => $scripturl . '?action=profile;u=' . $row['id_member'],
561
			);
562
		elseif ($xml_format == 'rdf')
563
			$data[] = array(
564
				'title' => $row['real_name'],
565
				'link' => $scripturl . '?action=profile;u=' . $row['id_member'],
566
			);
567
		elseif ($xml_format == 'atom')
568
			$data[] = array(
569
				'title' => $row['real_name'],
570
				'link' => $scripturl . '?action=profile;u=' . $row['id_member'],
571
				'published' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $row['date_registered']),
572
				'updated' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $row['last_login']),
573
				'id' => $scripturl . '?action=profile;u=' . $row['id_member'],
574
			);
575
		// More logical format for the data, but harder to apply.
576
		else
577
			$data[] = array(
578
				'name' => $row['real_name'],
579
				'time' => $smcFunc['htmlspecialchars'](strip_tags(timeformat($row['date_registered']))),
580
				'id' => $row['id_member'],
581
				'link' => $scripturl . '?action=profile;u=' . $row['id_member']
582
			);
583
	}
584
	$smcFunc['db_free_result']($request);
585
586
	return $data;
587
}
588
589
/**
590
 * Get the latest topics information from a specific board,
591
 * to display later.
592
 * The returned array will be generated to match the xmf_format.
593
 * @todo does not belong here
594
 *
595
 * @param $xml_format The XML format. Can be 'atom', 'rdf', 'rss', 'rss2' or 'xml'.
596
 * @return array An array of arrays of topic data for the feed. Each array has keys corresponding to the tags for the specified format.
597
 */
598
function getXmlNews($xml_format)
599
{
600
	global $scripturl, $modSettings, $board, $user_info;
601
	global $query_this_board, $smcFunc, $context;
602
603
	/* Find the latest posts that:
604
		- are the first post in their topic.
605
		- are on an any board OR in a specified board.
606
		- can be seen by this user.
607
		- are actually the latest posts. */
608
609
	$done = false;
610
	$loops = 0;
611
	while (!$done)
612
	{
613
		$optimize_msg = implode(' AND ', $context['optimize_msg']);
614
		$request = $smcFunc['db_query']('', '
615
			SELECT
616
				m.smileys_enabled, m.poster_time, m.id_msg, m.subject, m.body, m.modified_time,
617
				m.icon, t.id_topic, t.id_board, t.num_replies,
618
				b.name AS bname,
619
				COALESCE(mem.id_member, 0) AS id_member,
620
				COALESCE(mem.email_address, m.poster_email) AS poster_email,
621
				COALESCE(mem.real_name, m.poster_name) AS poster_name
622
			FROM {db_prefix}topics AS t
623
				INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
624
				INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
625
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
626
			WHERE ' . $query_this_board . (empty($optimize_msg) ? '' : '
627
				AND {raw:optimize_msg}') . (empty($board) ? '' : '
628
				AND t.id_board = {int:current_board}') . ($modSettings['postmod_active'] ? '
629
				AND t.approved = {int:is_approved}' : '') . '
630
			ORDER BY t.id_first_msg DESC
631
			LIMIT {int:limit}',
632
			array(
633
				'current_board' => $board,
634
				'is_approved' => 1,
635
				'limit' => $_GET['limit'],
636
				'optimize_msg' => $optimize_msg,
637
			)
638
		);
639
		// If we don't have $_GET['limit'] results, try again with an unoptimized version covering all rows.
640
		if ($loops < 2 && $smcFunc['db_num_rows']($request) < $_GET['limit'])
641
		{
642
			$smcFunc['db_free_result']($request);
643 View Code Duplication
			if (empty($_REQUEST['boards']) && empty($board))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
644
				unset($context['optimize_msg']['lowest']);
645
			else
646
				$context['optimize_msg']['lowest'] = 'm.id_msg >= t.id_first_msg';
647
			$context['optimize_msg']['highest'] = 'm.id_msg <= t.id_last_msg';
648
			$loops++;
649
		}
650
		else
651
			$done = true;
652
	}
653
	$data = array();
654
	while ($row = $smcFunc['db_fetch_assoc']($request))
0 ignored issues
show
Bug introduced by
The variable $request does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
655
	{
656
		// Limit the length of the message, if the option is set.
657 View Code Duplication
		if (!empty($modSettings['xmlnews_maxlen']) && $smcFunc['strlen'](str_replace('<br>', "\n", $row['body'])) > $modSettings['xmlnews_maxlen'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
658
			$row['body'] = strtr($smcFunc['substr'](str_replace('<br>', "\n", $row['body']), 0, $modSettings['xmlnews_maxlen'] - 3), array("\n" => '<br>')) . '...';
659
660
		$row['body'] = parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']);
661
662
		censorText($row['body']);
663
		censorText($row['subject']);
664
665
		// Being news, this actually makes sense in rss format.
666
		if ($xml_format == 'rss' || $xml_format == 'rss2')
667
			$data[] = array(
668
				'title' => $row['subject'],
669
				'link' => $scripturl . '?topic=' . $row['id_topic'] . '.0',
670
				'description' => $row['body'],
671
				'author' => (allowedTo('moderate_forum') || $row['id_member'] == $user_info['id']) ? $row['poster_email'] . ' ('.$row['poster_name'].')' : null,
672
				'comments' => $scripturl . '?action=post;topic=' . $row['id_topic'] . '.0',
673
				'category' => $row['bname'],
674
				'pubDate' => gmdate('D, d M Y H:i:s \G\M\T', $row['poster_time']),
675
				'guid' => $scripturl . '?topic=' . $row['id_topic'] . '.0',
676
			);
677
		elseif ($xml_format == 'rdf')
678
			$data[] = array(
679
				'title' => $row['subject'],
680
				'link' => $scripturl . '?topic=' . $row['id_topic'] . '.0',
681
				'description' => $row['body'],
682
			);
683
		elseif ($xml_format == 'atom')
684
			$data[] = array(
685
				'title' => $row['subject'],
686
				'link' => $scripturl . '?topic=' . $row['id_topic'] . '.0',
687
				'summary' => $row['body'],
688
				'category' => $row['bname'],
689
				'author' => array(
690
					'name' => $row['poster_name'],
691
					'email' => (allowedTo('moderate_forum') || $row['id_member'] == $user_info['id']) ? $row['poster_email'] : null,
692
					'uri' => !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : null,
693
				),
694
				'published' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $row['poster_time']),
695
				'updated' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', empty($row['modified_time']) ? $row['poster_time'] : $row['modified_time']),
696
				'id' => $scripturl . '?topic=' . $row['id_topic'] . '.0',
697
			);
698
		// The biggest difference here is more information.
699
		else
700
			$data[] = array(
701
				'time' => $smcFunc['htmlspecialchars'](strip_tags(timeformat($row['poster_time']))),
702
				'id' => $row['id_topic'],
703
				'subject' => $row['subject'],
704
				'body' => $row['body'],
705
				'poster' => array(
706
					'name' => $row['poster_name'],
707
					'id' => $row['id_member'],
708
					'link' => !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : '',
709
				),
710
				'topic' => $row['id_topic'],
711
				'board' => array(
712
					'name' => $row['bname'],
713
					'id' => $row['id_board'],
714
					'link' => $scripturl . '?board=' . $row['id_board'] . '.0',
715
				),
716
				'link' => $scripturl . '?topic=' . $row['id_topic'] . '.0',
717
			);
718
	}
719
	$smcFunc['db_free_result']($request);
720
721
	return $data;
722
}
723
724
/**
725
 * Get the recent topics to display.
726
 * The returned array will be generated to match the xml_format.
727
 * @todo does not belong here.
728
 *
729
 * @param string $xml_format The XML format. Can be 'atom', 'rdf', 'rss', 'rss2' or 'xml'
730
 * @return array An array of arrays containing data for the feed. Each array has keys corresponding to the appropriate tags for the specified format.
731
 */
732
function getXmlRecent($xml_format)
733
{
734
	global $scripturl, $modSettings, $board;
735
	global $query_this_board, $smcFunc, $context, $user_info;
736
737
	$done = false;
738
	$loops = 0;
739
	while (!$done)
740
	{
741
		$optimize_msg = implode(' AND ', $context['optimize_msg']);
742
		$request = $smcFunc['db_query']('', '
743
			SELECT m.id_msg
744
			FROM {db_prefix}messages AS m
745
				INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
746
				INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
747
			WHERE ' . $query_this_board . (empty($optimize_msg) ? '' : '
748
				AND {raw:optimize_msg}') . (empty($board) ? '' : '
749
				AND m.id_board = {int:current_board}') . ($modSettings['postmod_active'] ? '
750
				AND m.approved = {int:is_approved}' : '') . '
751
			ORDER BY m.id_msg DESC
752
			LIMIT {int:limit}',
753
			array(
754
				'limit' => $_GET['limit'],
755
				'current_board' => $board,
756
				'is_approved' => 1,
757
				'optimize_msg' => $optimize_msg,
758
			)
759
		);
760
		// If we don't have $_GET['limit'] results, try again with an unoptimized version covering all rows.
761
		if ($loops < 2 && $smcFunc['db_num_rows']($request) < $_GET['limit'])
762
		{
763
			$smcFunc['db_free_result']($request);
764 View Code Duplication
			if (empty($_REQUEST['boards']) && empty($board))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
765
				unset($context['optimize_msg']['lowest']);
766
			else
767
				$context['optimize_msg']['lowest'] = $loops ? 'm.id_msg >= t.id_first_msg' : 'm.id_msg >= (t.id_last_msg - t.id_first_msg) / 2';
768
			$loops++;
769
		}
770
		else
771
			$done = true;
772
	}
773
	$messages = array();
774
	while ($row = $smcFunc['db_fetch_assoc']($request))
0 ignored issues
show
Bug introduced by
The variable $request does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
775
		$messages[] = $row['id_msg'];
776
	$smcFunc['db_free_result']($request);
777
778
	if (empty($messages))
779
		return array();
780
781
	// Find the most recent posts this user can see.
782
	$request = $smcFunc['db_query']('', '
783
		SELECT
784
			m.smileys_enabled, m.poster_time, m.id_msg, m.subject, m.body, m.id_topic, t.id_board,
785
			b.name AS bname, t.num_replies, m.id_member, m.icon, mf.id_member AS id_first_member,
786
			COALESCE(mem.real_name, m.poster_name) AS poster_name, mf.subject AS first_subject,
787
			COALESCE(memf.real_name, mf.poster_name) AS first_poster_name,
788
			COALESCE(mem.email_address, m.poster_email) AS poster_email, m.modified_time
789
		FROM {db_prefix}messages AS m
790
			INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
791
			INNER JOIN {db_prefix}messages AS mf ON (mf.id_msg = t.id_first_msg)
792
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
793
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
794
			LEFT JOIN {db_prefix}members AS memf ON (memf.id_member = mf.id_member)
795
		WHERE m.id_msg IN ({array_int:message_list})
796
			' . (empty($board) ? '' : 'AND t.id_board = {int:current_board}') . '
797
		ORDER BY m.id_msg DESC
798
		LIMIT {int:limit}',
799
		array(
800
			'limit' => $_GET['limit'],
801
			'current_board' => $board,
802
			'message_list' => $messages,
803
		)
804
	);
805
	$data = array();
806
	while ($row = $smcFunc['db_fetch_assoc']($request))
807
	{
808
		// Limit the length of the message, if the option is set.
809 View Code Duplication
		if (!empty($modSettings['xmlnews_maxlen']) && $smcFunc['strlen'](str_replace('<br>', "\n", $row['body'])) > $modSettings['xmlnews_maxlen'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
810
			$row['body'] = strtr($smcFunc['substr'](str_replace('<br>', "\n", $row['body']), 0, $modSettings['xmlnews_maxlen'] - 3), array("\n" => '<br>')) . '...';
811
812
		$row['body'] = parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']);
813
814
		censorText($row['body']);
815
		censorText($row['subject']);
816
817
		// Doesn't work as well as news, but it kinda does..
818
		if ($xml_format == 'rss' || $xml_format == 'rss2')
819
			$data[] = array(
820
				'title' => $row['subject'],
821
				'link' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'],
822
				'description' => $row['body'],
823
				'author' => (allowedTo('moderate_forum') || (!empty($row['id_member']) && $row['id_member'] == $user_info['id'])) ? $row['poster_email'] : null,
824
				'category' => $row['bname'],
825
				'comments' => $scripturl . '?action=post;topic=' . $row['id_topic'] . '.0',
826
				'pubDate' => gmdate('D, d M Y H:i:s \G\M\T', $row['poster_time']),
827
				'guid' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg']
828
			);
829
		elseif ($xml_format == 'rdf')
830
			$data[] = array(
831
				'title' => $row['subject'],
832
				'link' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'],
833
				'description' => $row['body'],
834
			);
835
		elseif ($xml_format == 'atom')
836
			$data[] = array(
837
				'title' => $row['subject'],
838
				'link' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'],
839
				'summary' => $row['body'],
840
				'category' => $row['bname'],
841
				'author' => array(
842
					'name' => $row['poster_name'],
843
					'email' => (allowedTo('moderate_forum') || (!empty($row['id_member']) && $row['id_member'] == $user_info['id'])) ? $row['poster_email'] : null,
844
					'uri' => !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : null,
845
				),
846
				'published' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $row['poster_time']),
847
				'updated' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', empty($row['modified_time']) ? $row['poster_time'] : $row['modified_time']),
848
				'id' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'],
849
			);
850
		// A lot of information here.  Should be enough to please the rss-ers.
851
		else
852
			$data[] = array(
853
				'time' => $smcFunc['htmlspecialchars'](strip_tags(timeformat($row['poster_time']))),
854
				'id' => $row['id_msg'],
855
				'subject' => $row['subject'],
856
				'body' => $row['body'],
857
				'starter' => array(
858
					'name' => $row['first_poster_name'],
859
					'id' => $row['id_first_member'],
860
					'link' => !empty($row['id_first_member']) ? $scripturl . '?action=profile;u=' . $row['id_first_member'] : ''
861
				),
862
				'poster' => array(
863
					'name' => $row['poster_name'],
864
					'id' => $row['id_member'],
865
					'link' => !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : ''
866
				),
867
				'topic' => array(
868
					'subject' => $row['first_subject'],
869
					'id' => $row['id_topic'],
870
					'link' => $scripturl . '?topic=' . $row['id_topic'] . '.new#new'
871
				),
872
				'board' => array(
873
					'name' => $row['bname'],
874
					'id' => $row['id_board'],
875
					'link' => $scripturl . '?board=' . $row['id_board'] . '.0'
876
				),
877
				'link' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg']
878
			);
879
	}
880
	$smcFunc['db_free_result']($request);
881
882
	return $data;
883
}
884
885
/**
886
 * Get the profile information for member into an array,
887
 * which will be generated to match the xml_format.
888
 * @todo refactor.
889
 *
890
 * @param $xml_format The XML format. Can be 'atom', 'rdf', 'rss', 'rss2' or 'xml'
891
 * @return array An array profile data
892
 */
893
function getXmlProfile($xml_format)
894
{
895
	global $scripturl, $memberContext, $user_profile, $user_info;
896
897
	// You must input a valid user....
898
	if (empty($_GET['u']) || !loadMemberData((int) $_GET['u']))
899
		return array();
900
901
	// Make sure the id is a number and not "I like trying to hack the database".
902
	$_GET['u'] = (int) $_GET['u'];
903
	// Load the member's contextual information!
904
	if (!loadMemberContext($_GET['u']) || !allowedTo('profile_view'))
905
		return array();
906
907
	// Okay, I admit it, I'm lazy.  Stupid $_GET['u'] is long and hard to type.
908
	$profile = &$memberContext[$_GET['u']];
909
910
	if ($xml_format == 'rss' || $xml_format == 'rss2')
911
		$data = array(array(
912
			'title' => $profile['name'],
913
			'link' => $scripturl . '?action=profile;u=' . $profile['id'],
914
			'description' => isset($profile['group']) ? $profile['group'] : $profile['post_group'],
915
			'comments' => $scripturl . '?action=pm;sa=send;u=' . $profile['id'],
916
			'pubDate' => gmdate('D, d M Y H:i:s \G\M\T', $user_profile[$profile['id']]['date_registered']),
917
			'guid' => $scripturl . '?action=profile;u=' . $profile['id'],
918
		));
919
	elseif ($xml_format == 'rdf')
920
		$data = array(array(
921
			'title' => $profile['name'],
922
			'link' => $scripturl . '?action=profile;u=' . $profile['id'],
923
			'description' => isset($profile['group']) ? $profile['group'] : $profile['post_group'],
924
		));
925
	elseif ($xml_format == 'atom')
926
		$data[] = array(
0 ignored issues
show
Coding Style Comprehensibility introduced by
$data was never initialized. Although not strictly required by PHP, it is generally a good practice to add $data = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
927
			'title' => $profile['name'],
928
			'link' => $scripturl . '?action=profile;u=' . $profile['id'],
929
			'summary' => isset($profile['group']) ? $profile['group'] : $profile['post_group'],
930
			'author' => array(
931
				'name' => $profile['real_name'],
932
				'email' => $profile['show_email'] ? $profile['email'] : null,
933
				'uri' => !empty($profile['website']) ? $profile['website']['url'] : null,
934
			),
935
			'published' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $user_profile[$profile['id']]['date_registered']),
936
			'updated' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $user_profile[$profile['id']]['last_login']),
937
			'id' => $scripturl . '?action=profile;u=' . $profile['id'],
938
			'logo' => !empty($profile['avatar']) ? $profile['avatar']['url'] : '',
939
		);
940
	else
941
	{
942
		$data = array(
943
			'username' => $user_info['is_admin'] || $user_info['id'] == $profile['id'] ? $profile['username'] : '',
944
			'name' => $profile['name'],
945
			'link' => $scripturl . '?action=profile;u=' . $profile['id'],
946
			'posts' => $profile['posts'],
947
			'post-group' => $profile['post_group'],
948
			'language' => $profile['language'],
949
			'last-login' => gmdate('D, d M Y H:i:s \G\M\T', $user_profile[$profile['id']]['last_login']),
950
			'registered' => gmdate('D, d M Y H:i:s \G\M\T', $user_profile[$profile['id']]['date_registered'])
951
		);
952
953
		// Everything below here might not be set, and thus maybe shouldn't be displayed.
954
		if ($profile['gender']['name'] != '')
955
			$data['gender'] = $profile['gender']['name'];
956
957
		if ($profile['avatar']['name'] != '')
958
			$data['avatar'] = $profile['avatar']['url'];
959
960
		// If they are online, show an empty tag... no reason to put anything inside it.
961
		if ($profile['online']['is_online'])
962
			$data['online'] = '';
963
964
		if ($profile['signature'] != '')
965
			$data['signature'] = $profile['signature'];
966
		if ($profile['blurb'] != '')
967
			$data['blurb'] = $profile['blurb'];
968
		if ($profile['title'] != '')
969
			$data['title'] = $profile['title'];
970
971
		if ($profile['website']['title'] != '')
972
			$data['website'] = array(
973
				'title' => $profile['website']['title'],
974
				'link' => $profile['website']['url']
975
			);
976
977
		if ($profile['group'] != '')
978
			$data['position'] = $profile['group'];
979
980
		if ($profile['show_email'])
981
			$data['email'] = $profile['email'];
982
983
		if (!empty($profile['birth_date']) && substr($profile['birth_date'], 0, 4) != '0000')
984
		{
985
			list ($birth_year, $birth_month, $birth_day) = sscanf($profile['birth_date'], '%d-%d-%d');
986
			$datearray = getdate(forum_time());
987
			$data['age'] = $datearray['year'] - $birth_year - (($datearray['mon'] > $birth_month || ($datearray['mon'] == $birth_month && $datearray['mday'] >= $birth_day)) ? 0 : 1);
988
		}
989
	}
990
991
	// Save some memory.
992
	unset($profile, $memberContext[$_GET['u']]);
993
994
	return $data;
995
}
996
997
?>
0 ignored issues
show
Best Practice introduced by
It is not recommended to use PHP's closing tag ?> in files other than templates.

Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.

A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.

Loading history...