Issues (1686)

sources/subs/News.subs.php (4 issues)

1
<?php
2
3
/**
4
 * Functions to help with managing the site news and newsletters
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
use BBC\ParserWrapper;
18
use ElkArte\Helper\Util;
19
20
/**
21
 * Prepares an array of the forum news items
22
 *
23
 * @return array
24
 * @package News
25
 */
26
function getNews()
27
{
28
	global $modSettings;
29
30
	$admin_current_news = array();
31
32
	$bbc_parser = ParserWrapper::instance();
33
34
	// Ready the current news.
35
	foreach (explode("\n", $modSettings['news']) as $id => $line)
36
	{
37
		$admin_current_news[$id] = array(
38
			'id' => $id,
39
			'unparsed' => un_preparsecode($line),
40
			'parsed' => preg_replace('~<([/]?)form[^>]*?[>]*>~i', '<em class="smalltext">&lt;$1form&gt;</em>', $bbc_parser->parseNews($line)),
41
		);
42
	}
43
44
	$admin_current_news['last'] = array(
45
		'id' => 'last',
46
		'unparsed' => '',
47
		'parsed' => '<div id="moreNewsItems_preview"></div>',
48
	);
49
50
	return $admin_current_news;
51
}
52
53
/**
54
 * Get a list of all full banned users.
55
 *
56
 * - Use their Username and email to find them.
57
 * - Only get the ones that can't login to turn off notification.
58
 *
59
 * @return array
60
 * @package News
61
 */
62
function excludeBannedMembers()
63
{
64
	$db = database();
65
66
	$excludes = array();
67
	$db->fetchQuery('
68
		SELECT 
69
			DISTINCT mem.id_member
70
		FROM {db_prefix}ban_groups AS bg
71
			INNER JOIN {db_prefix}ban_items AS bi ON (bg.id_ban_group = bi.id_ban_group)
72
			INNER JOIN {db_prefix}members AS mem ON (bi.id_member = mem.id_member)
73
		WHERE (bg.cannot_access = {int:cannot_access} OR bg.cannot_login = {int:cannot_login})
74
			AND (bg.expire_time IS NULL OR bg.expire_time > {int:current_time})',
75
		array(
76
			'cannot_access' => 1,
77
			'cannot_login' => 1,
78
			'current_time' => time(),
79
		)
80
	)->fetch_callback(
81
		function ($row) use (&$excludes) {
82
			$excludes[] = $row['id_member'];
83
		}
84
	);
85
86
	$condition_array = array();
87
	$condition_array_params = array();
88
	$count = 0;
89
	$db->fetchQuery('
90
		SELECT DISTINCT bi.email_address
91
		FROM {db_prefix}ban_items AS bi
92
			INNER JOIN {db_prefix}ban_groups AS bg ON (bg.id_ban_group = bi.id_ban_group)
93
		WHERE (bg.cannot_access = {int:cannot_access} OR bg.cannot_login = {int:cannot_login})
94
			AND (bg.expire_time IS NULL OR bg.expire_time > {int:current_time})
95
			AND bi.email_address != {string:blank_string}',
96
		array(
97
			'cannot_access' => 1,
98
			'cannot_login' => 1,
99
			'current_time' => time(),
100
			'blank_string' => '',
101
		)
102
	)->fetch_callback(
103
		function ($row) use (&$condition_array, &$condition_array_params, &$count) {
104
			$condition_array[] = '{string:email_' . $count . '}';
105
			$condition_array_params['email_' . ($count++)] = $row['email_address'];
106
		}
107
	);
108
109
	if (!empty($condition_array))
110
	{
111
		$db->fetchQuery('
112
			SELECT 
113
				id_member
114
			FROM {db_prefix}members
115
			WHERE email_address IN(' . implode(', ', $condition_array) . ')',
116
			$condition_array_params
117
		)->fetch_callback(
118
			function ($row) use (&$excludes) {
119
				$excludes[] = $row['id_member'];
120
			}
121
		);
122
	}
123
124
	return $excludes;
125
}
126
127
/**
128
 * Get a list of our local board moderators.
129
 *
130
 * @return array
131
 * @package News
132
 */
