Issues (1686)

sources/ElkArte/Controller/News.php (6 issues)

1
<?php
2
3
/**
4
 * This file contains the files necessary to display news as an XML feed.
5
 *
6
 * @package   ElkArte Forum
7
 * @copyright ElkArte Forum contributors
8
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file)
9
 *
10
 * This file contains code covered by:
11
 * copyright: 2011 Simple Machines (http://www.simplemachines.org)
12
 *
13
 * @version 2.0 dev
14
 *
15
 */
16
17
namespace ElkArte\Controller;
18
19
use BBC\ParserWrapper;
20
use ElkArte\AbstractController;
21
use ElkArte\Cache\Cache;
22
use ElkArte\Exceptions\Exception;
23
use ElkArte\Helper\Util;
24
use ElkArte\Http\Headers;
25
use ElkArte\Languages\Txt;
26
use ElkArte\MembersList;
27
28
/**
29
 * News Controller class
30
 */
31
class News extends AbstractController
32
{
33
	/** @var string Holds news specific version board query for news feeds */
34
	private $_query_this_board;
35
36
	/** @var int Holds the limit for the number of items to get */
37
	private $_limit;
38
39
	/**
40
	 * {@inheritDoc}
41
	 */
42
	public function trackStats($action = '')
43
	{
44
		if ($action === 'action_showfeed')
45
		{
46
			return false;
47
		}
48
49
		return parent::trackStats($action);
50
	}
51
52
	/**
53
	 * Dispatcher. Forwards to the action to execute.
54
	 *
55
	 * @see AbstractController::action_index
56
	 */
57
	public function action_index()
58
	{
59
		// do... something, of your favorite.
60
		// $this->action_xmlnews();
61
	}
62
63
	/**
64
	 * Outputs xml data representing recent information or a profile.
65
	 *
66
	 * What it does:
67
	 *
68
	 * - Can be passed 4 subactions which decide what is output:
69
	 *     * 'recent' for recent posts,
70
	 *     * 'news' for news topics,
71
	 *     * 'members' for recently registered members,
72
	 *     * 'profile' for a member's profile.
73
	 * - To display a member's profile, a user id has to be given. (;u=1) e.g. ?action=.xml;sa=profile;u=1;type=atom
74
	 * - Outputs a feed based on the 'type'
75
	 *       * parameter is 'rss', 'rss2', 'rdf', 'atom'.
76
	 * - Several sub action options are respected
77
	 *     * limit=x - display the "x" most recent posts
78
	 *     * board=y - display only the recent posts from board "y"
79
	 *     * boards=x,y,z - display only the recent posts from the specified boards
80
	 *     * c=x or c=x,y,z - display only the recent posts from boards in the specified category/categories
81
	 *     * action=.xml;sa=recent;board=2;limit=10
82
	 * - Accessed via ?action=.xml
83
	 * - Does not use any templates, sub templates, or template layers.
84
	 * - Use ;debug to view output for debugging feeds
85
	 *
86
	 * @uses Stats language file.
87
	 */
88
	public function action_showfeed()
89
	{
90
		global $board, $board_info, $context, $txt, $modSettings, $db_show_debug;
91
92
		// If it's not enabled, die.
93
		if (empty($modSettings['xmlnews_enable']))
94
		{
95
			obExit(false);
96
		}
97
98
		// This is just here to make it easier for the developers :P
99
		$db_show_debug = false;
100
101
		require_once(SUBSDIR . '/News.subs.php');
102
103
		Txt::load('Stats');
104
		$txt['xml_rss_desc'] = replaceBasicActionUrl($txt['xml_rss_desc']);
105
106
		// Default to latest 5.  No more than what is defined in the ACP or 255
107
		$limit = empty($modSettings['xmlnews_limit']) ? 5 : min($modSettings['xmlnews_limit'], 255);
108
		$this->_limit = min($this->_req->getQuery('limit', 'intval', $limit), $limit);
109
110
		// Handle the cases where a board, boards, or category is asked for.
111
		$this->_query_this_board = '1=1';
112
		$context['optimize_msg'] = array(
113
			'highest' => 'm.id_msg <= b.id_last_msg',
114
		);
115
116
		// Specifying specific categories only?
117
		if (!empty($this->_req->query->c) && empty($board))
118
		{
119
			$categories = array_map('intval', explode(',', $this->_req->query->c));
120
121
			if (count($categories) === 1)
122
			{
123
				require_once(SUBSDIR . '/Categories.subs.php');
124
				$feed_title = categoryName($categories[0]);
125
				$feed_title = ' - ' . strip_tags($feed_title);
126
			}
127
128
			require_once(SUBSDIR . '/Boards.subs.php');
129
			$boards_posts = boardsPosts(array(), $categories);
130
			$total_cat_posts = array_sum($boards_posts);
131
			$boards = array_keys($boards_posts);
132
133
			if (!empty($boards))
134
			{
135
				$this->_query_this_board = 'b.id_board IN (' . implode(', ', $boards) . ')';
136
			}
137
138
			// Try to limit the number of messages we look through.
139
			if ($total_cat_posts > 100 && $total_cat_posts > $modSettings['totalMessages'] / 15)
140
			{
141
				$context['optimize_msg']['lowest'] = 'm.id_msg >= ' . max(0, $modSettings['maxMsgID'] - 400 - $this->_limit * 5);
142
			}
143
		}
144
		// Maybe they only want to see feeds form some certain boards?
145
		elseif (!empty($this->_req->query->boards))
146
		{
147
			require_once(SUBSDIR . '/Boards.subs.php');
148
			$query_boards = array_map('intval', explode(',', $this->_req->query->boards));
149
150
			$boards_data = fetchBoardsInfo(array('boards' => $query_boards), array('selects' => 'detailed'));
151
152
			// Either the board specified doesn't exist or you have no access.
153
			$num_boards = count($boards_data);
154
			if ($num_boards === 0)
155
			{
156
				throw new Exception('no_board');
157
			}
158
159
			$total_posts = 0;
160
			$boards = array_keys($boards_data);
161
			foreach ($boards_data as $row)
162
			{
163
				if ($num_boards === 1)
164
				{
165
					$feed_title = ' - ' . strip_tags($row['name']);
166
				}
167
168
				$total_posts += $row['num_posts'];
169
			}
170
171
			$this->_query_this_board = 'b.id_board IN (' . implode(', ', $boards) . ')';
172
173
			// The more boards, the more we're going to look through...
174
			if ($total_posts > 100 && $total_posts > $modSettings['totalMessages'] / 12)
175
			{
176
				$context['optimize_msg']['lowest'] = 'm.id_msg >= ' . max(0, $modSettings['maxMsgID'] - 500 - $this->_limit * 5);
177
			}
178
		}
179
		// Just a single board
180
		elseif (!empty($board))
181
		{
182
			require_once(SUBSDIR . '/Boards.subs.php');
183
			$boards_data = fetchBoardsInfo(array('boards' => $board), array('selects' => 'posts'));
184
185
			$feed_title = ' - ' . strip_tags($board_info['name']);
186
187
			$this->_query_this_board = 'b.id_board = ' . $board;
188
189
			// Try to look through just a few messages, if at all possible.
190
			if ($boards_data[(int) $board]['num_posts'] > 80 && $boards_data[(int) $board]['num_posts'] > $modSettings['totalMessages'] / 10)
191
			{
192
				$context['optimize_msg']['lowest'] = 'm.id_msg >= ' . max(0, $modSettings['maxMsgID'] - 600 - $this->_limit * 5);
193
			}
194
		}
195
		else
196
		{
197
			$this->_query_this_board = '{query_see_board}' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? '
198
				AND b.id_board != ' . $modSettings['recycle_board'] : '');
199
			$context['optimize_msg']['lowest'] = 'm.id_msg >= ' . max(0, $modSettings['maxMsgID'] - 100 - $this->_limit * 5);
200
		}
201
202
		// If format isn't set, or is wrong, rss2 is default
203
		$xml_format = $this->_req->getQuery('type', 'trim', 'rss2');
204
		if (!in_array($xml_format, array('rss', 'rss2', 'atom', 'rdf')))
205
		{
206
			$xml_format = 'rss2';
207
		}
208
209
		// List all the different types of data they can pull.
210
		$subActions = array(
211
			'recent' => array('action_xmlrecent'),
212
			'news' => array('action_xmlnews'),
213
			'members' => array('action_xmlmembers'),
214
			'profile' => array('action_xmlprofile'),
215
		);
216
217
		// Easy adding of sub actions
218
		call_integration_hook('integrate_xmlfeeds', array(&$subActions));
219
220
		$subAction = $this->_req->getQuery('sa', 'strtolower', 'recent');
221
		$subAction = isset($subActions[$subAction]) ? $subAction : 'recent';
222
223
		// We only want some information, not all of it.
224
		$cachekey = array($xml_format, $this->_req->query->action, $this->_limit, $subAction);
225
		foreach (array('board', 'boards', 'c') as $var)
226
		{
227
			if (isset($this->_req->query->{$var}))
228
			{
229
				$cachekey[] = $this->_req->query->{$var};
230
			}
231
		}
232
233
		$cachekey = md5(serialize($cachekey) . (empty($this->_query_this_board) ? '' : $this->_query_this_board));
234
		$cache_t = microtime(true);
235
		$cache = Cache::instance();
236
237
		// Get the associative array representing the xml.
238
		if ($this->user->is_guest === false || $cache->levelHigherThan(2))
0 ignored issues
show
Bug Best Practice introduced by
The property is_guest does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
239
		{
240
			$xml = $cache->get('xmlfeed-' . $xml_format . ':' . ($this->user->is_guest ? '' : $this->user->id . '-') . $cachekey, 240);
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
241
		}
242
243
		if (empty($xml))
244
		{
245
			$xml = $this->{$subActions[$subAction][0]}($xml_format);
246
247
			if ($cache->isEnabled() && (($this->user->is_guest && $cache->levelHigherThan(2)) || ($this->user->is_guest === false && (microtime(true) - $cache_t > 0.2))))
248
			{
249
				$cache->put('xmlfeed-' . $xml_format . ':' . ($this->user->is_guest ? '' : $this->user->id . '-') . $cachekey, $xml, 240);
250
			}
251
		}
252
253
		$context['feed_title'] = encode_special(strip_tags(un_htmlspecialchars($context['forum_name']) . ($feed_title ?? '')));
254
255
		// We send a feed with recent posts, and alerts for PMs for logged-in users
256
		$context['recent_posts_data'] = $xml;
257
		$context['xml_format'] = $xml_format;
258
259
		obStart(!empty($modSettings['enableCompressedOutput']));
260
261
		// This is an xml file....
262
		$headers = Headers::instance();
263
		if (isset($this->_req->query->debug))
264
		{
265
			$headers->contentType('text/xml', 'UTF-8');
266
		}
267
		elseif ($xml_format === 'rss' || $xml_format === 'rss2')
268
		{
269
			$headers->contentType('application/rss+xml', 'UTF-8');
270
		}
271
		elseif ($xml_format === 'atom')
272
		{
273
			$headers->contentType('application/atom+xml', 'UTF-8');
274
		}
275
		elseif ($xml_format === 'rdf')
276
		{
277
			$headers->contentType('application/rdf+xml', 'UTF-8');
278
		}
279
280
		// Set our own 30min cache control so auto-readers know how often to check in
281
		$context['no_last_modified'] = true;
282
		$headers
283
			->header('Cache-Control', 'max-age=' . (3600 * .5) . ', private')
284
			->header('Last-Modified', gmdate('D, d M Y H:i:s') . ' GMT');
285
286
		theme()->getTemplates()->load('Xml');
287
		theme()->getLayers()->removeAll();
288
289
		// Are we outputting a rss feed or one with more information?
290
		if ($xml_format === 'rss' || $xml_format === 'rss2')
291
		{
292
			$context['sub_template'] = 'feedrss';
293
		}
294
		elseif ($xml_format === 'atom')
295
		{
296
			$url_parts = array();
297
			foreach (array('board', 'boards', 'c') as $var)
298
			{
299
				if (isset($this->_req->query->{$var}))
300
				{
301
					$url_parts[] = $var . '=' . (is_array($this->_req->query->{$var}) ? implode(',', $this->_req->query->{$var}) : $this->_req->query->{$var});
302
				}
303
			}
304
305
			$context['url_parts'] = empty($url_parts) ? '' : implode(';', $url_parts);
306
			$context['sub_template'] = 'feedatom';
307
		}
308
		// rdf by default
309
		else
310
		{
311
			$context['sub_template'] = 'rdf';
312
		}
313
	}
314
315
	/**
316
	 * Retrieve the list of members from database.
317
	 * The array will be generated to match the format.
318
	 *
319
	 * @param string $xml_format
320
	 * @return array
321
	 */
322
	public function action_xmlmembers($xml_format)
323
	{
324
		global $scripturl;
325
326
		// Not allowed, then you get nothing
327
		if (!allowedTo('view_mlist'))
328
		{
329
			return array();
330
		}
331
332
		// Find the most recent members.
333
		require_once(SUBSDIR . '/Members.subs.php');
334
		$members = recentMembers((int) $this->_limit);
335
336
		// No data yet
337
		$data = array();
338
339
		require_once(SUBSDIR . '/News.subs.php');
340
		foreach ($members as $member)
341
		{
342
			// Make the data look rss-ish.
343
			if ($xml_format === 'rss' || $xml_format === 'rss2')
344
			{
345
				$data[] = array(
346
					'title' => cdata_parse($member['real_name']),
347
					'link' => $scripturl . '?action=profile;u=' . $member['id_member'],
348
					'comments' => $scripturl . '?action=pm;sa=send;u=' . $member['id_member'],
349
					'pubDate' => gmdate('D, d M Y H:i:s \G\M\T', $member['date_registered']),
350
					'guid' => $scripturl . '?action=profile;u=' . $member['id_member'],
351
				);
352
			}
353
			elseif ($xml_format === 'rdf')
354
			{
355
				$data[] = array(
356
					'title' => cdata_parse($member['real_name']),
357
					'link' => $scripturl . '?action=profile;u=' . $member['id_member'],
358
				);
359
			}
360
			elseif ($xml_format === 'atom')
361
			{
362
				$data[] = array(
363
					'title' => cdata_parse($member['real_name']),
364
					'link' => $scripturl . '?action=profile;u=' . $member['id_member'],
365
					'published' => Util::gmstrftime('%Y-%m-%dT%H:%M:%SZ', $member['date_registered']),
366
					'updated' => Util::gmstrftime('%Y-%m-%dT%H:%M:%SZ', $member['last_login']),
367
					'id' => $scripturl . '?action=profile;u=' . $member['id_member'],
368
				);
369
			}
370
			// More logical format for the data, but harder to apply.
371
			else
372
			{
373
				$data[] = array(
374
					'name' => cdata_parse($member['real_name']),
375
					'time' => htmlspecialchars(strip_tags(standardTime($member['date_registered'])), ENT_COMPAT, 'UTF-8'),
376
					'id' => $member['id_member'],
377
					'link' => $scripturl . '?action=profile;u=' . $member['id_member']
378
				);
379
			}
380
		}
381
382
		return $data;
383
	}
384
385
	/**
386
	 * Get the latest topics information from a specific board, to display later.
387
	 * The returned array will be generated to match the xmf_format.
388
	 *
389
	 * @param string $xml_format one of rss, rss2, rdf, atom
390
	 * @return array array of topics
391
	 */
392
	public function action_xmlnews($xml_format)
393
	{
394
		global $scripturl, $modSettings, $board;
395
396
		// Get the latest topics from a board
397
		require_once(SUBSDIR . '/News.subs.php');
398
		$results = getXMLNews($this->_query_this_board, $board, $this->_limit);
399
400
		// Prepare it for the feed in the format chosen (rss, atom, etc)
401
		$data = array();
402
		$bbc_parser = ParserWrapper::instance();
403
404
		foreach ($results as $row)
405
		{
406
			// Limit the length of the message, if the option is set.
407
			if (!empty($modSettings['xmlnews_maxlen']) && Util::strlen(str_replace('<br />', "\n", $row['body'])) > $modSettings['xmlnews_maxlen'])
408
			{
409
				$row['body'] = strtr(Util::shorten_text(str_replace('<br />', "\n", $row['body']), $modSettings['xmlnews_maxlen'], true), array("\n" => '<br />'));
410
			}
411
412
			$row['body'] = $bbc_parser->parseMessage($row['body'], $row['smileys_enabled']);
413
414
			// Dirty mouth?
415
			$row['body'] = censor($row['body']);
416
			$row['subject'] = censor($row['subject']);
417
418
			// Being news, this actually makes sense in rss format.
419
			if ($xml_format === 'rss' || $xml_format === 'rss2')
420
			{
421
				$data[] = array(
422
					'title' => cdata_parse($row['subject']),
423
					'link' => $scripturl . '?topic=' . $row['id_topic'] . '.0',
424
					'description' => cdata_parse(str_replace('&', '&#x26;', un_htmlspecialchars($row['body']))),
425
					'author' => showEmailAddress($row['id_member']) ? $row['poster_email'] . ' (' . un_htmlspecialchars($row['poster_name']) . ')' : '<![CDATA[[email protected] (' . un_htmlspecialchars($row['poster_name']) . ')]]>',
426
					'comments' => $scripturl . '?action=post;topic=' . $row['id_topic'] . '.0',
427
					'category' => '<![CDATA[' . $row['bname'] . ']]>',
428
					'pubDate' => gmdate('D, d M Y H:i:s \G\M\T', $row['poster_time']),
429
					'guid' => $scripturl . '?topic=' . $row['id_topic'] . '.0',
430
				);
431
432
				// Add the poster name on if we are rss2
433
				if ($xml_format === 'rss2')
434
				{
435
					$data[count($data) - 1]['dc:creator'] = $row['poster_name'];
436
					unset($data[count($data) - 1]['author']);
437
				}
438
			}
439
			// RDF Format anyone
440
			elseif ($xml_format === 'rdf')
441
			{
442
				$data[] = array(
443
					'title' => cdata_parse($row['subject']),
444
					'link' => $scripturl . '?topic=' . $row['id_topic'] . '.0',
445
					'description' => cdata_parse($row['body']),
446
				);
447
			}
448
			// Atom feed
449
			elseif ($xml_format === 'atom')
450
			{
451
				$data[] = array(
452
					'title' => cdata_parse($row['subject']),
453
					'link' => $scripturl . '?topic=' . $row['id_topic'] . '.0',
454
					'summary' => cdata_parse($row['body']),
455
					'category' => $row['bname'],
456
					'author' => array(
457
						'name' => $row['poster_name'],
458
						'email' => showEmailAddress($row['id_member']) ? $row['poster_email'] : null,
459
						'uri' => empty($row['id_member']) ? '' : $scripturl . '?action=profile;u=' . $row['id_member'],
460
					),
461
					'published' => Util::gmstrftime('%Y-%m-%dT%H:%M:%SZ', $row['poster_time']),
462
					'modified' => Util::gmstrftime('%Y-%m-%dT%H:%M:%SZ', empty($row['modified_time']) ? $row['poster_time'] : $row['modified_time']),
463
					'id' => $scripturl . '?topic=' . $row['id_topic'] . '.0',
464
				);
465
			}
466
			// The biggest difference here is more information.
467
			else
468
			{
469
				$data[] = array(
470
					'time' => htmlspecialchars(strip_tags(standardTime($row['poster_time'])), ENT_COMPAT, 'UTF-8'),
471
					'id' => $row['id_topic'],
472
					'subject' => cdata_parse($row['subject']),
473
					'body' => cdata_parse($row['body']),
474
					'poster' => array(
475
						'name' => cdata_parse($row['poster_name']),
476
						'id' => $row['id_member'],
477
						'link' => empty($row['id_member']) ? '' : $scripturl . '?action=profile;u=' . $row['id_member'],
478
					),
479
					'topic' => $row['id_topic'],
480
					'board' => array(
481
						'name' => cdata_parse($row['bname']),
482
						'id' => $row['id_board'],
483
						'link' => $scripturl . '?board=' . $row['id_board'] . '.0',
484
					),
485
					'link' => $scripturl . '?topic=' . $row['id_topic'] . '.0',
486
				);
487
			}
488
		}
489
490
		return $data;
491
	}
492
493
	/**
494
	 * Get the recent topics to display.
495
	 * The returned array will be generated to match the xml_format.
496
	 *
497
	 * @param string $xml_format one of rss, rss2, rdf, atom
498
	 * @return array of recent posts
499
	 */
500
	public function action_xmlrecent($xml_format)
501
	{
502
		global $scripturl, $modSettings, $board;
503
504
		// Get the latest news
505
		require_once(SUBSDIR . '/News.subs.php');
506
		$results = getXMLRecent($this->_query_this_board, $board, $this->_limit);
507
508
		// Loop on the results and prepare them in the format requested
509
		$data = array();
510
		$bbc_parser = ParserWrapper::instance();
511
512
		foreach ($results as $row)
513
		{
514
			// Limit the length of the message, if the option is set.
515
			if (!empty($modSettings['xmlnews_maxlen']) && Util::strlen(str_replace('<br />', "\n", $row['body'])) > $modSettings['xmlnews_maxlen'])
516
			{
517
				$row['body'] = strtr(Util::shorten_text(str_replace('<br />', "\n", $row['body']), $modSettings['xmlnews_maxlen'], true), array("\n" => '<br />'));
518
			}
519
520
			$row['body'] = $bbc_parser->parseMessage($row['body'], $row['smileys_enabled']);
521
522
			// You can't say that
523
			$row['body'] = censor($row['body']);
524
			$row['subject'] = censor($row['subject']);
525
526
			// Doesn't work as well as news, but it kinda does..
527
			if ($xml_format === 'rss' || $xml_format === 'rss2')
528
			{
529
				$data[] = array(
530
					'title' => $row['subject'],
531
					'link' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'],
532
					'description' => cdata_parse(strtr(un_htmlspecialchars($row['body']), '&', '&#x26;')),
533
					'author' => showEmailAddress($row['id_member']) ? $row['poster_email'] . ' (' . un_htmlspecialchars($row['poster_name']) . ')' : '<![CDATA[[email protected] (' . un_htmlspecialchars($row['poster_name']) . ')]]>',
534
					'category' => cdata_parse($row['bname']),
535
					'comments' => $scripturl . '?action=post;topic=' . $row['id_topic'] . '.0',
536
					'pubDate' => gmdate('D, d M Y H:i:s \G\M\T', $row['poster_time']),
537
					'guid' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg']
538
				);
539
540
				// Add the poster name on if we are rss2
541
				if ($xml_format === 'rss2')
542
				{
543
					$data[count($data) - 1]['dc:creator'] = $row['poster_name'];
544
					unset($data[count($data) - 1]['author']);
545
				}
546
			}
547
			elseif ($xml_format === 'rdf')
548
			{
549
				$data[] = array(
550
					'title' => $row['subject'],
551
					'link' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'],
552
					'description' => cdata_parse($row['body']),
553
				);
554
			}
555
			elseif ($xml_format === 'atom')
556
			{
557
				$data[] = array(
558
					'title' => $row['subject'],
559
					'link' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'],
560
					'summary' => cdata_parse($row['body']),
561
					'category' => $row['bname'],
562
					'author' => array(
563
						'name' => $row['poster_name'],
564
						'email' => showEmailAddress($row['id_member']) ? $row['poster_email'] : null,
565
						'uri' => empty($row['id_member']) ? '' : $scripturl . '?action=profile;u=' . $row['id_member']
566
					),
567
					'published' => Util::gmstrftime('%Y-%m-%dT%H:%M:%SZ', $row['poster_time']),
568
					'updated' => Util::gmstrftime('%Y-%m-%dT%H:%M:%SZ', empty($row['modified_time']) ? $row['poster_time'] : $row['modified_time']),
569
					'id' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'],
570
				);
571
			}
572
			// A lot of information here.  Should be enough to please the rss-ers.
573
			else
574
			{
575
				$data[] = array(
576
					'time' => htmlspecialchars(strip_tags(standardTime($row['poster_time'])), ENT_COMPAT, 'UTF-8'),
577
					'id' => $row['id_msg'],
578
					'subject' => cdata_parse($row['subject']),
579
					'body' => cdata_parse($row['body']),
580
					'starter' => array(
581
						'name' => cdata_parse($row['first_poster_name']),
582
						'id' => $row['id_first_member'],
583
						'link' => empty($row['id_first_member']) ? '' : $scripturl . '?action=profile;u=' . $row['id_first_member']
584
					),
585
					'poster' => array(
586
						'name' => cdata_parse($row['poster_name']),
587
						'id' => $row['id_member'],
588
						'link' => empty($row['id_member']) ? '' : $scripturl . '?action=profile;u=' . $row['id_member']
589
					),
590
					'topic' => array(
591
						'subject' => cdata_parse($row['first_subject']),
592
						'id' => $row['id_topic'],
593
						'link' => $scripturl . '?topic=' . $row['id_topic'] . '.new#new'
594
					),
595
					'board' => array(
596
						'name' => cdata_parse($row['bname']),
597
						'id' => $row['id_board'],
598
						'link' => $scripturl . '?board=' . $row['id_board'] . '.0'
599
					),
600
					'link' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg']
601
				);
602
			}
603
		}
604
605
		return $data;
606
	}
607
608
	/**
609
	 * Get the profile information for member into an array,
610
	 * which will be generated to match the xml_format.
611
	 *
612
	 * @param string $xml_format one of rss, rss2, rdf, atom
613
	 * @return array array of profile data.
614
	 */
615
	public function action_xmlprofile($xml_format)
616
	{
617
		global $scripturl, $modSettings, $language;
618
619
		// You must input a valid user....
620
		if (empty($this->_req->query->u))
621
		{
622
			return array();
623
		}
624
625
		// Make sure the id is a number and not "I like trying to hack the database".
626
		$uid = (int) $this->_req->query->u;
627
628
		// You must input a valid user....
629
		if (MembersList::load($uid) === false)
630
		{
631
			return array();
632
		}
633
634
		// Load the member's contextual information!
635
		if (!allowedTo('profile_view_any'))
636
		{
637
			return array();
638
		}
639
640
		$member = MembersList::get($uid);
641
		$member->loadContext();
642
643
		// No feed data yet
644
		$data = array();
645
646
		require_once(SUBSDIR . '/News.subs.php');
647
		if ($xml_format === 'rss' || $xml_format === 'rss2')
648
		{
649
			$data = array(array(
650
				'title' => cdata_parse($member['name']),
651
				'link' => $scripturl . '?action=profile;u=' . $member['id'],
652
				'description' => cdata_parse($member['group'] ?? $member['post_group']),
653
				'comments' => $scripturl . '?action=pm;sa=send;u=' . $member['id'],
654
				'pubDate' => gmdate('D, d M Y H:i:s \G\M\T', $member->date_registered),
0 ignored issues
show
Bug Best Practice introduced by
The property date_registered does not exist on anonymous//sources/ElkArte/MembersList.php$0. Since you implemented __get, consider adding a @property annotation.
Loading history...
655
				'guid' => $scripturl . '?action=profile;u=' . $member['id'],
656
			));
657
		}
658
		elseif ($xml_format === 'rdf')
659
		{
660
			$data = array(array(
661
				'title' => cdata_parse($member['name']),
662
				'link' => $scripturl . '?action=profile;u=' . $member['id'],
663
				'description' => cdata_parse($member['group'] ?? $member['post_group']),
664
			));
665
		}
666
		elseif ($xml_format === 'atom')
667
		{
668
			$data[] = array(
669
				'title' => cdata_parse($member['name']),
670
				'link' => $scripturl . '?action=profile;u=' . $member['id'],
671
				'summary' => cdata_parse($member['group'] ?? $member['post_group']),
672
				'author' => array(
673
					'name' => $member['real_name'],
674
					'email' => showEmailAddress($member['id']) ? $member['email'] : null,
675
					'uri' => empty($member['website']) ? '' : $member['website']['url']
676
				),
677
				'published' => Util::gmstrftime('%Y-%m-%dT%H:%M:%SZ', $member->date_registered),
678
				'updated' => Util::gmstrftime('%Y-%m-%dT%H:%M:%SZ', $member->last_login),
0 ignored issues
show
Bug Best Practice introduced by
The property last_login does not exist on anonymous//sources/ElkArte/MembersList.php$0. Since you implemented __get, consider adding a @property annotation.
Loading history...
679
				'id' => $scripturl . '?action=profile;u=' . $member['id'],
680
				'logo' => empty($member['avatar']) ? '' : $member['avatar']['url'],
681
			);
682
		}
683
		else
684
		{
685
			$data = array(
686
				'username' => $this->user->is_admin || $this->user->id == $member['id'] ? cdata_parse($member['username']) : '',
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug Best Practice introduced by
The property is_admin does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
687
				'name' => cdata_parse($member['name']),
688
				'link' => $scripturl . '?action=profile;u=' . $member['id'],
689
				'posts' => $member['posts'],
690
				'post-group' => cdata_parse($member['post_group']),
691
				'language' => cdata_parse(empty($member['language']) ? Util::ucwords(strtr($language, array('_' => ' ', '-utf8' => ''))) : $member['language']),
692
				'last-login' => gmdate('D, d M Y H:i:s \G\M\T', $member->last_login),
693
				'registered' => gmdate('D, d M Y H:i:s \G\M\T', $member->date_registered)
694
			);
695
696
			// Everything below here might not be set, and thus maybe shouldn't be displayed.
697
			if ($member['avatar']['name'] !== '')
698
			{
699
				$data['avatar'] = $member['avatar']['url'];
700
			}
701
702
			// If they are online, show an empty tag... no reason to put anything inside it.
703
			if ($member['online']['is_online'])
704
			{
705
				$data['online'] = '';
706
			}
707
708
			if ($member['signature'] !== '')
709
			{
710
				$data['signature'] = cdata_parse($member['signature']);
711
			}
712
713
			if ($member['title'] !== '')
714
			{
715
				$data['title'] = cdata_parse($member['title']);
716
			}
717
718
			if ($member['website']['title'] !== '')
719
			{
720
				$data['website'] = array(
721
					'title' => cdata_parse($member['website']['title']),
722
					'link' => $member['website']['url']
723
				);
724
			}
725
726
			if ($member['group'] !== '')
727
			{
728
				$data['position'] = cdata_parse($member['group']);
729
			}
730
731
			if (!empty($modSettings['karmaMode']))
732
			{
733
				$data['karma'] = array(
734
					'good' => $member['karma']['good'],
735
					'bad' => $member['karma']['bad']
736
				);
737
			}
738
739
			if ($member['show_email'])
740
			{
741
				$data['email'] = $member['email'];
742
			}
743
744
			if (!empty($member['birth_date']) && strpos($member['birth_date'], '0000') !== 0)
745
			{
746
				[$birth_year, $birth_month, $birth_day] = sscanf($member['birth_date'], '%d-%d-%d');
747
				$datearray = getdate(forum_time());
748
				$data['age'] = $datearray['year'] - $birth_year - (($datearray['mon'] > $birth_month || ($datearray['mon'] === $birth_month && $datearray['mday'] >= $birth_day)) ? 0 : 1);
749
			}
750
		}
751
752
		// Save some memory.
753
		MembersList::unset($uid);
754
755
		return $data;
756
	}
757
}
758