Completed
Push — release-2.1 ( 6f6d35...abeae7 )
by Mathias
08:46
created

News.php ➔ getXmlNews()   F

Complexity

Conditions 36
Paths 700

Size

Total Lines 387
Code Lines 243

Duplication

Lines 104
Ratio 26.87 %

Importance

Changes 0
Metric Value
cc 36
eloc 243
nc 700
nop 1
dl 104
loc 387
rs 2.0567
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 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',
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)
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...
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)
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...
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)
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...
174
			$context['optimize_msg']['lowest'] = 'm.id_msg >= ' . max(0, $modSettings['maxMsgID'] - 600 - $_GET['limit'] * 5);
175
	}
176
	else
177
	{
178
		$query_this_board = '{query_see_board}' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? '
179
			AND b.id_board != ' . $modSettings['recycle_board'] : '');
180
		$context['optimize_msg']['lowest'] = 'm.id_msg >= ' . max(0, $modSettings['maxMsgID'] - 100 - $_GET['limit'] * 5);
181
	}
182
183
	// Show in rss or proprietary format?
184
	$xml_format = isset($_GET['type']) && in_array($_GET['type'], array('smf', 'rss', 'rss2', 'atom', 'rdf')) ? $_GET['type'] : 'rss2';
185
186
	// @todo Birthdays?
187
188
	// List all the different types of data they can pull.
189
	$subActions = array(
190
		'recent' => array('getXmlRecent', 'recent-post'),
191
		'news' => array('getXmlNews', 'article'),
192
		'members' => array('getXmlMembers', 'member'),
193
		'profile' => array('getXmlProfile', null),
194
	);
195
196
	// Easy adding of sub actions
197
 	call_integration_hook('integrate_xmlfeeds', array(&$subActions));
198
199
	if (empty($_GET['sa']) || !isset($subActions[$_GET['sa']]))
200
		$_GET['sa'] = 'recent';
201
202
	// We only want some information, not all of it.
203
	$cachekey = array($xml_format, $_GET['action'], $_GET['limit'], $_GET['sa']);
204
	foreach (array('board', 'boards', 'c') as $var)
205
		if (isset($_REQUEST[$var]))
206
			$cachekey[] = $_REQUEST[$var];
207
	$cachekey = md5($smcFunc['json_encode']($cachekey) . (!empty($query_this_board) ? $query_this_board : ''));
208
	$cache_t = microtime();
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')
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...
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')
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...
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)
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...
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)
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...
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'); });
393
			$link = array_pop($link);
394
395
			echo '
396
				<rdf:li rdf:resource="', $link['content'], '" />';
397
		}
398
399
		echo '
400
			</rdf:Seq>
401
		</items>
402
	</channel>
403
';
404
405
		dumpTags($xml_data, 1, null, $xml_format, $forceCdataKeys, $nsKeys);
406
407
		echo '
408
</rdf:RDF>';
409
	}
410
	// Otherwise, we're using our proprietary formats - they give more data, though.
411
	else
412
	{
413
		echo '
414
<smf:xml-feed xml:lang="', strtr($txt['lang_locale'], '_', '-'), '"', $ns_string, '>';
415
416
		// Hard to imagine anyone wanting to add these for the proprietary format, but just in case...
417
		echo $extraFeedTags_string;
418
419
		// Dump out that associative array.  Indent properly.... and use the right names for the base elements.
420
		dumpTags($xml_data, 1, $subActions[$_GET['sa']][1], $xml_format, $forceCdataKeys, $nsKeys);
421
422
		echo '
423
</smf:xml-feed>';
424
}
425
426
	obExit(false);
427
}
428
429
/**
430
 * Called from dumpTags to convert data to xml
431
 * Finds urls for local site and sanitizes them
432
 *
433
 * @param string $val A string containing a possible URL
434
 * @return string $val The string with any possible URLs sanitized
435
 */
436
function fix_possible_url($val)
437
{
438
	global $modSettings, $context, $scripturl;
439
440
	if (substr($val, 0, strlen($scripturl)) != $scripturl)
441
		return $val;
442
443
	call_integration_hook('integrate_fix_url', array(&$val));
444
445
	if (empty($modSettings['queryless_urls']) || ($context['server']['is_cgi'] && ini_get('cgi.fix_pathinfo') == 0 && @get_cfg_var('cgi.fix_pathinfo') == 0) || (!$context['server']['is_apache'] && !$context['server']['is_lighttpd']))
446
		return $val;
447
448
	$val = preg_replace_callback('~\b' . preg_quote($scripturl, '~') . '\?((?:board|topic)=[^#"]+)(#[^"]*)?$~', function($m) use ($scripturl)
449
		{
450
			return $scripturl . '/' . strtr("$m[1]", '&;=', '//,') . '.html' . (isset($m[2]) ? $m[2] : "");
451
		}, $val);
452
	return $val;
453
}
454
455
/**
456
 * Ensures supplied data is properly encapsulated in cdata xml tags
457
 * Called from getXmlProfile in News.php
458
 *
459
 * @param string $data XML data
460
 * @param string $ns A namespace prefix for the XML data elements (used by mods, maybe)
461
 * @param boolean $force If true, enclose the XML data in cdata tags no matter what (used by mods, maybe)
462
 * @return string The XML data enclosed in cdata tags when necessary
463
 */
