Failed Conditions
Branch release-2.1 (4e22cf)
by Rick
06:39
created

News.php ➔ getXmlMembers()   C

Complexity

Conditions 7
Paths 6

Size

Total Lines 130
Code Lines 85

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 85
nc 6
nop 1
dl 0
loc 130
rs 6.4589
c 0
b 0
f 0

How to fix   Long Method   

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 2017 Simple Machines and individual contributors
11
 * @license http://www.simplemachines.org/about/smf/license.php BSD
12
 *
13
 * @version 2.1 Beta 4
14
 */
15
16
if (!defined('SMF'))
17
	die('No direct access...');
18
19
/**
20
 * Outputs xml data representing recent information or a profile.
21
 * Can be passed 4 subactions which decide what is output:
22
 *  'recent' for recent posts,
23
 *  'news' for news topics,
24
 *  'members' for recently registered members,
25
 *  'profile' for a member's profile.
26
 * To display a member's profile, a user id has to be given. (;u=1)
27
 * Outputs an rss feed instead of a proprietary one if the 'type' $_GET
28
 * parameter is 'rss' or 'rss2'.
29
 * Accessed via ?action=.xml.
30
 * Does not use any templates, sub templates, or template layers.
31
 *
32
 * @uses Stats language file.
33
 */
34
function ShowXmlFeed()
35
{
36
	global $board, $board_info, $context, $scripturl, $boardurl, $txt, $modSettings, $user_info;
37
	global $query_this_board, $smcFunc, $forum_version, $settings;
38
39
	// If it's not enabled, die.
40
	if (empty($modSettings['xmlnews_enable']))
41
		obExit(false);
42
43
	loadLanguage('Stats');
44
45
	// Default to latest 5.  No more than 255, please.
46
	$_GET['limit'] = empty($_GET['limit']) || (int) $_GET['limit'] < 1 ? 5 : min((int) $_GET['limit'], 255);
47
48
	// Some general metadata for this feed. We'll change some of these values below.
49
	$feed_meta = array(
50
		'title' => '',
51
		'desc' => $txt['xml_rss_desc'],
52
		'author' => $context['forum_name'],
53
		'source' => $scripturl,
54
		'rights' => '© ' . date('Y') . ' ' . $context['forum_name'],
55
		'icon' => !empty($settings['og_image']) ? $settings['og_image'] : $boardurl . '/favicon.ico',
56
		'language' => !empty($txt['lang_locale']) ? str_replace("_", "-", substr($txt['lang_locale'], 0, strcspn($txt['lang_locale'], "."))) : 'en',
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal _ does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
Coding Style Comprehensibility introduced by
The string literal - does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
Coding Style Comprehensibility introduced by
The string literal . does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
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 View Code Duplication
		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 View Code Duplication
		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 View Code Duplication
		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?
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
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();
209
210
	// Get the associative array representing the xml.
211
	if (!empty($modSettings['cache_enable']) && (!$user_info['is_guest'] || $modSettings['cache_enable'] >= 3))
212
		$xml_data = cache_get_data('xmlfeed-' . $xml_format . ':' . ($user_info['is_guest'] ? '' : $user_info['id'] . '-') . $cachekey, 240);
213
	if (empty($xml_data))
214
	{
215
		$call = call_helper($subActions[$_GET['sa']][0], true);
216
217
		if (!empty($call))
218
			$xml_data = call_user_func($call, $xml_format);
219
220
		if (!empty($modSettings['cache_enable']) && (($user_info['is_guest'] && $modSettings['cache_enable'] >= 3)
221
		|| (!$user_info['is_guest'] && (array_sum(explode(' ', microtime())) - array_sum(explode(' ', $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
Bug introduced by
The variable $xml_data 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...
223
	}
224
225
	$feed_meta['title'] = $smcFunc['htmlspecialchars'](strip_tags($context['forum_name'])) . (isset($feed_meta['title']) ? $feed_meta['title'] : '');
226
227
	// Allow mods to add extra namespaces and tags to the feed/channel
228
	$namespaces = array(
229
		'rss' => array(),
230
		'rss2' => array('atom' => 'http://www.w3.org/2005/Atom'),
231
		'atom' => array('' => 'http://www.w3.org/2005/Atom'),
232
		'rdf' => array(
233
			'' => 'http://purl.org/rss/1.0/',
234
			'rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
235
			'dc' => 'http://purl.org/dc/elements/1.1/',
236
		),
237
		'smf' => array(
238
			'' => 'http://www.simplemachines.org/xml/' . $_GET['sa'],
239
			'smf' => 'http://www.simplemachines.org/',
240
		),
241
	);
242
	$extraFeedTags = array(
243
		'rss' => array(),
244
		'rss2' => array(),
245
		'atom' => array(),
246
		'rdf' => array(),
247
		'smf' => array(),
248
	);
249
250
	// Allow mods to specify any keys that need special handling
251
	$forceCdataKeys = array();
252
	$nsKeys = array();
253
254
	// Remember this, just in case...
255
	$orig_feed_meta = $feed_meta;
256
257
	// If mods want to do somthing with this feed, let them do that now.
258
	// Provide the feed's data, metadata, namespaces, extra feed-level tags, keys that need special handling, the feed format, and the requested subaction
259
	call_integration_hook('integrate_xml_data', array(&$xml_data, &$feed_meta, &$namespaces, &$extraFeedTags, &$forceCdataKeys, &$nsKeys, $xml_format, $_GET['sa']));
260
261
	// These can't be empty
262
	foreach (array('title', 'desc', 'source') as $mkey)
263
		$feed_meta[$mkey] = !empty($feed_meta[$mkey]) ? $feed_meta[$mkey] : $orig_feed_meta[$mkey];
264
265
	// Sanitize basic feed metadata values
266
	foreach ($feed_meta as $mkey => $mvalue)
267
		$feed_meta[$mkey] = cdata_parse(strip_tags(fix_possible_url($feed_meta[$mkey])));
268
269
	$ns_string = '';
270
	if (!empty($namespaces[$xml_format]))
271
	{
272
		foreach ($namespaces[$xml_format] as $nsprefix => $nsurl)
273
			$ns_string .= ' xmlns' . ($nsprefix !== '' ? ':' : '') . $nsprefix . '="' . $nsurl . '"';
274
	}
275
276
	$extraFeedTags_string = '';
277
	if (!empty($extraFeedTags[$xml_format]))
278
	{
279
		$indent = "\t" . ($xml_format !== 'atom' ? "\t" : '');
280
		foreach ($extraFeedTags[$xml_format] as $extraTag)
281
			$extraFeedTags_string .= "\n" . $indent . $extraTag;
282
	}
283
284
	// This is an xml file....
285
	ob_end_clean();
286
	if (!empty($modSettings['enableCompressedOutput']))
287
		@ob_start('ob_gzhandler');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition 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...
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 View Code Duplication
	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 View Code Duplication
	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 View Code Duplication
			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);
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...
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 View Code Duplication
		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);
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...
349
350
		echo '
351
<feed', $ns_string, !empty($feed_meta['language']) ? ' xml:lang="' . $feed_meta['language'] . '"' : '', '>
352
	<title>', $feed_meta['title'], '</title>
353
	<link rel="alternate" type="text/html" href="', $feed_meta['source'], '" />
354
	<link rel="self" type="application/atom+xml" href="', $scripturl, !empty($url_parts) ? '?' . implode(';', $url_parts) : '', '" />
355
	<updated>', gmstrftime('%Y-%m-%dT%H:%M:%SZ'), '</updated>
356
	<id>', $feed_meta['source'], '</id>
357
	<subtitle>', $feed_meta['desc'], '</subtitle>
358
	<generator uri="https://www.simplemachines.org" version="', strtr($forum_version, array('SMF' => '')), '">SMF</generator>',
359
	!empty($feed_meta['icon']) ? '
360
	<icon>' . $feed_meta['icon'] . '</icon>' : '',
361
	!empty($feed_meta['author']) ? '
362
	<author>
363
		<name>' . $feed_meta['author'] . '</name>
364
	</author>' : '',
365
	!empty($feed_meta['rights']) ? '
366
	<rights>' . $feed_meta['rights'] . '</rights>' : '';
367
368
		echo $extraFeedTags_string;
369
370
		dumpTags($xml_data, 1, null, $xml_format, $forceCdataKeys, $nsKeys);
371
372
		echo '
373
</feed>';
374
	}
375
	elseif ($xml_format == 'rdf')
376
	{
377
		echo '
378
<rdf:RDF', $ns_string, '>
379
	<channel rdf:about="', $scripturl, '">
380
		<title>', $feed_meta['title'], '</title>
381
		<link>', $feed_meta['source'], '</link>
382
		<description>', $feed_meta['desc'], '</description>';
383
384
		echo $extraFeedTags_string;
385
386
		echo '
387
		<items>
388
			<rdf:Seq>';
389
390
		foreach ($xml_data as $item)
391
		{
392
			$link = array_filter($item['content'], function ($e) { return ($e['tag'] == 'link'); });
0 ignored issues
show
Coding Style introduced by
It is generally recommended to place each PHP statement on a line by itself.

Let’s take a look at an example:

// Bad
$a = 5; $b = 6; $c = 7;

// Good
$a = 5;
$b = 6;
$c = 7;
Loading history...
393
			$link = array_pop($link);
394
395
			echo '
396
				<rdf:li rdf:resource="', $link['content'], '" />';
397
		}
398
399
		echo '
400
			</rdf:Seq>
401
		</items>
402
	</channel>
403
';
404
405
		dumpTags($xml_data, 1, null, $xml_format, $forceCdataKeys, $nsKeys);
406
407
		echo '
408
</rdf:RDF>';
409
	}
410
	// Otherwise, we're using our proprietary formats - they give more data, though.
411
	else
412
	{
413
		echo '
414
<smf:xml-feed xml:lang="', strtr($txt['lang_locale'], '_', '-'), '"', $ns_string, '>';
415
416
		// Hard to imagine anyone wanting to add these for the proprietary format, but just in case...
417
		echo $extraFeedTags_string;
418
419
		// Dump out that associative array.  Indent properly.... and use the right names for the base elements.
420
		dumpTags($xml_data, 1, $subActions[$_GET['sa']][1], $xml_format, $forceCdataKeys, $nsKeys);
421
422
		echo '
423
</smf:xml-feed>';
424
}
425
426
	obExit(false);
427
}
428
429
/**
430
 * Called from dumpTags to convert data to xml
431
 * Finds urls for local site and sanitizes them
432
 *
433
 * @param string $val A string containing a possible URL
434
 * @return string $val The string with any possible URLs sanitized
435
 */
436
function fix_possible_url($val)
437
{
438
	global $modSettings, $context, $scripturl;
439
440
	if (substr($val, 0, strlen($scripturl)) != $scripturl)
441
		return $val;
442
443
	call_integration_hook('integrate_fix_url', array(&$val));
444
445
	if (empty($modSettings['queryless_urls']) || ($context['server']['is_cgi'] && ini_get('cgi.fix_pathinfo') == 0 && @get_cfg_var('cgi.fix_pathinfo') == 0) || (!$context['server']['is_apache'] && !$context['server']['is_lighttpd']))
446
		return $val;
447
448
	$val = preg_replace_callback('~\b' . preg_quote($scripturl, '~') . '\?((?:board|topic)=[^#"]+)(#[^"]*)?$~', function($m) use ($scripturl)
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $m. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
449
		{
450
			return $scripturl . '/' . strtr("$m[1]", '&;=', '//,') . '.html' . (isset($m[2]) ? $m[2] : "");
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $m instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
Coding Style Comprehensibility introduced by
The string literal does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
451
		}, $val);
452
	return $val;
453
}
454
455
/**
456
 * Ensures supplied data is properly encapsulated in cdata xml tags
457
 * Called from getXmlProfile in News.php
458
 *
459
 * @param string $data XML data
460
 * @param string $ns A namespace prefix for the XML data elements (used by mods, maybe)
461
 * @param boolean $force If true, enclose the XML data in cdata tags no matter what (used by mods, maybe)
462
 * @return string The XML data enclosed in cdata tags when necessary
463
 */
464
function cdata_parse($data, $ns = '', $force = false)
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $ns. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
465
{
466
	global $smcFunc;
467
468
	// Do we even need to do this?
469
	if (strpbrk($data, '<>&') == false && $force !== true)
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing strpbrk($data, '<>&') of type string to the boolean false. If you are specifically checking for an empty string, consider using the more explicit === '' instead.
Loading history...
470
		return $data;
471
472
	$cdata = '<![CDATA[';
473
474
	for ($pos = 0, $n = $smcFunc['strlen']($data); $pos < $n; null)
475
	{
476
		$positions = array(
477
			$smcFunc['strpos']($data, '&', $pos),
478
			$smcFunc['strpos']($data, ']', $pos),
479
		);
480
		if ($ns != '')
481
			$positions[] = $smcFunc['strpos']($data, '<', $pos);
482
		foreach ($positions as $k => $dummy)
483
		{
484
			if ($dummy === false)
485
				unset($positions[$k]);
486
		}
487
488
		$old = $pos;
489
		$pos = empty($positions) ? $n : min($positions);
490
491
		if ($pos - $old > 0)
492
			$cdata .= $smcFunc['substr']($data, $old, $pos - $old);
493
		if ($pos >= $n)
494
			break;
495
496
		if ($smcFunc['substr']($data, $pos, 1) == '<')
497
		{
498
			$pos2 = $smcFunc['strpos']($data, '>', $pos);
499
			if ($pos2 === false)
500
				$pos2 = $n;
501
			if ($smcFunc['substr']($data, $pos + 1, 1) == '/')
502
				$cdata .= ']]></' . $ns . ':' . $smcFunc['substr']($data, $pos + 2, $pos2 - $pos - 1) . '<![CDATA[';
503
			else
504
				$cdata .= ']]><' . $ns . ':' . $smcFunc['substr']($data, $pos + 1, $pos2 - $pos) . '<![CDATA[';
505
			$pos = $pos2 + 1;
506
		}
507
		elseif ($smcFunc['substr']($data, $pos, 1) == ']')
508
		{
509
			$cdata .= ']]>&#093;<![CDATA[';
510
			$pos++;
511
		}
512
		elseif ($smcFunc['substr']($data, $pos, 1) == '&')
513
		{
514
			$pos2 = $smcFunc['strpos']($data, ';', $pos);
515
			if ($pos2 === false)
516
				$pos2 = $n;
517
			$ent = $smcFunc['substr']($data, $pos + 1, $pos2 - $pos - 1);
518
519
			if ($smcFunc['substr']($data, $pos + 1, 1) == '#')
520
				$cdata .= ']]>' . $smcFunc['substr']($data, $pos, $pos2 - $pos + 1) . '<![CDATA[';
521
			elseif (in_array($ent, array('amp', 'lt', 'gt', 'quot')))
522
				$cdata .= ']]>' . $smcFunc['substr']($data, $pos, $pos2 - $pos + 1) . '<![CDATA[';
523
524
			$pos = $pos2 + 1;
525
		}
526
	}
527
528
	$cdata .= ']]>';
529
530
	return strtr($cdata, array('<![CDATA[]]>' => ''));
531
}
532
533
/**
534
 * Formats data retrieved in other functions into xml format.
535
 * Additionally formats data based on the specific format passed.
536
 * This function is recursively called to handle sub arrays of data.
537
 *
538
 * @param array $data The array to output as xml data
539
 * @param int $i The amount of indentation to use.
540
 * @param string $xml_format The format to use ('atom', 'rss', 'rss2' or empty for plain XML)
541
 * @param array $forceCdataKeys A list of keys on which to force cdata wrapping (used by mods, maybe)
542
 * @param array $nsKeys Key-value pairs of namespace prefixes to pass to cdata_parse() (used by mods, maybe)
543
 */
544
function dumpTags($data, $i, $tag = null, $xml_format = '', $forceCdataKeys = array(), $nsKeys = array())
545
{
546
	// Wrap the values of these keys into CDATA tags
547
	$keysToCdata = array(
548
		'title',
549
		'name',
550
		'description',
551
		'summary',
552
		'subject',
553
		'body',
554
		'username',
555
		'signature',
556
		'position',
557
		'language',
558
		'gender',
559
		'blurb',
560
	);
561
	if ($xml_format != 'atom')
562
		$keysToCdata[] = 'category';
563
564
	if (!empty($forceCdataKeys))
565
	{
566
		$keysToCdata = array_merge($keysToCdata, $forceCdataKeys);
567
		$keysToCdata = array_unique($keysToCdata);
568
	}
569
570
	// For every array in the data...
571
	foreach ($data as $element)
572
	{
573
		// If a tag was passed, use it instead of the key.
574
		$key = isset($tag) ? $tag : (isset($element['tag']) ? $element['tag'] : null);
575
		$val = isset($element['content']) ? $element['content'] : null;
576
		$attrs = isset($element['attributes']) ? $element['attributes'] : null;
577
578
		// Skip it, it's been set to null.
579
		if ($key === null || ($val === null && $attrs === null))
580
			continue;
581
582
		$forceCdata = in_array($key, $forceCdataKeys);
583
		$ns = !empty($nsKeys[$key]) ? $nsKeys[$key] : '';
584
585
		// First let's indent!
586
		echo "\n", str_repeat("\t", $i);
587
588
		// Beginning tag.
589
		echo '<', $key;
590
591
		if (!empty($attrs))
592
		{
593
			foreach ($attrs as $attr_key => $attr_value)
594
				echo ' ', $attr_key, '="', fix_possible_url($attr_value), '"';
595
		}
596
597
		// If it's empty, simply output an empty element.
598
		if (empty($val))
599
		{
600
			echo ' />';
601
		}
602
		else
603
		{
604
			echo '>';
605
606
			// The element's value.
607
			if (is_array($val))
608
			{
609
				// An array.  Dump it, and then indent the tag.
610
				dumpTags($val, $i + 1, null, $xml_format, $forceCdataKeys, $nsKeys);
611
				echo "\n", str_repeat("\t", $i);
612
			}
613
			// A string with returns in it.... show this as a multiline element.
614
			elseif (strpos($val, "\n") !== false)
615
				echo "\n", in_array($key, $keysToCdata) ? cdata_parse(fix_possible_url($val), $ns, $forceCdata) : fix_possible_url($val), "\n", str_repeat("\t", $i);
616
			// A simple string.
617
			else
618
				echo in_array($key, $keysToCdata) ? cdata_parse(fix_possible_url($val), $ns, $forceCdata) : fix_possible_url($val);
619
620
			// Ending tag.
621
			echo '</', $key, '>';
622
		}
623
	}
624
}
625
626
/**
627
 * Retrieve the list of members from database.
628
 * The array will be generated to match the format.
629
 * @todo get the list of members from Subs-Members.
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
630
 *
631
 * @param string $xml_format The format to use. Can be 'atom', 'rdf', 'rss', 'rss2' or 'smf'
632
 * @return array An array of arrays of feed items. Each array has keys corresponding to the appropriate tags for the specified format.
633
 */
634
function getXmlMembers($xml_format)
635
{
636
	global $scripturl, $smcFunc;
637
638
	if (!allowedTo('view_mlist'))
639
		return array();
640
641
	// Find the most recent members.
642
	$request = $smcFunc['db_query']('', '
643
		SELECT id_member, member_name, real_name, date_registered, last_login
644
		FROM {db_prefix}members
645
		ORDER BY id_member DESC
646
		LIMIT {int:limit}',
647
		array(
648
			'limit' => $_GET['limit'],
649
		)
650
	);
651
	$data = array();
652
	while ($row = $smcFunc['db_fetch_assoc']($request))
653
	{
654
		// Create a GUID for each member using the tag URI scheme
655
		$guid = 'tag:' . parse_url($scripturl, PHP_URL_HOST) . ',' . gmdate('Y-m-d', $row['date_registered']) . ':member=' . $row['id_member'];
656
657
		// Make the data look rss-ish.
658
		if ($xml_format == 'rss' || $xml_format == 'rss2')
659
			$data[] = array(
660
				'tag' => 'item',
661
				'content' => array(
662
					array(
663
						'tag' => 'title',
664
						'content' => $row['real_name'],
665
					),
666
					array(
667
						'tag' => 'link',
668
						'content' => $scripturl . '?action=profile;u=' . $row['id_member'],
669
					),
670
					array(
671
						'tag' => 'comments',
672
						'content' => $scripturl . '?action=pm;sa=send;u=' . $row['id_member'],
673
					),
674
					array(
675
						'tag' => 'pubDate',
676
						'content' => gmdate('D, d M Y H:i:s \G\M\T', $row['date_registered']),
677
					),
678
					array(
679
						'tag' => 'guid',
680
						'content' => $guid,
681
						'attributes' => array(
682
							'isPermaLink' => 'false',
683
						),
684
					),
685
				),
686
			);
687
		elseif ($xml_format == 'rdf')
688
			$data[] = array(
689
				'tag' => 'item',
690
				'attributes' => array('rdf:about' => $scripturl . '?action=profile;u=' . $row['id_member']),
691
				'content' => array(
692
					array(
693
						'tag' => 'dc:format',
694
						'content' => 'text/html',
695
					),
696
					array(
697
						'tag' => 'title',
698
						'content' => $row['real_name'],
699
					),
700
					array(
701
						'tag' => 'link',
702
						'content' => $scripturl . '?action=profile;u=' . $row['id_member'],
703
					),
704
				),
705
			);
706
		elseif ($xml_format == 'atom')
707
			$data[] = array(
708
				'tag' => 'entry',
709
				'content' => array(
710
					array(
711
						'tag' => 'title',
712
						'content' => $row['real_name'],
713
					),
714
					array(
715
						'tag' => 'link',
716
						'attributes' => array(
717
							'rel' => 'alternate',
718
							'type' => 'text/html',
719
							'href' => $scripturl . '?action=profile;u=' . $row['id_member'],
720
						),
721
					),
722
					array(
723
						'tag' => 'published',
724
						'content' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $row['date_registered']),
725
					),
726
					array(
727
						'tag' => 'updated',
728
						'content' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $row['last_login']),
729
					),
730
					array(
731
						'tag' => 'id',
732
						'content' => $guid,
733
					),
734
				),
735
			);
736
		// More logical format for the data, but harder to apply.
737
		else
738
			$data[] = array(
739
				'tag' => 'member',
740
				'content' => array(
741
					array(
742
						'tag' => 'name',
743
						'content' => $row['real_name'],
744
					),
745
					array(
746
						'tag' => 'time',
747
						'content' => $smcFunc['htmlspecialchars'](strip_tags(timeformat($row['date_registered']))),
748
					),
749
					array(
750
						'tag' => 'id',
751
						'content' => $row['id_member'],
752
					),
753
					array(
754
						'tag' => 'link',
755
						'content' => $scripturl . '?action=profile;u=' . $row['id_member'],
756
					),
757
				),
758
			);
759
	}
760
	$smcFunc['db_free_result']($request);
761
762
	return $data;
763
}
764
765
/**
766
 * Get the latest topics information from a specific board,
767
 * to display later.
768
 * The returned array will be generated to match the xml_format.
769
 * @todo does not belong here
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
770
 *
771
 * @param string $xml_format The XML format. Can be 'atom', 'rdf', 'rss', 'rss2' or 'smf'.
772
 * @return array An array of arrays of topic data for the feed. Each array has keys corresponding to the tags for the specified format.
773
 */
774
function getXmlNews($xml_format)
775
{
776
	global $scripturl, $modSettings, $board, $user_info;
777
	global $query_this_board, $smcFunc, $context, $txt;
778
779
	/* Find the latest posts that:
780
		- are the first post in their topic.
781
		- are on an any board OR in a specified board.
782
		- can be seen by this user.
783
		- are actually the latest posts. */
784
785
	$done = false;
786
	$loops = 0;
787
	while (!$done)
788
	{
789
		$optimize_msg = implode(' AND ', $context['optimize_msg']);
790
		$request = $smcFunc['db_query']('', '
791
			SELECT
792
				m.smileys_enabled, m.poster_time, m.id_msg, m.subject, m.body, m.modified_time,
793
				m.icon, t.id_topic, t.id_board, t.num_replies,
794
				b.name AS bname,
795
				COALESCE(mem.id_member, 0) AS id_member,
796
				COALESCE(mem.email_address, m.poster_email) AS poster_email,
797
				COALESCE(mem.real_name, m.poster_name) AS poster_name
798
			FROM {db_prefix}topics AS t
799
				INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
800
				INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
801
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
802
			WHERE ' . $query_this_board . (empty($optimize_msg) ? '' : '
803
				AND {raw:optimize_msg}') . (empty($board) ? '' : '
804
				AND t.id_board = {int:current_board}') . ($modSettings['postmod_active'] ? '
805
				AND t.approved = {int:is_approved}' : '') . '
806
			ORDER BY t.id_first_msg DESC
807
			LIMIT {int:limit}',
808
			array(
809
				'current_board' => $board,
810
				'is_approved' => 1,
811
				'limit' => $_GET['limit'],
812
				'optimize_msg' => $optimize_msg,
813
			)
814
		);
815
		// If we don't have $_GET['limit'] results, try again with an unoptimized version covering all rows.
816
		if ($loops < 2 && $smcFunc['db_num_rows']($request) < $_GET['limit'])
817
		{
818
			$smcFunc['db_free_result']($request);
819 View Code Duplication
			if (empty($_REQUEST['boards']) && empty($board))
820
				unset($context['optimize_msg']['lowest']);
821
			else
822
				$context['optimize_msg']['lowest'] = 'm.id_msg >= t.id_first_msg';
823
			$context['optimize_msg']['highest'] = 'm.id_msg <= t.id_last_msg';
824
			$loops++;
825
		}
826
		else
827
			$done = true;
828
	}
829
	$data = array();
830
	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...
831
	{
832
		// Limit the length of the message, if the option is set.
833 View Code Duplication
		if (!empty($modSettings['xmlnews_maxlen']) && $smcFunc['strlen'](str_replace('<br>', "\n", $row['body'])) > $modSettings['xmlnews_maxlen'])
834
			$row['body'] = strtr($smcFunc['substr'](str_replace('<br>', "\n", $row['body']), 0, $modSettings['xmlnews_maxlen'] - 3), array("\n" => '<br>')) . '...';
835
836
		$row['body'] = parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']);
837
838
		censorText($row['body']);
839
		censorText($row['subject']);
840
841
		// Do we want to include any attachments?
842 View Code Duplication
		if (!empty($modSettings['attachmentEnable']) && !empty($modSettings['xmlnews_attachments']) && allowedTo('view_attachments', $row['id_board']))
843
		{
844
			$attach_request = $smcFunc['db_query']('', '
845
				SELECT
846
					a.id_attach, a.filename, COALESCE(a.size, 0) AS filesize, a.mime_type, a.downloads, a.approved, m.id_topic AS topic
847
				FROM {db_prefix}attachments AS a
848
					LEFT JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg)
849
				WHERE a.attachment_type = {int:attachment_type}
850
					AND a.id_msg = {int:message_id}',
851
				array(
852
					'message_id' => $row['id_msg'],
853
					'attachment_type' => 0,
854
					'is_approved' => 1,
855
				)
856
			);
857
			$loaded_attachments = array();
858
			while ($attach = $smcFunc['db_fetch_assoc']($attach_request))
859
			{
860
				// Include approved attachments only
861
				if ($attach['approved'])
862
					$loaded_attachments['attachment_' . $attach['id_attach']] = $attach;
863
			}
864
			$smcFunc['db_free_result']($attach_request);
865
866
			// Sort the attachments by size to make things easier below
867
			if (!empty($loaded_attachments))
868
			{
869
				uasort($loaded_attachments, function($a, $b) {
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $a. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
Comprehensibility introduced by
Avoid variables with short names like $b. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
870
					if ($a['filesize'] == $b['filesize'])
871
					        return 0;
872
					return ($a['filesize'] < $b['filesize']) ? -1 : 1;
873
				});
874
			}
875
			else
876
				$loaded_attachments = null;
877
		}
878
		else
879
			$loaded_attachments = null;
880
881
		// Create a GUID for this topic using the tag URI scheme
882
		$guid = 'tag:' . parse_url($scripturl, PHP_URL_HOST) . ',' . gmdate('Y-m-d', $row['poster_time']) . ':topic=' . $row['id_topic'];
883
884
		// Being news, this actually makes sense in rss format.
885
		if ($xml_format == 'rss' || $xml_format == 'rss2')
886
		{
887
			// Only one attachment allowed in RSS.
888 View Code Duplication
			if ($loaded_attachments !== null)
889
			{
890
				$attachment = array_pop($loaded_attachments);
891
				$enclosure = array(
892
					'url' => fix_possible_url($scripturl . '?action=dlattach;topic=' . $attachment['topic'] . '.0;attach=' . $attachment['id_attach']),
893
					'length' => $attachment['filesize'],
894
					'type' => $attachment['mime_type'],
895
				);
896
			}
897
			else
898
				$enclosure = null;
899
900
			$data[] = array(
901
				'tag' => 'item',
902
				'content' => array(
903
					array(
904
						'tag' => 'title',
905
						'content' => $row['subject'],
906
					),
907
					array(
908
						'tag' => 'link',
909
						'content' => $scripturl . '?topic=' . $row['id_topic'] . '.0',
910
					),
911
					array(
912
						'tag' => 'description',
913
						'content' => $row['body'],
914
					),
915
					array(
916
						'tag' => 'author',
917
						'content' => (allowedTo('moderate_forum') || $row['id_member'] == $user_info['id']) ? $row['poster_email'] . ' (' . $row['poster_name'] . ')' : null,
918
					),
919
					array(
920
						'tag' => 'comments',
921
						'content' => $scripturl . '?action=post;topic=' . $row['id_topic'] . '.0',
922
					),
923
					array(
924
						'tag' => 'category',
925
						'content' => $row['bname'],
926
					),
927
					array(
928
						'tag' => 'pubDate',
929
						'content' => gmdate('D, d M Y H:i:s \G\M\T', $row['poster_time']),
930
					),
931
					array(
932
						'tag' => 'guid',
933
						'content' => $guid,
934
						'attributes' => array(
935
							'isPermaLink' => 'false',
936
						),
937
					),
938
					array(
939
						'tag' => 'enclosure',
940
						'attributes' => $enclosure,
941
					),
942
				),
943
			);
944
		}
945
		elseif ($xml_format == 'rdf')
946
		{
947
			$data[] = array(
948
				'tag' => 'item',
949
				'attributes' => array('rdf:about' => $scripturl . '?topic=' . $row['id_topic'] . '.0'),
950
				'content' => array(
951
					array(
952
						'tag' => 'dc:format',
953
						'content' => 'text/html',
954
					),
955
					array(
956
						'tag' => 'title',
957
						'content' => $row['subject'],
958
					),
959
					array(
960
						'tag' => 'link',
961
						'content' => $scripturl . '?topic=' . $row['id_topic'] . '.0',
962
					),
963
					array(
964
						'tag' => 'description',
965
						'content' => $row['body'],
966
					),
967
				),
968
			);
969
		}
970
		elseif ($xml_format == 'atom')
971
		{
972
			// Only one attachment allowed
973 View Code Duplication
			if (!empty($loaded_attachments))
974
			{
975
				$attachment = array_pop($loaded_attachments);
976
				$enclosure = array(
977
					'rel' => 'enclosure',
978
					'href' => fix_possible_url($scripturl . '?action=dlattach;topic=' . $attachment['topic'] . '.0;attach=' . $attachment['id_attach']),
979
					'length' => $attachment['filesize'],
980
					'type' => $attachment['mime_type'],
981
				);
982
			}
983
			else
984
				$enclosure = null;
985
986
			$data[] = array(
987
				'tag' => 'entry',
988
				'content' => array(
989
					array(
990
						'tag' => 'title',
991
						'content' => $row['subject'],
992
					),
993
					array(
994
						'tag' => 'link',
995
						'attributes' => array(
996
							'rel' => 'alternate',
997
							'type' => 'text/html',
998
							'href' => $scripturl . '?topic=' . $row['id_topic'] . '.0',
999
						),
1000
					),
1001
					array(
1002
						'tag' => 'summary',
1003
						'attributes' => array('type' => 'html'),
1004
						'content' => $row['body'],
1005
					),
1006
					array(
1007
						'tag' => 'category',
1008
						'attributes' => array('term' => $row['bname']),
1009
					),
1010
					array(
1011
						'tag' => 'author',
1012
						'content' => array(
1013
							array(
1014
								'tag' => 'name',
1015
								'content' => $row['poster_name'],
1016
							),
1017
							array(
1018
								'tag' => 'email',
1019
								'content' => (allowedTo('moderate_forum') || $row['id_member'] == $user_info['id']) ? $row['poster_email'] : null,
1020
							),
1021
							array(
1022
								'tag' => 'uri',
1023
								'content' => !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : null,
1024
							),
1025
						)
1026
					),
1027
					array(
1028
						'tag' => 'published',
1029
						'content' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $row['poster_time']),
1030
					),
1031
					array(
1032
						'tag' => 'updated',
1033
						'content' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', empty($row['modified_time']) ? $row['poster_time'] : $row['modified_time']),
1034
					),
1035
					array(
1036
						'tag' => 'id',
1037
						'content' => $guid,
1038
					),
1039
					array(
1040
						'tag' => 'link',
1041
						'attributes' => $enclosure,
1042
					),
1043
				),
1044
			);
1045
		}
1046
		// The biggest difference here is more information.
1047
		else
1048
		{
1049
			$attachments = array();
1050 View Code Duplication
			if (!empty($loaded_attachments))
1051
			{
1052
				foreach ($loaded_attachments as $attachment)
1053
				{
1054
					$attachments[] = array(
1055
						'tag' => 'attachment',
1056
						'content' => array(
1057
							array(
1058
								'tag' => 'id',
1059
								'content' => $attachment['id_attach'],
1060
							),
1061
							array(
1062
								'tag' => 'name',
1063
								'content' => preg_replace('~&amp;#(\\d{1,7}|x[0-9a-fA-F]{1,6});~', '&#\\1;', $smcFunc['htmlspecialchars']($attachment['filename'])),
1064
							),
1065
							array(
1066
								'tag' => 'downloads',
1067
								'content' => $attachment['downloads'],
1068
							),
1069
							array(
1070
								'tag' => 'size',
1071
								'content' => ($attachment['filesize'] < 1024000) ? round($attachment['filesize'] / 1024, 2) . ' ' . $txt['kilobyte'] : round($attachment['filesize'] / 1024 / 1024, 2) . ' ' . $txt['megabyte'],
1072
							),
1073
							array(
1074
								'tag' => 'byte_size',
1075
								'content' => $attachment['filesize'],
1076
							),
1077
							array(
1078
								'tag' => 'link',
1079
								'content' => $scripturl . '?action=dlattach;topic=' . $attachment['topic'] . '.0;attach=' . $attachment['id_attach'],
1080
							),
1081
						)
1082
					);
1083
				}
1084
			}
1085
			else
1086
				$attachments = null;
1087
1088
			$data[] = array(
1089
				'tag' => 'article',
1090
				'content' => array(
1091
					array(
1092
						'tag' => 'time',
1093
						'content' => $smcFunc['htmlspecialchars'](strip_tags(timeformat($row['poster_time']))),
1094
					),
1095
					array(
1096
						'tag' => 'id',
1097
						'content' => $row['id_topic'],
1098
					),
1099
					array(
1100
						'tag' => 'subject',
1101
						'content' => $row['subject'],
1102
					),
1103
					array(
1104
						'tag' => 'body',
1105
						'content' => $row['body'],
1106
					),
1107
					array(
1108
						'tag' => 'poster',
1109
						'content' => array(
1110
							array(
1111
								'tag' => 'name',
1112
								'content' => $row['poster_name'],
1113
							),
1114
							array(
1115
								'tag' => 'id',
1116
								'content' => $row['id_member'],
1117
							),
1118
							array(
1119
								'tag' => 'link',
1120
								'content' => !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : '',
1121
							),
1122
						)
1123
					),
1124
					array(
1125
						'tag' => 'topic',
1126
						'content' => $row['id_topic'],
1127
					),
1128
					array(
1129
						'tag' => 'board',
1130
						'content' => array(
1131
							array(
1132
								'tag' => 'name',
1133
								'content' => $row['bname'],
1134
							),
1135
							array(
1136
								'tag' => 'id',
1137
								'content' => $row['id_board'],
1138
							),
1139
							array(
1140
								'tag' => 'link',
1141
								'content' => $scripturl . '?board=' . $row['id_board'] . '.0',
1142
							),
1143
						),
1144
					),
1145
					array(
1146
						'tag' => 'link',
1147
						'content' => $scripturl . '?topic=' . $row['id_topic'] . '.0',
1148
					),
1149
					array(
1150
						'tag' => 'attachments',
1151
						'content' => $attachments,
1152
					),
1153
				),
1154
			);
1155
		}
1156
	}
1157
	$smcFunc['db_free_result']($request);
1158
1159
	return $data;
1160
}
1161
1162
/**
1163
 * Get the recent topics to display.
1164
 * The returned array will be generated to match the xml_format.
1165
 * @todo does not belong here.
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
1166
 *
1167
 * @param string $xml_format The XML format. Can be 'atom', 'rdf', 'rss', 'rss2' or 'smf'
1168
 * @return array An array of arrays containing data for the feed. Each array has keys corresponding to the appropriate tags for the specified format.
1169
 */