133
function getModerators()
134
{
135
	$db = database();
136
137
	$mods = array();
138
139
	$db->fetchQuery('
140
		SELECT 
141
			DISTINCT mem.id_member AS identifier
142
		FROM {db_prefix}members AS mem
143
			INNER JOIN {db_prefix}moderators AS mods ON (mods.id_member = mem.id_member)
144
		WHERE mem.is_activated = {int:is_activated}',
145
		array(
146
			'is_activated' => 1,
147
		)
148
	)->fetch_callback(
149
		function ($row) use (&$mods) {
150
			$mods[] = $row['identifier'];
151
		}
152
	);
153
154
	return $mods;
155
}
156
157
/**
158
 * Lists our newsletter recipients, step by step.
159
 *
160
 * @param string $sendQuery
161
 * @param mixed[] $sendParams
162
 * @param int $start
163
 * @param int $increment
164
 * @param int $counter
165
 * @return array
166
 * @package News
167
 */
168
function getNewsletterRecipients($sendQuery, $sendParams, $start, $increment, $counter)
169
{
170
	$db = database();
171
172
	$recipients = array();
173
174
	$db->fetchQuery('
175
		SELECT 
176
			mem.id_member, mem.email_address, mem.real_name, mem.id_group, mem.additional_groups, mem.id_post_group
177
		FROM {db_prefix}members AS mem
178
		WHERE mem.id_member > {int:min_id_member}
179
			AND mem.id_member < {int:max_id_member}
180
			AND ' . $sendQuery . '
181
			AND mem.is_activated = {int:is_activated}
182
		ORDER BY mem.id_member ASC
183
		LIMIT {int:atonce}',
184
		array_merge($sendParams, array(
185
			'min_id_member' => $start,
186
			'max_id_member' => $start + $increment - $counter,
187
			'atonce' => $increment - $counter,
188
			'regular_group' => 0,
189
			'notify_announcements' => 1,
190
			'is_activated' => 1,
191
		))
192
	)->fetch_callback(
193
		function ($row) use (&$recipients) {
194
			$recipients[] = $row;
195
		}
196
	);
197
198
	return $recipients;
199
}
200
201
/**
202
 * Find the latest posts that:
203
 * - are the first post in their topic.
204
 * - are on an any board OR in a specified board.
205
 * - can be seen by this user.
206
 * - are actually the latest posts.
207
 *
208
 * @param string $query_this_board passed to query, assumed raw and inserted as such
209
 * @param int $board
210
 * @param int $limit
211
 *
212
 * @return array
213
 * @package News
214
 *
215
 */
216
function getXMLNews($query_this_board, $board, $limit)
0 ignored issues
show
The parameter $board is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

216
function getXMLNews($query_this_board, /** @scrutinizer ignore-unused */ $board, $limit)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
217
{
218
	global $modSettings, $board, $context;
219
220
	$db = database();
221
222
	$done = false;
223
	$loops = 0;
224
	while (!$done)
225
	{
226
		$optimize_msg = implode(' AND ', $context['optimize_msg']);
227
		$request = $db->query('', '
228
			SELECT
229
				m.smileys_enabled, m.poster_time, m.id_msg, m.subject, m.body, m.modified_time,
230
				m.icon, t.id_topic, t.id_board, t.num_replies,
231
				b.name AS bname,
232
				COALESCE(mem.id_member, 0) AS id_member,
233
				COALESCE(mem.email_address, m.poster_email) AS poster_email,
234
				COALESCE(mem.real_name, m.poster_name) AS poster_name
235
			FROM {db_prefix}topics AS t
236
				INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
237
				INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
238
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
239
			WHERE ' . $query_this_board . (empty($optimize_msg) ? '' : '
240
				AND {raw:optimize_msg}') . (empty($board) ? '' : '
241
				AND t.id_board = {int:current_board}') . ($modSettings['postmod_active'] ? '
242
				AND t.approved = {int:is_approved}' : '') . '
243
			ORDER BY t.id_first_msg DESC
244
			LIMIT {int:limit}',
245
			array(
246
				'current_board' => $board,
247
				'is_approved' => 1,
248
				'limit' => $limit,
249
				'optimize_msg' => $optimize_msg,
250
			)
251
		);
252
		// If we don't have $limit results, we try again with an unoptimized version covering all rows.
253
		if ($loops < 2 && $request->num_rows() < $limit)
254
		{
255
			$request->free_result();
256
257
			if (empty($_REQUEST['boards']) && empty($board))
258
			{
259
				unset($context['optimize_msg']['lowest']);
260
			}
261
			else
262
			{
263
				$context['optimize_msg']['lowest'] = 'm.id_msg >= t.id_first_msg';
264
			}
265
266
			$context['optimize_msg']['highest'] = 'm.id_msg <= t.id_last_msg';
267
			$loops++;
268
		}
269
		else
270
		{
271
			$done = true;
272
		}
273
	}
274
	$data = array();
275
	while (($row = $request->fetch_assoc()))
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $request does not seem to be defined for all execution paths leading up to this point.
Loading history...
276
	{
277
		$data[] = $row;
278
	}
279
280
	$request->free_result();
281
282
	return $data;
283
}
284
285
/**
286
 * Get the recent topics to display.
287
 *
288
 * @param string $query_this_board passed to query, assumed raw and inserted as such
289
 * @param int $board
290
 * @param int $limit
291
 *
292
 * @return array
293
 * @package News
294
 *
295
 */