464
function cdata_parse($data, $ns = '', $force = false)
465
{
466
	global $smcFunc;
467
468
	// Do we even need to do this?
469
	if (strpbrk($data, '<>&') == false && $force !== true)
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing strpbrk($data, '<>&') of type string to the boolean false. If you are specifically checking for an empty string, consider using the more explicit === '' instead.
Loading history...
470
		return $data;
471
472
	$cdata = '<![CDATA[';
473
474
	for ($pos = 0, $n = $smcFunc['strlen']($data); $pos < $n; null)
475
	{
476
		$positions = array(
477
			$smcFunc['strpos']($data, '&', $pos),
478
			$smcFunc['strpos']($data, ']', $pos),
479
		);
480
		if ($ns != '')
481
			$positions[] = $smcFunc['strpos']($data, '<', $pos);
482
		foreach ($positions as $k => $dummy)
483
		{
484
			if ($dummy === false)
485
				unset($positions[$k]);
486
		}
487
488
		$old = $pos;
489
		$pos = empty($positions) ? $n : min($positions);
490
491
		if ($pos - $old > 0)
492
			$cdata .= $smcFunc['substr']($data, $old, $pos - $old);
493
		if ($pos >= $n)
494
			break;
495
496
		if ($smcFunc['substr']($data, $pos, 1) == '<')
497
		{
498
			$pos2 = $smcFunc['strpos']($data, '>', $pos);
499
			if ($pos2 === false)
500
				$pos2 = $n;
501
			if ($smcFunc['substr']($data, $pos + 1, 1) == '/')
502
				$cdata .= ']]></' . $ns . ':' . $smcFunc['substr']($data, $pos + 2, $pos2 - $pos - 1) . '<![CDATA[';
503
			else
504
				$cdata .= ']]><' . $ns . ':' . $smcFunc['substr']($data, $pos + 1, $pos2 - $pos) . '<![CDATA[';
505
			$pos = $pos2 + 1;
506
		}
507
		elseif ($smcFunc['substr']($data, $pos, 1) == ']')
508
		{
509
			$cdata .= ']]>&#093;<![CDATA[';
510
			$pos++;
511
		}
512
		elseif ($smcFunc['substr']($data, $pos, 1) == '&')
513
		{
514
			$pos2 = $smcFunc['strpos']($data, ';', $pos);
515
			if ($pos2 === false)
516
				$pos2 = $n;
517
			$ent = $smcFunc['substr']($data, $pos + 1, $pos2 - $pos - 1);
518
519
			if ($smcFunc['substr']($data, $pos + 1, 1) == '#')
520
				$cdata .= ']]>' . $smcFunc['substr']($data, $pos, $pos2 - $pos + 1) . '<![CDATA[';
521
			elseif (in_array($ent, array('amp', 'lt', 'gt', 'quot')))
522
				$cdata .= ']]>' . $smcFunc['substr']($data, $pos, $pos2 - $pos + 1) . '<![CDATA[';
523
524
			$pos = $pos2 + 1;
525
		}
526
	}
527
528
	$cdata .= ']]>';
529
530
	return strtr($cdata, array('<![CDATA[]]>' => ''));
531
}
532
533
/**
534
 * Formats data retrieved in other functions into xml format.
535
 * Additionally formats data based on the specific format passed.
536
 * This function is recursively called to handle sub arrays of data.
537
 *
538
 * @param array $data The array to output as xml data
539
 * @param int $i The amount of indentation to use.
540
 * @param null|string $tag
541
 * @param string $xml_format The format to use ('atom', 'rss', 'rss2' or empty for plain XML)
542
 * @param array $forceCdataKeys A list of keys on which to force cdata wrapping (used by mods, maybe)
543
 * @param array $nsKeys Key-value pairs of namespace prefixes to pass to cdata_parse() (used by mods, maybe)
544
 */
545
function dumpTags($data, $i, $tag = null, $xml_format = '', $forceCdataKeys = array(), $nsKeys = array())
546
{
547
	// Wrap the values of these keys into CDATA tags
548
	$keysToCdata = array(
549
		'title',
550
		'name',
551
		'description',
552
		'summary',
553
		'subject',
554
		'body',
555
		'username',
556
		'signature',
557
		'position',
558
		'language',
559
		'gender',
560
		'blurb',
561
	);
562
	if ($xml_format != 'atom')
563
		$keysToCdata[] = 'category';
564
565
	if (!empty($forceCdataKeys))
566
	{
567
		$keysToCdata = array_merge($keysToCdata, $forceCdataKeys);
568
		$keysToCdata = array_unique($keysToCdata);
569
	}
570
571
	// For every array in the data...
572
	foreach ($data as $element)
573
	{
574
		// If a tag was passed, use it instead of the key.
575
		$key = isset($tag) ? $tag : (isset($element['tag']) ? $element['tag'] : null);
576
		$val = isset($element['content']) ? $element['content'] : null;
577
		$attrs = isset($element['attributes']) ? $element['attributes'] : null;
578
579
		// Skip it, it's been set to null.
580
		if ($key === null || ($val === null && $attrs === null))
581
			continue;
582
583
		$forceCdata = in_array($key, $forceCdataKeys);
584
		$ns = !empty($nsKeys[$key]) ? $nsKeys[$key] : '';
585
586
		// First let's indent!
587
		echo "\n", str_repeat("\t", $i);
588
589
		// Beginning tag.
590
		echo '<', $key;
591
592
		if (!empty($attrs))
593
		{
594
			foreach ($attrs as $attr_key => $attr_value)
595
				echo ' ', $attr_key, '="', fix_possible_url($attr_value), '"';
596
		}
597
598
		// If it's empty, simply output an empty element.
599
		if (empty($val))
600
		{
601
			echo ' />';
602
		}
603
		else
604
		{
605
			echo '>';
606
607
			// The element's value.
608
			if (is_array($val))
609
			{
610
				// An array.  Dump it, and then indent the tag.
611
				dumpTags($val, $i + 1, null, $xml_format, $forceCdataKeys, $nsKeys);
612
				echo "\n", str_repeat("\t", $i);
613
			}
614
			// A string with returns in it.... show this as a multiline element.
615
			elseif (strpos($val, "\n") !== false)
616
				echo "\n", in_array($key, $keysToCdata) ? cdata_parse(fix_possible_url($val), $ns, $forceCdata) : fix_possible_url($val), "\n", str_repeat("\t", $i);
617
			// A simple string.
618
			else
619
				echo in_array($key, $keysToCdata) ? cdata_parse(fix_possible_url($val), $ns, $forceCdata) : fix_possible_url($val);
620
621
			// Ending tag.
622
			echo '</', $key, '>';
623
		}
624
	}
625
}
626
627
/**
628
 * Retrieve the list of members from database.
629
 * The array will be generated to match the format.
630
 * @todo get the list of members from Subs-Members.
631
 *
632
 * @param string $xml_format The format to use. Can be 'atom', 'rdf', 'rss', 'rss2' or 'smf'
633
 * @return array An array of arrays of feed items. Each array has keys corresponding to the appropriate tags for the specified format.
634
 */
635
function getXmlMembers($xml_format)
636
{
637
	global $scripturl, $smcFunc;
638
639
	if (!allowedTo('view_mlist'))
640
		return array();
641
642
	// Find the most recent members.
643
	$request = $smcFunc['db_query']('', '
644
		SELECT id_member, member_name, real_name, date_registered, last_login
645
		FROM {db_prefix}members
646
		ORDER BY id_member DESC
647
		LIMIT {int:limit}',
648
		array(
649
			'limit' => $_GET['limit'],
650
		)
651
	);
652
	$data = array();
653
	while ($row = $smcFunc['db_fetch_assoc']($request))
654
	{
655
		// Create a GUID for each member using the tag URI scheme
656
		$guid = 'tag:' . parse_url($scripturl, PHP_URL_HOST) . ',' . gmdate('Y-m-d', $row['date_registered']) . ':member=' . $row['id_member'];
657
658
		// Make the data look rss-ish.
659
		if ($xml_format == 'rss' || $xml_format == 'rss2')
660
			$data[] = array(
661
				'tag' => 'item',
662
				'content' => array(
663
					array(
664
						'tag' => 'title',
665
						'content' => $row['real_name'],
666
					),
667
					array(
668
						'tag' => 'link',
669
						'content' => $scripturl . '?action=profile;u=' . $row['id_member'],
670
					),
671
					array(
672
						'tag' => 'comments',
673
						'content' => $scripturl . '?action=pm;sa=send;u=' . $row['id_member'],
674
					),
675
					array(
676
						'tag' => 'pubDate',
677
						'content' => gmdate('D, d M Y H:i:s \G\M\T', $row['date_registered']),
678
					),
679
					array(
680
						'tag' => 'guid',
681
						'content' => $guid,
682
						'attributes' => array(
683
							'isPermaLink' => 'false',
684
						),
685
					),
686
				),
687
			);
688
		elseif ($xml_format == 'rdf')
689
			$data[] = array(
690
				'tag' => 'item',
691
				'attributes' => array('rdf:about' => $scripturl . '?action=profile;u=' . $row['id_member']),
692
				'content' => array(
693
					array(
694
						'tag' => 'dc:format',
695
						'content' => 'text/html',
696
					),
697
					array(
698
						'tag' => 'title',
699
						'content' => $row['real_name'],
700
					),
701
					array(
702
						'tag' => 'link',
703
						'content' => $scripturl . '?action=profile;u=' . $row['id_member'],
704
					),
705
				),
706
			);
707
		elseif ($xml_format == 'atom')
708
			$data[] = array(
709
				'tag' => 'entry',
710
				'content' => array(
711
					array(
712
						'tag' => 'title',
713
						'content' => $row['real_name'],
714
					),
715
					array(
716
						'tag' => 'link',
717
						'attributes' => array(
718
							'rel' => 'alternate',
719
							'type' => 'text/html',
720
							'href' => $scripturl . '?action=profile;u=' . $row['id_member'],
721
						),
722
					),
723
					array(
724
						'tag' => 'published',
725
						'content' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $row['date_registered']),
726
					),
727
					array(
728
						'tag' => 'updated',
729
						'content' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $row['last_login']),
730
					),
731
					array(
732
						'tag' => 'id',
733
						'content' => $guid,
734
					),
735
				),
736
			);