1170
function getXmlRecent($xml_format)
1171
{
1172
	global $scripturl, $modSettings, $board, $txt;
1173
	global $query_this_board, $smcFunc, $context, $user_info, $sourcedir;
1174
1175
	require_once($sourcedir . '/Subs-Attachments.php');
1176
1177
	$done = false;
1178
	$loops = 0;
1179
	while (!$done)
1180
	{
1181
		$optimize_msg = implode(' AND ', $context['optimize_msg']);
1182
		$request = $smcFunc['db_query']('', '
1183
			SELECT m.id_msg
1184
			FROM {db_prefix}messages AS m
1185
				INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
1186
				INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
1187
			WHERE ' . $query_this_board . (empty($optimize_msg) ? '' : '
1188
				AND {raw:optimize_msg}') . (empty($board) ? '' : '
1189
				AND m.id_board = {int:current_board}') . ($modSettings['postmod_active'] ? '
1190
				AND m.approved = {int:is_approved}' : '') . '
1191
			ORDER BY m.id_msg DESC
1192
			LIMIT {int:limit}',
1193
			array(
1194
				'limit' => $_GET['limit'],
1195
				'current_board' => $board,
1196
				'is_approved' => 1,
1197
				'optimize_msg' => $optimize_msg,
1198
			)
1199
		);
1200
		// If we don't have $_GET['limit'] results, try again with an unoptimized version covering all rows.
1201
		if ($loops < 2 && $smcFunc['db_num_rows']($request) < $_GET['limit'])
1202
		{
1203
			$smcFunc['db_free_result']($request);
1204 View Code Duplication
			if (empty($_REQUEST['boards']) && empty($board))
1205
				unset($context['optimize_msg']['lowest']);
1206
			else
1207
				$context['optimize_msg']['lowest'] = $loops ? 'm.id_msg >= t.id_first_msg' : 'm.id_msg >= (t.id_last_msg - t.id_first_msg) / 2';
1208
			$loops++;
1209
		}
1210
		else
1211
			$done = true;
1212
	}
1213
	$messages = array();
1214
	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...
1215
		$messages[] = $row['id_msg'];
1216
	$smcFunc['db_free_result']($request);
1217
1218
	if (empty($messages))
1219
		return array();
1220
1221
	// Find the most recent posts this user can see.
1222
	$request = $smcFunc['db_query']('', '
1223
		SELECT
1224
			m.smileys_enabled, m.poster_time, m.id_msg, m.subject, m.body, m.id_topic, t.id_board,
1225
			b.name AS bname, t.num_replies, m.id_member, m.icon, mf.id_member AS id_first_member,
1226
			COALESCE(mem.real_name, m.poster_name) AS poster_name, mf.subject AS first_subject,
1227
			COALESCE(memf.real_name, mf.poster_name) AS first_poster_name,
1228
			COALESCE(mem.email_address, m.poster_email) AS poster_email, m.modified_time
1229
		FROM {db_prefix}messages AS m
1230
			INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
1231
			INNER JOIN {db_prefix}messages AS mf ON (mf.id_msg = t.id_first_msg)
1232
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
1233
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
1234
			LEFT JOIN {db_prefix}members AS memf ON (memf.id_member = mf.id_member)
1235
		WHERE m.id_msg IN ({array_int:message_list})
1236
			' . (empty($board) ? '' : 'AND t.id_board = {int:current_board}') . '
1237
		ORDER BY m.id_msg DESC
1238
		LIMIT {int:limit}',
1239
		array(
1240
			'limit' => $_GET['limit'],
1241
			'current_board' => $board,
1242
			'message_list' => $messages,
1243
		)
1244
	);
1245
	$data = array();
1246
	while ($row = $smcFunc['db_fetch_assoc']($request))
1247
	{
1248
		// Limit the length of the message, if the option is set.
1249 View Code Duplication
		if (!empty($modSettings['xmlnews_maxlen']) && $smcFunc['strlen'](str_replace('<br>', "\n", $row['body'])) > $modSettings['xmlnews_maxlen'])
1250
			$row['body'] = strtr($smcFunc['substr'](str_replace('<br>', "\n", $row['body']), 0, $modSettings['xmlnews_maxlen'] - 3), array("\n" => '<br>')) . '...';
1251
1252
		$row['body'] = parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']);
1253
1254
		censorText($row['body']);
1255
		censorText($row['subject']);
1256
1257
		// Do we want to include any attachments?
1258 View Code Duplication
		if (!empty($modSettings['attachmentEnable']) && !empty($modSettings['xmlnews_attachments']) && allowedTo('view_attachments', $row['id_board']))
1259
		{
1260
			$attach_request = $smcFunc['db_query']('', '
1261
				SELECT
1262
					a.id_attach, a.filename, COALESCE(a.size, 0) AS filesize, a.mime_type, a.downloads, a.approved, m.id_topic AS topic
1263
				FROM {db_prefix}attachments AS a
1264
					LEFT JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg)
1265
				WHERE a.attachment_type = {int:attachment_type}
1266
					AND a.id_msg = {int:message_id}',
1267
				array(
1268
					'message_id' => $row['id_msg'],
1269
					'attachment_type' => 0,
1270
					'is_approved' => 1,
1271
				)
1272
			);
1273
			$loaded_attachments = array();
1274
			while ($attach = $smcFunc['db_fetch_assoc']($attach_request))
1275
			{
1276
				// Include approved attachments only
1277
				if ($attach['approved'])
1278
					$loaded_attachments['attachment_' . $attach['id_attach']] = $attach;
1279
			}
1280
			$smcFunc['db_free_result']($attach_request);
1281
1282
			// Sort the attachments by size to make things easier below
1283
			if (!empty($loaded_attachments))
1284
			{
1285
				uasort($loaded_attachments, function($a, $b) {
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $a. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
Comprehensibility introduced by
Avoid variables with short names like $b. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
1286
					if ($a['filesize'] == $b['filesize'])
1287
					        return 0;
1288
					return ($a['filesize'] < $b['filesize']) ? -1 : 1;
1289
				});
1290
			}
1291
			else
1292
				$loaded_attachments = null;
1293
		}
1294
		else
1295
			$loaded_attachments = null;
1296
1297
		// Create a GUID for this post using the tag URI scheme
1298
		$guid = 'tag:' . parse_url($scripturl, PHP_URL_HOST) . ',' . gmdate('Y-m-d', $row['poster_time']) . ':msg=' . $row['id_msg'];
1299
1300
		// Doesn't work as well as news, but it kinda does..
1301
		if ($xml_format == 'rss' || $xml_format == 'rss2')
1302
		{
1303
			// Only one attachment allowed in RSS.
1304 View Code Duplication
			if ($loaded_attachments !== null)
1305
			{
1306
				$attachment = array_pop($loaded_attachments);
1307
				$enclosure = array(
1308
					'url' => fix_possible_url($scripturl . '?action=dlattach;topic=' . $attachment['topic'] . '.0;attach=' . $attachment['id_attach']),
1309
					'length' => $attachment['filesize'],
1310
					'type' => $attachment['mime_type'],
1311
				);
1312
			}
1313
			else
1314
				$enclosure = null;
1315
1316
			$data[] = array(
1317
				'tag' => 'item',
1318
				'content' => array(
1319
					array(
1320
						'tag' => 'title',
1321
						'content' => $row['subject'],
1322
					),
1323
					array(
1324
						'tag' => 'link',
1325
						'content' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'],
1326
					),
1327
					array(
1328
						'tag' => 'description',
1329
						'content' => $row['body'],
1330
					),
1331
					array(
1332
						'tag' => 'author',
1333
						'content' => (allowedTo('moderate_forum') || (!empty($row['id_member']) && $row['id_member'] == $user_info['id'])) ? $row['poster_email'] : null,
1334
					),
1335
					array(
1336
						'tag' => 'category',
1337
						'content' => $row['bname'],
1338
					),
1339
					array(
1340
						'tag' => 'comments',
1341
						'content' => $scripturl . '?action=post;topic=' . $row['id_topic'] . '.0',
1342
					),
1343
					array(
1344
						'tag' => 'pubDate',
1345
						'content' => gmdate('D, d M Y H:i:s \G\M\T', $row['poster_time']),
1346
					),
1347
					array(
1348
						'tag' => 'guid',
1349
						'content' => $guid,
1350
						'attributes' => array(
1351
							'isPermaLink' => 'false',
1352
						),
1353
					),
1354
					array(
1355
						'tag' => 'enclosure',
1356
						'attributes' => $enclosure,
1357
					),
1358
				),
1359
			);
1360
		}
1361
		elseif ($xml_format == 'rdf')
1362
		{
1363
			$data[] = array(
1364
				'tag' => 'item',
1365
				'attributes' => array('rdf:about' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg']),
1366
				'content' => array(
1367
					array(
1368
						'tag' => 'dc:format',
1369
						'content' => 'text/html',
1370
					),
1371
					array(
1372
						'tag' => 'title',
1373
						'content' => $row['subject'],
1374
					),
1375
					array(
1376
						'tag' => 'link',
1377
						'content' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'],
1378
					),
1379
					array(
1380
						'tag' => 'description',
1381
						'content' => $row['body'],
1382
					),
1383
				),
1384
			);
1385
		}
1386
		elseif ($xml_format == 'atom')
1387
		{
1388
			// Only one attachment allowed
1389 View Code Duplication
			if (!empty($loaded_attachments))
1390
			{
1391
				$attachment = array_pop($loaded_attachments);
1392
				$enclosure = array(
1393
					'rel' => 'enclosure',
1394
					'href' => fix_possible_url($scripturl . '?action=dlattach;topic=' . $attachment['topic'] . '.0;attach=' . $attachment['id_attach']),
1395
					'length' => $attachment['filesize'],
1396
					'type' => $attachment['mime_type'],
1397
				);
1398
			}
1399
			else
1400
				$enclosure = null;
1401
1402
			$data[] = array(
1403
				'tag' => 'entry',
1404
				'content' => array(
1405
					array(
1406
						'tag' => 'title',
1407
						'content' => $row['subject'],
1408
					),
1409
					array(
1410
						'tag' => 'link',
1411
						'attributes' => array(
1412
							'rel' => 'alternate',
1413
							'type' => 'text/html',
1414
							'href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'],
1415
						),
1416
					),
1417
					array(
1418
						'tag' => 'summary',
1419
						'attributes' => array('type' => 'html'),
1420
						'content' => $row['body'],
1421
					),
1422
					array(
1423
						'tag' => 'category',
1424
						'attributes' => array('term' => $row['bname']),
1425
					),
1426
					array(
1427
						'tag' => 'author',
1428
						'content' => array(
1429
							array(
1430
								'tag' => 'name',
1431
								'content' => $row['poster_name'],
1432
							),
1433
							array(
1434
								'tag' => 'email',
1435
								'content' => (allowedTo('moderate_forum') || (!empty($row['id_member']) && $row['id_member'] == $user_info['id'])) ? $row['poster_email'] : null,
1436
							),
1437
							array(
1438
								'tag' => 'uri',
1439
								'content' => !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : null,
1440
							),
1441
						),
1442
					),
1443
					array(
1444
						'tag' => 'published',
1445
						'content' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $row['poster_time']),
1446
					),
1447
					array(
1448
						'tag' => 'updated',
1449
						'content' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', empty($row['modified_time']) ? $row['poster_time'] : $row['modified_time']),
1450
					),
1451
					array(
1452
						'tag' => 'id',
1453
						'content' => $guid,
1454
					),
1455
					array(
1456
						'tag' => 'link',
1457
						'attributes' => $enclosure,
1458
					),
1459
				),
1460
			);
1461
		}