296
function getXMLRecent($query_this_board, $board, $limit)
0 ignored issues
show
The parameter $board is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

296
function getXMLRecent($query_this_board, /** @scrutinizer ignore-unused */ $board, $limit)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
297
{
298
	global $modSettings, $board, $context;
299
300
	$db = database();
301
302
	$done = false;
303
	$loops = 0;
304
	while (!$done)
305
	{
306
		$optimize_msg = implode(' AND ', $context['optimize_msg']);
307
		$request = $db->query('', '
308
			SELECT
309
			 	m.id_msg
310
			FROM {db_prefix}messages AS m
311
				INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
312
				INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
313
			WHERE ' . $query_this_board . (empty($optimize_msg) ? '' : '
314
				AND {raw:optimize_msg}') . (empty($board) ? '' : '
315
				AND m.id_board = {int:current_board}') . ($modSettings['postmod_active'] ? '
316
				AND m.approved = {int:is_approved}' : '') . '
317
			ORDER BY m.id_msg DESC
318
			LIMIT {int:limit}',
319
			array(
320
				'limit' => $limit,
321
				'current_board' => $board,
322
				'is_approved' => 1,
323
				'optimize_msg' => $optimize_msg,
324
			)
325
		);
326
		// If we don't have $limit results, try again with an unoptimized version covering all rows.
327
		if ($loops < 2 && $request->num_rows() < $limit)
328
		{
329
			$request->free_result();
330
331
			if (empty($_REQUEST['boards']) && empty($board))
332
			{
333
				unset($context['optimize_msg']['lowest']);
334
			}
335
			else
336
			{
337
				$context['optimize_msg']['lowest'] = $loops !== 0 ? 'm.id_msg >= t.id_first_msg' : 'm.id_msg >= (t.id_last_msg - t.id_first_msg) / 2';
338
			}
339
340
			$loops++;
341
		}
342
		else
343
		{
344
			$done = true;
345
		}
346
	}
347
	$messages = array();
348
	while (($row = $request->fetch_assoc()))
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $request does not seem to be defined for all execution paths leading up to this point.
Loading history...
349
	{
350
		$messages[] = $row['id_msg'];
351
	}
352
	$request->free_result();
353
354
	// No messages found, then return nothing
355
	if (empty($messages))
356
	{
357
		return array();
358
	}
359
360
	// Find the most recent posts from our message list that this user can see.
361
	$data = array();
362
	$db->fetchQuery('
363
		SELECT
364
			m.smileys_enabled, m.poster_time, m.id_msg, m.subject, m.body, m.id_topic, t.id_board,
365
			b.name AS bname, t.num_replies, m.id_member, m.icon, mf.id_member AS id_first_member,
366
			COALESCE(mem.real_name, m.poster_name) AS poster_name, mf.subject AS first_subject,
367
			COALESCE(memf.real_name, mf.poster_name) AS first_poster_name,
368
			COALESCE(mem.email_address, m.poster_email) AS poster_email, m.modified_time
369
		FROM {db_prefix}messages AS m
370
			INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
371
			INNER JOIN {db_prefix}messages AS mf ON (mf.id_msg = t.id_first_msg)
372
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
373
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
374
			LEFT JOIN {db_prefix}members AS memf ON (memf.id_member = mf.id_member)
375
		WHERE m.id_msg IN ({array_int:message_list})
376
			' . (empty($board) ? '' : 'AND t.id_board = {int:current_board}') . '
377
		ORDER BY m.id_msg DESC
378
		LIMIT {int:limit}',
379
		array(
380
			'limit' => $limit,
381
			'current_board' => $board,
382
			'message_list' => $messages,
383
		)
384
	)->fetch_callback(
385
		function ($row) use (&$data) {
386
			$data[] = $row;
387
		}
388
	);
389
390
	return $data;
391
}
392
393
/**
394
 * Called to convert data to xml
395
 * Finds urls for local site and sanitizes them
396
 *
397
 * @param string $val
398
 *
399
 * @return string
400
 */