737
		// More logical format for the data, but harder to apply.
738
		else
739
			$data[] = array(
740
				'tag' => 'member',
741
				'content' => array(
742
					array(
743
						'tag' => 'name',
744
						'content' => $row['real_name'],
745
					),
746
					array(
747
						'tag' => 'time',
748
						'content' => $smcFunc['htmlspecialchars'](strip_tags(timeformat($row['date_registered']))),
749
					),
750
					array(
751
						'tag' => 'id',
752
						'content' => $row['id_member'],
753
					),
754
					array(
755
						'tag' => 'link',
756
						'content' => $scripturl . '?action=profile;u=' . $row['id_member'],
757
					),
758
				),
759
			);
760
	}
761
	$smcFunc['db_free_result']($request);
762
763
	return $data;
764
}
765
766
/**
767
 * Get the latest topics information from a specific board,
768
 * to display later.
769
 * The returned array will be generated to match the xml_format.
770
 * @todo does not belong here
771
 *
772
 * @param string $xml_format The XML format. Can be 'atom', 'rdf', 'rss', 'rss2' or 'smf'.
773
 * @return array An array of arrays of topic data for the feed. Each array has keys corresponding to the tags for the specified format.
774
 */
775
function getXmlNews($xml_format)
776
{
777
	global $scripturl, $modSettings, $board, $user_info;
778
	global $query_this_board, $smcFunc, $context, $txt;
779
780
	/* Find the latest posts that:
781
		- are the first post in their topic.
782
		- are on an any board OR in a specified board.
783
		- can be seen by this user.
784
		- are actually the latest posts. */
785
786
	$done = false;
787
	$loops = 0;
788
	while (!$done)
789
	{
790
		$optimize_msg = implode(' AND ', $context['optimize_msg']);
791
		$request = $smcFunc['db_query']('', '
792
			SELECT
793
				m.smileys_enabled, m.poster_time, m.id_msg, m.subject, m.body, m.modified_time,
794
				m.icon, t.id_topic, t.id_board, t.num_replies,
795
				b.name AS bname,
796
				COALESCE(mem.id_member, 0) AS id_member,
797
				COALESCE(mem.email_address, m.poster_email) AS poster_email,
798
				COALESCE(mem.real_name, m.poster_name) AS poster_name
799
			FROM {db_prefix}topics AS t
800
				INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
801
				INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
802
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
803
			WHERE ' . $query_this_board . (empty($optimize_msg) ? '' : '
804
				AND {raw:optimize_msg}') . (empty($board) ? '' : '
805
				AND t.id_board = {int:current_board}') . ($modSettings['postmod_active'] ? '
806
				AND t.approved = {int:is_approved}' : '') . '
807
			ORDER BY t.id_first_msg DESC
808
			LIMIT {int:limit}',
809
			array(
810
				'current_board' => $board,
811
				'is_approved' => 1,
812
				'limit' => $_GET['limit'],
813
				'optimize_msg' => $optimize_msg,
814
			)
815
		);
816
		// If we don't have $_GET['limit'] results, try again with an unoptimized version covering all rows.
817
		if ($loops < 2 && $smcFunc['db_num_rows']($request) < $_GET['limit'])
818
		{
819
			$smcFunc['db_free_result']($request);
820 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...
821
				unset($context['optimize_msg']['lowest']);
822
			else
823
				$context['optimize_msg']['lowest'] = 'm.id_msg >= t.id_first_msg';
824
			$context['optimize_msg']['highest'] = 'm.id_msg <= t.id_last_msg';
825
			$loops++;
826
		}
827
		else
828
			$done = true;
829
	}
830
	$data = array();
831
	while ($row = $smcFunc['db_fetch_assoc']($request))
0 ignored issues
show
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...
832
	{
833
		// Limit the length of the message, if the option is set.
834 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...
835
			$row['body'] = strtr($smcFunc['substr'](str_replace('<br>', "\n", $row['body']), 0, $modSettings['xmlnews_maxlen'] - 3), array("\n" => '<br>')) . '...';
836
837
		$row['body'] = parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']);
838
839
		censorText($row['body']);
840
		censorText($row['subject']);
841
842
		// Do we want to include any attachments?
843 View Code Duplication
		if (!empty($modSettings['attachmentEnable']) && !empty($modSettings['xmlnews_attachments']) && allowedTo('view_attachments', $row['id_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...
844
		{
845
			$attach_request = $smcFunc['db_query']('', '
846
				SELECT
847
					a.id_attach, a.filename, COALESCE(a.size, 0) AS filesize, a.mime_type, a.downloads, a.approved, m.id_topic AS topic
848
				FROM {db_prefix}attachments AS a
849
					LEFT JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg)
850
				WHERE a.attachment_type = {int:attachment_type}
851
					AND a.id_msg = {int:message_id}',
852
				array(
853
					'message_id' => $row['id_msg'],
854
					'attachment_type' => 0,
855
					'is_approved' => 1,
856
				)
857
			);
858
			$loaded_attachments = array();
859
			while ($attach = $smcFunc['db_fetch_assoc']($attach_request))
860
			{
861
				// Include approved attachments only
862
				if ($attach['approved'])
863
					$loaded_attachments['attachment_' . $attach['id_attach']] = $attach;
864
			}
865
			$smcFunc['db_free_result']($attach_request);
866
867
			// Sort the attachments by size to make things easier below
868
			if (!empty($loaded_attachments))
869
			{
870
				uasort($loaded_attachments, function($a, $b) {
871
					if ($a['filesize'] == $b['filesize'])
872
					        return 0;
873
					return ($a['filesize'] < $b['filesize']) ? -1 : 1;
874
				});
875
			}
876
			else
877
				$loaded_attachments = null;
878
		}
879
		else
880
			$loaded_attachments = null;
881
882
		// Create a GUID for this topic using the tag URI scheme
883
		$guid = 'tag:' . parse_url($scripturl, PHP_URL_HOST) . ',' . gmdate('Y-m-d', $row['poster_time']) . ':topic=' . $row['id_topic'];
884
885
		// Being news, this actually makes sense in rss format.
886
		if ($xml_format == 'rss' || $xml_format == 'rss2')
887
		{
888
			// Only one attachment allowed in RSS.
889 View Code Duplication
			if ($loaded_attachments !== null)
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...
890
			{
891
				$attachment = array_pop($loaded_attachments);
892
				$enclosure = array(
893
					'url' => fix_possible_url($scripturl . '?action=dlattach;topic=' . $attachment['topic'] . '.0;attach=' . $attachment['id_attach']),
894
					'length' => $attachment['filesize'],
895
					'type' => $attachment['mime_type'],
896
				);
897
			}
898
			else
899
				$enclosure = null;
900
901
			$data[] = array(
902
				'tag' => 'item',
903
				'content' => array(
904
					array(
905
						'tag' => 'title',
906
						'content' => $row['subject'],
907
					),
908
					array(
909
						'tag' => 'link',
910
						'content' => $scripturl . '?topic=' . $row['id_topic'] . '.0',
911
					),
912
					array(
913
						'tag' => 'description',
914
						'content' => $row['body'],
915
					),
916
					array(
917
						'tag' => 'author',
918
						'content' => (allowedTo('moderate_forum') || $row['id_member'] == $user_info['id']) ? $row['poster_email'] . ' (' . $row['poster_name'] . ')' : null,
919
					),
920
					array(
921
						'tag' => 'comments',
922
						'content' => $scripturl . '?action=post;topic=' . $row['id_topic'] . '.0',
923
					),
924
					array(
925
						'tag' => 'category',
926
						'content' => $row['bname'],
927
					),
928
					array(
929
						'tag' => 'pubDate',
930
						'content' => gmdate('D, d M Y H:i:s \G\M\T', $row['poster_time']),
931
					),
932
					array(
933
						'tag' => 'guid',
934
						'content' => $guid,
935
						'attributes' => array(
936
							'isPermaLink' => 'false',
937
						),
938
					),
939
					array(
940
						'tag' => 'enclosure',
941
						'attributes' => $enclosure,
942
					),
943
				),
944
			);
945
		}
946
		elseif ($xml_format == 'rdf')
947
		{
948
			$data[] = array(
949
				'tag' => 'item',
950
				'attributes' => array('rdf:about' => $scripturl . '?topic=' . $row['id_topic'] . '.0'),
951
				'content' => array(
952
					array(
953
						'tag' => 'dc:format',
954
						'content' => 'text/html',
955
					),
956
					array(
957
						'tag' => 'title',
958
						'content' => $row['subject'],
959
					),
960
					array(
961
						'tag' => 'link',
962
						'content' => $scripturl . '?topic=' . $row['id_topic'] . '.0',
963
					),
964
					array(
965
						'tag' => 'description',
966
						'content' => $row['body'],
967
					),
968
				),
969
			);
970
		}
971
		elseif ($xml_format == 'atom')
972
		{
973
			// Only one attachment allowed
974 View Code Duplication
			if (!empty($loaded_attachments))
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...
975
			{
976
				$attachment = array_pop($loaded_attachments);
977
				$enclosure = array(
978
					'rel' => 'enclosure',
979
					'href' => fix_possible_url($scripturl . '?action=dlattach;topic=' . $attachment['topic'] . '.0;attach=' . $attachment['id_attach']),
980
					'length' => $attachment['filesize'],
981
					'type' => $attachment['mime_type'],
982
				);
983
			}
984
			else
985
				$enclosure = null;
986
987
			$data[] = array(
988
				'tag' => 'entry',
989
				'content' => array(
990
					array(
991
						'tag' => 'title',
992
						'content' => $row['subject'],
993
					),
994
					array(
995
						'tag' => 'link',
996
						'attributes' => array(
997
							'rel' => 'alternate',
998
							'type' => 'text/html',
999
							'href' => $scripturl . '?topic=' . $row['id_topic'] . '.0',
1000
						),
1001
					),
1002
					array(
1003
						'tag' => 'summary',
1004
						'attributes' => array('type' => 'html'),
1005
						'content' => $row['body'],
1006
					),
1007
					array(
1008
						'tag' => 'category',
1009
						'attributes' => array('term' => $row['bname']),
1010
					),
1011
					array(
1012
						'tag' => 'author',
1013
						'content' => array(
1014
							array(
1015
								'tag' => 'name',
1016
								'content' => $row['poster_name'],
1017
							),
1018
							array(
1019
								'tag' => 'email',
1020
								'content' => (allowedTo('moderate_forum') || $row['id_member'] == $user_info['id']) ? $row['poster_email'] : null,
1021
							),
1022
							array(
1023
								'tag' => 'uri',
1024
								'content' => !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : null,
1025
							),
1026
						)
1027
					),
1028
					array(
1029
						'tag' => 'published',
1030
						'content' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $row['poster_time']),
1031
					),
1032
					array(
1033
						'tag' => 'updated',
1034
						'content' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', empty($row['modified_time']) ? $row['poster_time'] : $row['modified_time']),
1035
					),
1036
					array(
1037
						'tag' => 'id',
1038
						'content' => $guid,
1039
					),
1040
					array(
1041
						'tag' => 'link',
1042
						'attributes' => $enclosure,
1043
					),
1044
				),
1045
			);
1046
		}
1047
		// The biggest difference here is more information.
1048
		else
1049
		{
1050
			$attachments = array();
1051 View Code Duplication
			if (!empty($loaded_attachments))
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...
1052
			{
1053
				foreach ($loaded_attachments as $attachment)
1054
				{
1055
					$attachments[] = array(
1056
						'tag' => 'attachment',
1057
						'content' => array(
1058
							array(
1059
								'tag' => 'id',
1060
								'content' => $attachment['id_attach'],
1061
							),
1062
							array(
1063
								'tag' => 'name',
1064
								'content' => preg_replace('~&amp;#(\\d{1,7}|x[0-9a-fA-F]{1,6});~', '&#\\1;', $smcFunc['htmlspecialchars']($attachment['filename'])),
1065
							),
1066
							array(
1067
								'tag' => 'downloads',
1068
								'content' => $attachment['downloads'],
1069
							),
1070
							array(
1071
								'tag' => 'size',
1072
								'content' => ($attachment['filesize'] < 1024000) ? round($attachment['filesize'] / 1024, 2) . ' ' . $txt['kilobyte'] : round($attachment['filesize'] / 1024 / 1024, 2) . ' ' . $txt['megabyte'],
1073
							),
1074
							array(
1075
								'tag' => 'byte_size',
1076
								'content' => $attachment['filesize'],
1077
							),
1078
							array(
1079
								'tag' => 'link',
1080
								'content' => $scripturl . '?action=dlattach;topic=' . $attachment['topic'] . '.0;attach=' . $attachment['id_attach'],
1081
							),
1082
						)
1083
					);
1084
				}
1085
			}
1086
			else
1087
				$attachments = null;
1088
1089
			$data[] = array(
1090
				'tag' => 'article',
1091
				'content' => array(
1092
					array(
1093
						'tag' => 'time',
1094
						'content' => $smcFunc['htmlspecialchars'](strip_tags(timeformat($row['poster_time']))),
1095
					),
1096
					array(
1097
						'tag' => 'id',
1098
						'content' => $row['id_topic'],
1099
					),
1100
					array(
1101
						'tag' => 'subject',
1102
						'content' => $row['subject'],
1103
					),
1104
					array(
1105
						'tag' => 'body',
1106
						'content' => $row['body'],
1107
					),
1108
					array(
1109
						'tag' => 'poster',
1110
						'content' => array(
1111
							array(
1112
								'tag' => 'name',
1113
								'content' => $row['poster_name'],
1114
							),
1115
							array(
1116
								'tag' => 'id',
1117
								'content' => $row['id_member'],
1118
							),
1119
							array(
1120
								'tag' => 'link',
1121
								'content' => !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : '',
1122
							),
1123
						)
1124
					),
1125
					array(
1126
						'tag' => 'topic',
1127
						'content' => $row['id_topic'],
1128
					),
1129
					array(
1130
						'tag' => 'board',
1131
						'content' => array(
1132
							array(
1133
								'tag' => 'name',
1134
								'content' => $row['bname'],
1135
							),
1136
							array(
1137
								'tag' => 'id',
1138
								'content' => $row['id_board'],
1139
							),
1140
							array(
1141
								'tag' => 'link',
1142
								'content' => $scripturl . '?board=' . $row['id_board'] . '.0',
1143
							),
1144
						),
1145
					),
1146
					array(
1147
						'tag' => 'link',
1148
						'content' => $scripturl . '?topic=' . $row['id_topic'] . '.0',
1149
					),
1150
					array(
1151
						'tag' => 'attachments',
1152
						'content' => $attachments,
1153
					),
1154
				),
1155
			);
1156
		}
1157
	}
1158
	$smcFunc['db_free_result']($request);
1159
1160
	return $data;
1161
}
1162
1163
/**
1164
 * Get the recent topics to display.
1165
 * The returned array will be generated to match the xml_format.
1166
 * @todo does not belong here.
1167
 *
1168
 * @param string $xml_format The XML format. Can be 'atom', 'rdf', 'rss', 'rss2' or 'smf'
1169
 * @return array An array of arrays containing data for the feed. Each array has keys corresponding to the appropriate tags for the specified format.
1170
 */
1171
function getXmlRecent($xml_format)
1172
{
1173
	global $scripturl, $modSettings, $board, $txt;
1174
	global $query_this_board, $smcFunc, $context, $user_info, $sourcedir;
1175
1176
	require_once($sourcedir . '/Subs-Attachments.php');
1177
1178
	$done = false;
1179
	$loops = 0;
1180
	while (!$done)
1181
	{
1182
		$optimize_msg = implode(' AND ', $context['optimize_msg']);
1183
		$request = $smcFunc['db_query']('', '
1184
			SELECT m.id_msg
1185
			FROM {db_prefix}messages AS m
1186
				INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
1187
				INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
1188
			WHERE ' . $query_this_board . (empty($optimize_msg) ? '' : '
1189
				AND {raw:optimize_msg}') . (empty($board) ? '' : '
1190
				AND m.id_board = {int:current_board}') . ($modSettings['postmod_active'] ? '
1191
				AND m.approved = {int:is_approved}' : '') . '
1192
			ORDER BY m.id_msg DESC
1193
			LIMIT {int:limit}',
1194
			array(
1195
				'limit' => $_GET['limit'],
1196
				'current_board' => $board,
1197
				'is_approved' => 1,
1198
				'optimize_msg' => $optimize_msg,
1199
			)
1200
		);
1201
		// If we don't have $_GET['limit'] results, try again with an unoptimized version covering all rows.
1202
		if ($loops < 2 && $smcFunc['db_num_rows']($request) < $_GET['limit'])
1203
		{
1204
			$smcFunc['db_free_result']($request);
1205 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...
1206
				unset($context['optimize_msg']['lowest']);
1207
			else
1208
				$context['optimize_msg']['lowest'] = $loops ? 'm.id_msg >= t.id_first_msg' : 'm.id_msg >= (t.id_last_msg - t.id_first_msg) / 2';
1209
			$loops++;
1210
		}
1211
		else
1212
			$done = true;
1213
	}
1214
	$messages = array();
1215
	while ($row = $smcFunc['db_fetch_assoc']($request))
0 ignored issues
show
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...
1216
		$messages[] = $row['id_msg'];
1217
	$smcFunc['db_free_result']($request);
1218
1219
	if (empty($messages))
1220
		return array();
1221
1222
	// Find the most recent posts this user can see.
1223
	$request = $smcFunc['db_query']('', '
1224
		SELECT
1225
			m.smileys_enabled, m.poster_time, m.id_msg, m.subject, m.body, m.id_topic, t.id_board,
1226
			b.name AS bname, t.num_replies, m.id_member, m.icon, mf.id_member AS id_first_member,
1227
			COALESCE(mem.real_name, m.poster_name) AS poster_name, mf.subject AS first_subject,
1228
			COALESCE(memf.real_name, mf.poster_name) AS first_poster_name,
1229
			COALESCE(mem.email_address, m.poster_email) AS poster_email, m.modified_time
1230
		FROM {db_prefix}messages AS m
1231
			INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
1232
			INNER JOIN {db_prefix}messages AS mf ON (mf.id_msg = t.id_first_msg)
1233
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
1234
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
1235
			LEFT JOIN {db_prefix}members AS memf ON (memf.id_member = mf.id_member)
1236
		WHERE m.id_msg IN ({array_int:message_list})
1237
			' . (empty($board) ? '' : 'AND t.id_board = {int:current_board}') . '
1238
		ORDER BY m.id_msg DESC
1239
		LIMIT {int:limit}',
1240
		array(
1241
			'limit' => $_GET['limit'],
1242
			'current_board' => $board,
1243
			'message_list' => $messages,
1244
		)
1245
	);
1246
	$data = array();
1247
	while ($row = $smcFunc['db_fetch_assoc']($request))
1248
	{
1249
		// Limit the length of the message, if the option is set.
1250 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...
1251
			$row['body'] = strtr($smcFunc['substr'](str_replace('<br>', "\n", $row['body']), 0, $modSettings['xmlnews_maxlen'] - 3), array("\n" => '<br>')) . '...';
1252
1253
		$row['body'] = parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']);
1254
1255
		censorText($row['body']);
1256
		censorText($row['subject']);
1257
1258
		// Do we want to include any attachments?
1259 View Code Duplication
		if (!empty($modSettings['attachmentEnable']) && !empty($modSettings['xmlnews_attachments']) && allowedTo('view_attachments', $row['id_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...
1260
		{
1261
			$attach_request = $smcFunc['db_query']('', '
1262
				SELECT
1263
					a.id_attach, a.filename, COALESCE(a.size, 0) AS filesize, a.mime_type, a.downloads, a.approved, m.id_topic AS topic
1264
				FROM {db_prefix}attachments AS a
1265
					LEFT JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg)
1266
				WHERE a.attachment_type = {int:attachment_type}
1267
					AND a.id_msg = {int:message_id}',
1268
				array(
1269
					'message_id' => $row['id_msg'],
1270
					'attachment_type' => 0,
1271
					'is_approved' => 1,
1272
				)
1273
			);
1274
			$loaded_attachments = array();
1275
			while ($attach = $smcFunc['db_fetch_assoc']($attach_request))
1276
			{
1277
				// Include approved attachments only
1278
				if ($attach['approved'])
1279
					$loaded_attachments['attachment_' . $attach['id_attach']] = $attach;
1280
			}
1281
			$smcFunc['db_free_result']($attach_request);
1282
1283
			// Sort the attachments by size to make things easier below
1284
			if (!empty($loaded_attachments))
1285
			{
1286
				uasort($loaded_attachments, function($a, $b) {
1287
					if ($a['filesize'] == $b['filesize'])
1288
					        return 0;
1289
					return ($a['filesize'] < $b['filesize']) ? -1 : 1;
1290
				});
1291
			}
1292
			else
1293
				$loaded_attachments = null;
1294
		}
1295
		else
1296
			$loaded_attachments = null;
1297
1298
		// Create a GUID for this post using the tag URI scheme
1299
		$guid = 'tag:' . parse_url($scripturl, PHP_URL_HOST) . ',' . gmdate('Y-m-d', $row['poster_time']) . ':msg=' . $row['id_msg'];
1300
1301
		// Doesn't work as well as news, but it kinda does..
1302
		if ($xml_format == 'rss' || $xml_format == 'rss2')
1303
		{
1304
			// Only one attachment allowed in RSS.
1305 View Code Duplication
			if ($loaded_attachments !== null)
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...
1306
			{
1307
				$attachment = array_pop($loaded_attachments);
1308
				$enclosure = array(
1309
					'url' => fix_possible_url($scripturl . '?action=dlattach;topic=' . $attachment['topic'] . '.0;attach=' . $attachment['id_attach']),
1310
					'length' => $attachment['filesize'],
1311
					'type' => $attachment['mime_type'],
1312
				);
1313
			}
1314
			else
1315
				$enclosure = null;
1316
1317
			$data[] = array(
1318
				'tag' => 'item',
1319
				'content' => array(
1320
					array(
1321
						'tag' => 'title',
1322
						'content' => $row['subject'],
1323
					),
1324
					array(
1325
						'tag' => 'link',
1326
						'content' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'],
1327
					),
1328
					array(
1329
						'tag' => 'description',
1330
						'content' => $row['body'],
1331
					),
1332
					array(
1333
						'tag' => 'author',
1334
						'content' => (allowedTo('moderate_forum') || (!empty($row['id_member']) && $row['id_member'] == $user_info['id'])) ? $row['poster_email'] : null,
1335
					),
1336
					array(
1337
						'tag' => 'category',
1338
						'content' => $row['bname'],
1339
					),
1340
					array(
1341
						'tag' => 'comments',
1342
						'content' => $scripturl . '?action=post;topic=' . $row['id_topic'] . '.0',
1343
					),
1344
					array(
1345
						'tag' => 'pubDate',
1346
						'content' => gmdate('D, d M Y H:i:s \G\M\T', $row['poster_time']),
1347
					),
1348
					array(
1349
						'tag' => 'guid',
1350
						'content' => $guid,
1351
						'attributes' => array(
1352
							'isPermaLink' => 'false',
1353
						),
1354
					),
1355
					array(
1356
						'tag' => 'enclosure',
1357
						'attributes' => $enclosure,
1358
					),
1359
				),
1360
			);
1361
		}
1362
		elseif ($xml_format == 'rdf')
1363
		{
1364
			$data[] = array(
1365
				'tag' => 'item',
1366
				'attributes' => array('rdf:about' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg']),
1367
				'content' => array(
1368
					array(
1369
						'tag' => 'dc:format',
1370
						'content' => 'text/html',
1371
					),
1372
					array(
1373
						'tag' => 'title',
1374
						'content' => $row['subject'],
1375
					),
1376
					array(
1377
						'tag' => 'link',
1378
						'content' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'],
1379
					),
1380
					array(
1381
						'tag' => 'description',
1382
						'content' => $row['body'],
1383
					),
1384
				),
1385
			);
1386
		}
1387
		elseif ($xml_format == 'atom')
1388
		{
1389
			// Only one attachment allowed
1390 View Code Duplication
			if (!empty($loaded_attachments))
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...
1391
			{
1392
				$attachment = array_pop($loaded_attachments);
1393
				$enclosure = array(
1394
					'rel' => 'enclosure',
1395
					'href' => fix_possible_url($scripturl . '?action=dlattach;topic=' . $attachment['topic'] . '.0;attach=' . $attachment['id_attach']),
1396
					'length' => $attachment['filesize'],
1397
					'type' => $attachment['mime_type'],
1398
				);
1399
			}
1400
			else
1401
				$enclosure = null;
1402
1403
			$data[] = array(
1404
				'tag' => 'entry',
1405
				'content' => array(
1406
					array(
1407
						'tag' => 'title',
1408
						'content' => $row['subject'],
1409
					),
1410
					array(
1411
						'tag' => 'link',
1412
						'attributes' => array(
1413
							'rel' => 'alternate',
1414
							'type' => 'text/html',
1415
							'href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'],
1416
						),
1417
					),
1418
					array(
1419
						'tag' => 'summary',
1420
						'attributes' => array('type' => 'html'),
1421
						'content' => $row['body'],
1422
					),
1423
					array(
1424
						'tag' => 'category',
1425
						'attributes' => array('term' => $row['bname']),
1426
					),
1427
					array(
1428
						'tag' => 'author',
1429
						'content' => array(
1430
							array(
1431
								'tag' => 'name',
1432
								'content' => $row['poster_name'],
1433
							),
1434
							array(
1435
								'tag' => 'email',
1436
								'content' => (allowedTo('moderate_forum') || (!empty($row['id_member']) && $row['id_member'] == $user_info['id'])) ? $row['poster_email'] : null,
1437
							),
1438
							array(
1439
								'tag' => 'uri',
1440
								'content' => !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : null,
1441
							),
1442
						),
1443
					),
1444
					array(
1445
						'tag' => 'published',
1446
						'content' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $row['poster_time']),
1447
					),
1448
					array(
1449
						'tag' => 'updated',
1450
						'content' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', empty($row['modified_time']) ? $row['poster_time'] : $row['modified_time']),
1451
					),
1452
					array(
1453
						'tag' => 'id',
1454
						'content' => $guid,
1455
					),
1456
					array(
1457
						'tag' => 'link',
1458
						'attributes' => $enclosure,
1459
					),
1460
				),
1461
			);
1462
		}
1463
		// A lot of information here.  Should be enough to please the rss-ers.
1464
		else
1465
		{
1466
			$attachments = array();
1467 View Code Duplication
			if (!empty($loaded_attachments))
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...
1468
			{
1469
				foreach ($loaded_attachments as $attachment)
1470
				{
1471
					$attachments[] = array(
1472
						'tag' => 'attachment',
1473
						'content' => array(
1474
							array(
1475
								'tag' => 'id',
1476
								'content' => $attachment['id_attach'],
1477
							),
1478
							array(
1479
								'tag' => 'name',
1480
								'content' => preg_replace('~&amp;#(\\d{1,7}|x[0-9a-fA-F]{1,6});~', '&#\\1;', $smcFunc['htmlspecialchars']($attachment['filename'])),
1481
							),
1482
							array(
1483
								'tag' => 'downloads',
1484
								'content' => $attachment['downloads'],
1485
							),
1486
							array(
1487
								'tag' => 'size',
1488
								'content' => ($attachment['filesize'] < 1024000) ? round($attachment['filesize'] / 1024, 2) . ' ' . $txt['kilobyte'] : round($attachment['filesize'] / 1024 / 1024, 2) . ' ' . $txt['megabyte'],
1489
							),
1490
							array(
1491
								'tag' => 'byte_size',
1492
								'content' => $attachment['filesize'],
1493
							),
1494
							array(
1495
								'tag' => 'link',
1496
								'content' => $scripturl . '?action=dlattach;topic=' . $attachment['topic'] . '.0;attach=' . $attachment['id_attach'],
1497
							),
1498
						)
1499
					);
1500
				}
1501
			}
1502
			else
1503
				$attachments = null;
1504
1505
			$data[] = array(
1506
				'tag' => 'recent-post',
1507
				'content' => array(
1508
					array(
1509
						'tag' => 'time',
1510
						'content' => $smcFunc['htmlspecialchars'](strip_tags(timeformat($row['poster_time']))),
1511
					),
1512
					array(
1513
						'tag' => 'id',
1514
						'content' => $row['id_msg'],
1515
					),
1516
					array(
1517
						'tag' => 'subject',
1518
						'content' => $row['subject'],
1519
					),
1520
					array(
1521
						'tag' => 'body',
1522
						'content' => $row['body'],
1523
					),
1524
					array(
1525
						'tag' => 'starter',
1526
						'content' => array(
1527
							array(
1528
								'tag' => 'name',
1529
								'content' => $row['first_poster_name'],
1530
							),
1531
							array(
1532
								'tag' => 'id',
1533
								'content' => $row['id_first_member'],
1534
							),
1535
							array(
1536
								'tag' => 'link',
1537
								'content' => !empty($row['id_first_member']) ? $scripturl . '?action=profile;u=' . $row['id_first_member'] : '',
1538
							),
1539
						),
1540
					),
1541
					array(
1542
						'tag' => 'poster',
1543
						'content' => array(
1544
							array(
1545
								'tag' => 'name',
1546
								'content' => $row['poster_name'],
1547
							),
1548
							array(
1549
								'tag' => 'id',
1550
								'content' => $row['id_member'],
1551
							),
1552
							array(
1553
								'tag' => 'link',
1554
								'content' => !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : '',
1555
							),
1556
						),
1557
					),
1558
					array(
1559
						'tag' => 'topic',
1560
						'content' => array(
1561
							array(
1562
								'tag' => 'subject',
1563
								'content' => $row['first_subject'],
1564
							),
1565
							array(
1566
								'tag' => 'id',
1567
								'content' => $row['id_topic'],
1568
							),
1569
							array(
1570
								'tag' => 'link',
1571
								'content' => $scripturl . '?topic=' . $row['id_topic'] . '.new#new',
1572
							),
1573
						),
1574
					),
1575
					array(
1576
						'tag' => 'board',
1577
						'content' => array(
1578
							array(
1579
								'tag' => 'name',
1580
								'content' => $row['bname'],
1581
							),
1582
							array(
1583
								'tag' => 'id',
1584
								'content' => $row['id_board'],
1585
							),
1586
							array(
1587
								'tag' => 'link',
1588
								'content' => $scripturl . '?board=' . $row['id_board'] . '.0',
1589
							),
1590
						),
1591
					),
1592
					array(
1593
						'tag' => 'link',
1594
						'content' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'],
1595
					),
1596
					array(
1597
						'tag' => 'attachments',
1598
						'content' => $attachments,
1599
					),
1600
				),
1601
			);
1602
		}
1603
	}
1604
	$smcFunc['db_free_result']($request);
1605
1606
	return $data;
1607
}
1608
1609
/**
1610
 * Get the profile information for member into an array,
1611
 * which will be generated to match the xml_format.
1612
 * @todo refactor.
1613
 *
1614
 * @param string $xml_format The XML format. Can be 'atom', 'rdf', 'rss', 'rss2' or 'smf'
1615
 * @return array An array profile data
1616
 */
1617
function getXmlProfile($xml_format)
1618
{
1619
	global $scripturl, $memberContext, $user_profile, $user_info;
1620
1621
	// You must input a valid user....
1622
	if (empty($_GET['u']) || !loadMemberData((int) $_GET['u']))
1623
		return array();
1624
1625
	// Make sure the id is a number and not "I like trying to hack the database".
1626
	$_GET['u'] = (int) $_GET['u'];
1627
	// Load the member's contextual information!
1628
	if (!loadMemberContext($_GET['u']) || !allowedTo('profile_view'))
1629
		return array();
1630
1631
	// Okay, I admit it, I'm lazy.  Stupid $_GET['u'] is long and hard to type.
1632
	$profile = &$memberContext[$_GET['u']];
1633
1634
	// Create a GUID for this member using the tag URI scheme
1635
	$guid = 'tag:' . parse_url($scripturl, PHP_URL_HOST) . ',' . gmdate('Y-m-d', $user_profile[$profile['id']]['date_registered']) . ':member=' . $profile['id'];
1636
1637
	if ($xml_format == 'rss' || $xml_format == 'rss2')
1638
	{
1639
		$data[] = array(
0 ignored issues
show
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...
1640
			'tag' => 'item',
1641
			'content' => array(
1642
				array(
1643
					'tag' => 'title',
1644
					'content' => $profile['name'],
1645
				),
1646
				array(
1647
					'tag' => 'link',
1648
					'content' => $scripturl . '?action=profile;u=' . $profile['id'],
1649
				),
1650
				array(
1651
					'tag' => 'description',
1652
					'content' => isset($profile['group']) ? $profile['group'] : $profile['post_group'],
1653
				),
1654
				array(
1655
					'tag' => 'comments',
1656
					'content' => $scripturl . '?action=pm;sa=send;u=' . $profile['id'],
1657
				),
1658
				array(
1659
					'tag' => 'pubDate',
1660
					'content' => gmdate('D, d M Y H:i:s \G\M\T', $user_profile[$profile['id']]['date_registered']),
1661
				),
1662
				array(
1663
					'tag' => 'guid',
1664
					'content' => $guid,
1665
					'attributes' => array(
1666
						'isPermaLink' => 'false',
1667
					),
1668
				),
1669
			)
1670
		);
1671
	}
1672
	elseif ($xml_format == 'rdf')
1673
	{
1674
		$data[] = array(
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...
1675
			'tag' => 'item',
1676
			'attributes' => array('rdf:about' => $scripturl . '?action=profile;u=' . $profile['id']),
1677
			'content' => array(
1678
				array(
1679
					'tag' => 'dc:format',
1680
					'content' => 'text/html',
1681
				),
1682
				array(
1683
					'tag' => 'title',
1684
					'content' => $profile['name'],
1685
				),
1686
				array(
1687
					'tag' => 'link',
1688
					'content' => $scripturl . '?action=profile;u=' . $profile['id'],
1689
				),
1690
				array(
1691
					'tag' => 'description',
1692
					'content' => isset($profile['group']) ? $profile['group'] : $profile['post_group'],
1693
				),
1694
			)
1695
		);
1696
	}
1697
	elseif ($xml_format == 'atom')
1698
	{
1699
		$data[] = array(
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...
1700
			'tag' => 'entry',
1701
			'content' => array(
1702
				array(
1703
					'tag' => 'title',
1704
					'content' => $profile['name'],
1705
				),
1706
				array(
1707
					'tag' => 'link',
1708
					'attributes' => array(
1709
						'rel' => 'alternate',
1710
						'type' => 'text/html',
1711
						'href' => $scripturl . '?action=profile;u=' . $profile['id'],
1712
					),
1713
				),
1714
				array(
1715
					'tag' => 'summary',
1716
					'attributes' => array('type' => 'html'),
1717
					'content' => isset($profile['group']) ? $profile['group'] : $profile['post_group'],
1718
				),
1719
				array(
1720
					'tag' => 'author',
1721
					'content' => array(
1722
						array(
1723
							'tag' => 'name',
1724
							'content' => $profile['name'],
1725
						),
1726
						array(
1727
							'tag' => 'email',
1728
							'content' => $profile['show_email'] ? $profile['email'] : null,
1729
						),
1730
						array(
1731
							'tag' => 'uri',
1732
							'content' => !empty($profile['website']['url']) ? $profile['website']['url'] : null,
1733
						),
1734
					),
1735
				),
1736
				array(
1737
					'tag' => 'published',
1738
					'content' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $user_profile[$profile['id']]['date_registered']),
1739
				),
1740
				array(
1741
					'tag' => 'updated',
1742
					'content' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $user_profile[$profile['id']]['last_login']),
1743
				),
1744
				array(
1745
					'tag' => 'id',
1746
					'content' => $guid,
1747
				),
1748
			)
1749
		);
1750
	}
1751
	else
1752
	{
1753
		$data = array(
1754
			array(
1755
				'tag' => 'username',
1756
				'content' => $user_info['is_admin'] || $user_info['id'] == $profile['id'] ? $profile['username'] : null,
1757
			),
1758
			array(
1759
				'tag' => 'name',
1760
				'content' => $profile['name'],
1761
			),
1762
			array(
1763
				'tag' => 'link',
1764
				'content' => $scripturl . '?action=profile;u=' . $profile['id'],
1765
			),
1766
			array(
1767
				'tag' => 'posts',
1768
				'content' => $profile['posts'],
1769
			),
1770
			array(
1771
				'tag' => 'post-group',
1772
				'content' => $profile['post_group'],
1773
			),
1774
			array(
1775
				'tag' => 'language',
1776
				'content' => $profile['language'],
1777
			),
1778
			array(
1779
				'tag' => 'last-login',
1780
				'content' => gmdate('D, d M Y H:i:s \G\M\T', $user_profile[$profile['id']]['last_login']),
1781
			),
1782
			array(
1783
				'tag' => 'registered',
1784
				'content' => gmdate('D, d M Y H:i:s \G\M\T', $user_profile[$profile['id']]['date_registered']),
1785
			),
1786
			array(
1787
				'tag' => 'gender',
1788
				'content' => !empty($profile['gender']['name']) ? $profile['gender']['name'] : null,
1789
			),
1790
			array(
1791
				'tag' => 'avatar',
1792
				'content' => !empty($profile['avatar']['url']) ? $profile['avatar']['url'] : null,
1793
			),
1794
			array(
1795
				'tag' => 'online',
1796
				'content' => !empty($profile['online']['is_online']) ? '' : null,
1797
			),
1798
			array(
1799
				'tag' => 'signature',
1800
				'content' => !empty($profile['signature']) ? $profile['signature'] : null,
1801
			),
1802
			array(
1803
				'tag' => 'blurb',
1804
				'content' => !empty($profile['blurb']) ? $profile['blurb'] : null,
1805
			),
1806
			array(
1807
				'tag' => 'title',
1808
				'content' => !empty($profile['title']) ? $profile['title'] : null,
1809
			),
1810
			array(
1811
				'tag' => 'position',
1812
				'content' => !empty($profile['group']) ? $profile['group'] : null,
1813
			),
1814
			array(
1815
				'tag' => 'email',
1816
				'content' => !empty($profile['show_email']) ? $profile['show_email'] : null,
1817
			),
1818
			array(
1819
				'tag' => 'website',
1820
				'content' => empty($profile['website']['url']) ? null : array(
1821
					array(
1822
						'tag' => 'title',
1823
						'content' => !empty($profile['website']['title']) ? $profile['website']['title'] : null,
1824
					),
1825
					array(
1826
						'tag' => 'link',
1827
						'content' => $profile['website']['url'],
1828
					),
1829
				),
1830
			),
1831
		);
1832
1833
		if (!empty($profile['birth_date']) && substr($profile['birth_date'], 0, 4) != '0000')
1834
		{
1835
			list ($birth_year, $birth_month, $birth_day) = sscanf($profile['birth_date'], '%d-%d-%d');
1836
			$datearray = getdate(forum_time());
1837
			$age = $datearray['year'] - $birth_year - (($datearray['mon'] > $birth_month || ($datearray['mon'] == $birth_month && $datearray['mday'] >= $birth_day)) ? 0 : 1);
1838
1839
			$data[] = array(
1840
				'tag' => 'age',
1841
				'content' => $age,
1842
			);
1843
1844
		}
1845
	}
1846
1847
	// Save some memory.
1848
	unset($profile, $memberContext[$_GET['u']]);
1849
1850
	return $data;
1851
}
1852
1853
?>
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...