1462
		// A lot of information here.  Should be enough to please the rss-ers.
1463
		else
1464
		{
1465
			$attachments = array();
1466 View Code Duplication
			if (!empty($loaded_attachments))
1467
			{
1468
				foreach ($loaded_attachments as $attachment)
1469
				{
1470
					$attachments[] = array(
1471
						'tag' => 'attachment',
1472
						'content' => array(
1473
							array(
1474
								'tag' => 'id',
1475
								'content' => $attachment['id_attach'],
1476
							),
1477
							array(
1478
								'tag' => 'name',
1479
								'content' => preg_replace('~&amp;#(\\d{1,7}|x[0-9a-fA-F]{1,6});~', '&#\\1;', $smcFunc['htmlspecialchars']($attachment['filename'])),
1480
							),
1481
							array(
1482
								'tag' => 'downloads',
1483
								'content' => $attachment['downloads'],
1484
							),
1485
							array(
1486
								'tag' => 'size',
1487
								'content' => ($attachment['filesize'] < 1024000) ? round($attachment['filesize'] / 1024, 2) . ' ' . $txt['kilobyte'] : round($attachment['filesize'] / 1024 / 1024, 2) . ' ' . $txt['megabyte'],
1488
							),
1489
							array(
1490
								'tag' => 'byte_size',
1491
								'content' => $attachment['filesize'],
1492
							),
1493
							array(
1494
								'tag' => 'link',
1495
								'content' => $scripturl . '?action=dlattach;topic=' . $attachment['topic'] . '.0;attach=' . $attachment['id_attach'],
1496
							),
1497
						)
1498
					);
1499
				}
1500
			}
1501
			else
1502
				$attachments = null;
1503
1504
			$data[] = array(
1505
				'tag' => 'recent-post',
1506
				'content' => array(
1507
					array(
1508
						'tag' => 'time',
1509
						'content' => $smcFunc['htmlspecialchars'](strip_tags(timeformat($row['poster_time']))),
1510
					),
1511
					array(
1512
						'tag' => 'id',
1513
						'content' => $row['id_msg'],
1514
					),
1515
					array(
1516
						'tag' => 'subject',
1517
						'content' => $row['subject'],
1518
					),
1519
					array(
1520
						'tag' => 'body',
1521
						'content' => $row['body'],
1522
					),
1523
					array(
1524
						'tag' => 'starter',
1525
						'content' => array(
1526
							array(
1527
								'tag' => 'name',
1528
								'content' => $row['first_poster_name'],
1529
							),
1530
							array(
1531
								'tag' => 'id',
1532
								'content' => $row['id_first_member'],
1533
							),
1534
							array(
1535
								'tag' => 'link',
1536
								'content' => !empty($row['id_first_member']) ? $scripturl . '?action=profile;u=' . $row['id_first_member'] : '',
1537
							),
1538
						),
1539
					),
1540
					array(
1541
						'tag' => 'poster',
1542
						'content' => array(
1543
							array(
1544
								'tag' => 'name',
1545
								'content' => $row['poster_name'],
1546
							),
1547
							array(
1548
								'tag' => 'id',
1549
								'content' => $row['id_member'],
1550
							),
1551
							array(
1552
								'tag' => 'link',
1553
								'content' => !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : '',
1554
							),
1555
						),
1556
					),
1557
					array(
1558
						'tag' => 'topic',
1559
						'content' => array(
1560
							array(
1561
								'tag' => 'subject',
1562
								'content' => $row['first_subject'],
1563
							),
1564
							array(
1565
								'tag' => 'id',
1566
								'content' => $row['id_topic'],
1567
							),
1568
							array(
1569
								'tag' => 'link',
1570
								'content' => $scripturl . '?topic=' . $row['id_topic'] . '.new#new',
1571
							),
1572
						),
1573
					),
1574
					array(
1575
						'tag' => 'board',
1576
						'content' => array(
1577
							array(
1578
								'tag' => 'name',
1579
								'content' => $row['bname'],
1580
							),
1581
							array(
1582
								'tag' => 'id',
1583
								'content' => $row['id_board'],
1584
							),
1585
							array(
1586
								'tag' => 'link',
1587
								'content' => $scripturl . '?board=' . $row['id_board'] . '.0',
1588
							),
1589
						),
1590
					),
1591
					array(
1592
						'tag' => 'link',
1593
						'content' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'],
1594
					),
1595
					array(
1596
						'tag' => 'attachments',
1597
						'content' => $attachments,
1598
					),
1599
				),
1600
			);
1601
		}