401
function fix_possible_url($val)
402
{
403
	global $scripturl;
404
405
	if (substr($val, 0, strlen($scripturl)) !== $scripturl)
406
	{
407
		return $val;
408
	}
409
410
	call_integration_hook('integrate_fix_url', array(&$val));
411
412
	return $val;
413
}
414
415
/**
416
 * For highest feed compatibility, some special characters should be provided
417
 * as character entities and not html entities
418
 *
419
 * @param string $data
420
 *
421
 * @return string
422
 */
423
function encode_special($data)
424
{
425
	return strtr($data, array('>' => '&#x3E;', '&' => '&#x26;', '<' => '&#x3C;'));
426
}
427
428
/**
429
 * Ensures supplied data is properly encapsulated in cdata xml tags
430
 * Called from action_xmlprofile in News.controller.php
431
 *
432
 * @param string $data
433
 * @param string $ns
434
 * @param string $override
435
 *
436
 * @return string
437
 */
438
function cdata_parse($data, $ns = '', $override = null)
439
{
440
	static $cdata_override = false;
441
442
	if ($override !== null)
443
	{
444
		$cdata_override = (bool) $override;
445
	}
446
447
	// Are we not doing it?
448
	if (!empty($cdata_override))
449
	{
450
		return $data;
451
	}
452
453
	$cdata = '<![CDATA[';
454
455
	for ($pos = 0, $n = Util::strlen($data); $pos < $n; null)
456
	{
457
		$positions = array(
458
			Util::strpos($data, '&', $pos),
459
			Util::strpos($data, ']]>', $pos),
460
		);
461
462
		if ($ns !== '')
463
		{
464
			$positions[] = Util::strpos($data, '<', $pos);
465
		}
466
467
		foreach ($positions as $k => $dummy)
468
		{
469
			if ($dummy === false)
470
			{
471
				unset($positions[$k]);
472
			}
473
		}
474
475
		$old = $pos;
476
		$pos = empty($positions) ? $n : min($positions);
477
478
		if ($pos - $old > 0)
479
		{
480
			$cdata .= Util::substr($data, $old, $pos - $old);
481
		}
482
483
		if ($pos >= $n)
484
		{
485
			break;
486
		}
487
488
		if (Util::substr($data, $pos, 1) === '<')
489
		{
490
			$pos2 = Util::strpos($data, '>', $pos);
491
			if ($pos2 === false)
492
			{
493
				$pos2 = $n;
494
			}
495
496
			if (Util::substr($data, $pos + 1, 1) === '/')
497
			{
498
				$cdata .= ']]></' . $ns . ':' . Util::substr($data, $pos + 2, $pos2 - $pos - 1) . '<![CDATA[';
499
			}
500
			else
501
			{
502
				$cdata .= ']]><' . $ns . ':' . Util::substr($data, $pos + 1, $pos2 - $pos) . '<![CDATA[';
503
			}
504
505
			$pos = $pos2 + 1;
506
		}
507
		elseif (Util::substr($data, $pos, 3) == ']]>')
508
		{
509
			$cdata .= ']]]]><![CDATA[>';
510
			$pos = $pos + 3;
511
		}
512
		elseif (Util::substr($data, $pos, 1) === '&')
513
		{
514
			$pos2 = Util::strpos($data, ';', $pos);
515
516
			if ($pos2 === false)
517
			{
518
				$pos2 = $n;
519
			}
520
521
			$ent = Util::substr($data, $pos + 1, $pos2 - $pos - 1);
522
523
			if (Util::substr($data, $pos + 1, 1) === '#')
524
			{
525
				$cdata .= ']]>' . Util::substr($data, $pos, $pos2 - $pos + 1) . '<![CDATA[';
526
			}
527
			elseif (in_array($ent, array('amp', 'lt', 'gt', 'quot')))
528
			{
529
				$cdata .= ']]>' . Util::substr($data, $pos, $pos2 - $pos + 1) . '<![CDATA[';
530
			}
531
532
			$pos = $pos2 + 1;
533
		}
534
	}
535
536
	$cdata .= ']]>';
537
538
	return strtr($cdata, array('<![CDATA[]]>' => ''));
539
}
540