1602
	}
1603
	$smcFunc['db_free_result']($request);
1604
1605
	return $data;
1606
}
1607
1608
/**
1609
 * Get the profile information for member into an array,
1610
 * which will be generated to match the xml_format.
1611
 * @todo refactor.
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
1612
 *
1613
 * @param string $xml_format The XML format. Can be 'atom', 'rdf', 'rss', 'rss2' or 'smf'
1614
 * @return array An array profile data
1615
 */
1616
function getXmlProfile($xml_format)
1617
{
1618
	global $scripturl, $memberContext, $user_profile, $user_info;
1619
1620
	// You must input a valid user....
1621
	if (empty($_GET['u']) || !loadMemberData((int) $_GET['u']))
1622
		return array();
1623
1624
	// Make sure the id is a number and not "I like trying to hack the database".
1625
	$_GET['u'] = (int) $_GET['u'];
1626
	// Load the member's contextual information!
1627
	if (!loadMemberContext($_GET['u']) || !allowedTo('profile_view'))
1628
		return array();
1629
1630
	// Okay, I admit it, I'm lazy.  Stupid $_GET['u'] is long and hard to type.
1631
	$profile = &$memberContext[$_GET['u']];
1632
1633
	// Create a GUID for this member using the tag URI scheme
1634
	$guid = 'tag:' . parse_url($scripturl, PHP_URL_HOST) . ',' . gmdate('Y-m-d', $user_profile[$profile['id']]['date_registered']) . ':member=' . $profile['id'];
1635
1636
	if ($xml_format == 'rss' || $xml_format == 'rss2')
1637
	{
1638
		$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...
1639
			'tag' => 'item',
1640
			'content' => array(
1641
				array(
1642
					'tag' => 'title',
1643
					'content' => $profile['name'],
1644
				),
1645
				array(
1646
					'tag' => 'link',
1647
					'content' => $scripturl . '?action=profile;u=' . $profile['id'],
1648
				),
1649
				array(
1650
					'tag' => 'description',
1651
					'content' => isset($profile['group']) ? $profile['group'] : $profile['post_group'],
1652
				),
1653
				array(
1654
					'tag' => 'comments',
1655
					'content' => $scripturl . '?action=pm;sa=send;u=' . $profile['id'],
1656
				),
1657
				array(
1658
					'tag' => 'pubDate',
1659
					'content' => gmdate('D, d M Y H:i:s \G\M\T', $user_profile[$profile['id']]['date_registered']),
1660
				),
1661
				array(
1662
					'tag' => 'guid',
1663
					'content' => $guid,
1664
					'attributes' => array(
1665
						'isPermaLink' => 'false',
1666
					),
1667
				),
1668
			)
1669
		);
1670
	}
1671
	elseif ($xml_format == 'rdf')
1672
	{
1673
		$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...
1674
			'tag' => 'item',
1675
			'attributes' => array('rdf:about' => $scripturl . '?action=profile;u=' . $profile['id']),
1676
			'content' => array(
1677
				array(
1678
					'tag' => 'dc:format',
1679
					'content' => 'text/html',
1680
				),
1681
				array(
1682
					'tag' => 'title',
1683
					'content' => $profile['name'],
1684
				),
1685
				array(
1686
					'tag' => 'link',
1687
					'content' => $scripturl . '?action=profile;u=' . $profile['id'],
1688
				),
1689
				array(
1690
					'tag' => 'description',
1691
					'content' => isset($profile['group']) ? $profile['group'] : $profile['post_group'],
1692
				),
1693
			)
1694
		);
1695
	}
1696
	elseif ($xml_format == 'atom')
1697
	{
1698
		$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...
1699
			'tag' => 'entry',
1700
			'content' => array(
1701
				array(
1702
					'tag' => 'title',
1703
					'content' => $profile['name'],
1704
				),
1705
				array(
1706
					'tag' => 'link',
1707
					'attributes' => array(
1708
						'rel' => 'alternate',
1709
						'type' => 'text/html',
1710
						'href' => $scripturl . '?action=profile;u=' . $profile['id'],
1711
					),
1712
				),
1713
				array(
1714
					'tag' => 'summary',
1715
					'attributes' => array('type' => 'html'),
1716
					'content' => isset($profile['group']) ? $profile['group'] : $profile['post_group'],
1717
				),
1718
				array(
1719
					'tag' => 'author',
1720
					'content' => array(
1721
						array(
1722
							'tag' => 'name',
1723
							'content' => $profile['name'],
1724
						),
1725
						array(
1726
							'tag' => 'email',
1727
							'content' => $profile['show_email'] ? $profile['email'] : null,
1728
						),
1729
						array(
1730
							'tag' => 'uri',
1731
							'content' => !empty($profile['website']['url']) ? $profile['website']['url'] : null,
1732
						),
1733
					),
1734
				),
1735
				array(
1736
					'tag' => 'published',
1737
					'content' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $user_profile[$profile['id']]['date_registered']),
1738
				),
1739
				array(
1740
					'tag' => 'updated',
1741
					'content' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $user_profile[$profile['id']]['last_login']),
1742
				),
1743
				array(
1744
					'tag' => 'id',
1745
					'content' => $guid,
1746
				),
1747
			)
1748
		);
1749
	}
1750
	else
1751
	{
1752
		$data = array(
1753
			array(
1754
				'tag' => 'username',
1755
				'content' => $user_info['is_admin'] || $user_info['id'] == $profile['id'] ? $profile['username'] : null,
1756
			),
1757
			array(
1758
				'tag' => 'name',
1759
				'content' => $profile['name'],
1760
			),
1761
			array(
1762
				'tag' => 'link',
1763
				'content' => $scripturl . '?action=profile;u=' . $profile['id'],
1764
			),
1765
			array(
1766
				'tag' => 'posts',
1767
				'content' => $profile['posts'],
1768
			),
1769
			array(
1770
				'tag' => 'post-group',
1771
				'content' => $profile['post_group'],
1772
			),
1773
			array(
1774
				'tag' => 'language',
1775
				'content' => $profile['language'],
1776
			),
1777
			array(
1778
				'tag' => 'last-login',
1779
				'content' => gmdate('D, d M Y H:i:s \G\M\T', $user_profile[$profile['id']]['last_login']),
1780
			),
1781
			array(
1782
				'tag' => 'registered',
1783
				'content' => gmdate('D, d M Y H:i:s \G\M\T', $user_profile[$profile['id']]['date_registered']),
1784
			),
1785
			array(
1786
				'tag' => 'gender',
1787
				'content' => !empty($profile['gender']['name']) ? $profile['gender']['name'] : null,
1788
			),
1789
			array(
1790
				'tag' => 'avatar',
1791
				'content' => !empty($profile['avatar']['url']) ? $profile['avatar']['url'] : null,
1792
			),
1793
			array(
1794
				'tag' => 'online',
1795
				'content' => !empty($profile['online']['is_online']) ? '' : null,
1796
			),
1797
			array(
1798
				'tag' => 'signature',
1799
				'content' => !empty($profile['signature']) ? $profile['signature'] : null,
1800
			),
1801
			array(
1802
				'tag' => 'blurb',
1803
				'content' => !empty($profile['blurb']) ? $profile['blurb'] : null,
1804
			),
1805
			array(
1806
				'tag' => 'title',
1807
				'content' => !empty($profile['title']) ? $profile['title'] : null,
1808
			),
1809
			array(
1810
				'tag' => 'position',
1811
				'content' => !empty($profile['group']) ? $profile['group'] : null,
1812
			),
1813
			array(
1814
				'tag' => 'email',
1815
				'content' => !empty($profile['show_email']) ? $profile['show_email'] : null,
1816
			),
1817
			array(
1818
				'tag' => 'website',
1819
				'content' => empty($profile['website']['url']) ? null : array(
1820
					array(
1821
						'tag' => 'title',
1822
						'content' => !empty($profile['website']['title']) ? $profile['website']['title'] : null,
1823
					),
1824
					array(
1825
						'tag' => 'link',
1826
						'content' => $profile['website']['url'],
1827
					),
1828
				),
1829
			),
1830
		);
1831
1832
		if (!empty($profile['birth_date']) && substr($profile['birth_date'], 0, 4) != '0000')
1833
		{
1834
			list ($birth_year, $birth_month, $birth_day) = sscanf($profile['birth_date'], '%d-%d-%d');
1835
			$datearray = getdate(forum_time());
1836
			$age = $datearray['year'] - $birth_year - (($datearray['mon'] > $birth_month || ($datearray['mon'] == $birth_month && $datearray['mday'] >= $birth_day)) ? 0 : 1);
1837
1838
			$data[] = array(
1839
				'tag' => 'age',
1840
				'content' => $age,
1841
			);
1842
1843
		}
1844
	}
1845
1846
	// Save some memory.
1847
	unset($profile, $memberContext[$_GET['u']]);
1848
1849
	return $data;
1850
}
1851
1852
?>