Completed
Pull Request — master (#3325)
by Emanuele
11:19
created

approvePosts()   F

Complexity

Conditions 36
Paths > 20000

Size

Total Lines 234
Code Lines 121

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 1332

Importance

Changes 0
Metric Value
cc 36
eloc 121
dl 0
loc 234
rs 0
c 0
b 0
f 0
nc 1113156
nop 2
ccs 0
cts 139
cp 0
crap 1332

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 those functions pertaining to posting, and other such
5
 * operations, including sending emails, ims, blocking spam, preparsing posts,
6
 * spell checking, and the post box.
7
 *
8
 * @name      ElkArte Forum
9
 * @copyright ElkArte Forum contributors
10
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause
11
 *
12
 * This file contains code covered by:
13
 * copyright:	2011 Simple Machines (http://www.simplemachines.org)
14
 * license:  	BSD, See included LICENSE.TXT for terms and conditions.
15
 *
16
 * @version 1.1.6
17
 *
18
 */
19
20
/**
21
 * Takes a message and parses it, returning the prepared message as a reference.
22
 *
23
 * - Cleans up links (javascript, etc.) and code/quote sections.
24
 * - Won't convert \n's and a few other things if previewing is true.
25
 *
26
 * @package Posts
27
 * @param string $message
28
 * @param boolean $previewing
29
 */
30
function preparsecode(&$message, $previewing = false)
31
{
32 2
	$preparse = \BBC\PreparseCode::instance();
33 2
	$preparse->preparsecode($message, $previewing);
34 2
}
35
36
/**
37
 * This is very simple, and just removes things done by preparsecode.
38
 *
39
 * @package Posts
40
 * @param string $message
41
 */
42
function un_preparsecode($message)
43
{
44
	$un_preparse = \BBC\PreparseCode::instance();
45
	return $un_preparse->un_preparsecode($message);
46
}
47
48
/**
49
 * Create a post, either as new topic (id_topic = 0) or in an existing one.
50
 *
51
 * The input parameters of this function assume:
52
 * - Strings have been escaped.
53
 * - Integers have been cast to integer.
54
 * - Mandatory parameters are set.
55
 *
56
 * @package Posts
57
 * @param mixed[] $msgOptions
58
 * @param mixed[] $topicOptions
59
 * @param mixed[] $posterOptions
60
 * @throws Elk_Exception
61
 */
62
function createPost(&$msgOptions, &$topicOptions, &$posterOptions)
63
{
64 10
	global $user_info, $txt, $modSettings;
65
66 10
	$db = database();
67
68
	// Set optional parameters to the default value.
69 10
	$msgOptions['icon'] = empty($msgOptions['icon']) ? 'xx' : $msgOptions['icon'];
70 10
	$msgOptions['smileys_enabled'] = !empty($msgOptions['smileys_enabled']);
71
	// @todo 2015/03/02 - The following line should probably be moved to a module
72 10
	$msgOptions['attachments'] = empty($msgOptions['attachments']) ? array() : $msgOptions['attachments'];
73 10
	$msgOptions['approved'] = isset($msgOptions['approved']) ? (int) $msgOptions['approved'] : 1;
74 10
	$topicOptions['id'] = empty($topicOptions['id']) ? 0 : (int) $topicOptions['id'];
75 10
	$topicOptions['poll'] = isset($topicOptions['poll']) ? (int) $topicOptions['poll'] : null;
76 10
	$topicOptions['lock_mode'] = isset($topicOptions['lock_mode']) ? $topicOptions['lock_mode'] : null;
77 10
	$topicOptions['sticky_mode'] = isset($topicOptions['sticky_mode']) ? $topicOptions['sticky_mode'] : null;
78 10
	$topicOptions['redirect_expires'] = isset($topicOptions['redirect_expires']) ? $topicOptions['redirect_expires'] : null;
79 10
	$topicOptions['redirect_topic'] = isset($topicOptions['redirect_topic']) ? $topicOptions['redirect_topic'] : null;
80 10
	$posterOptions['id'] = empty($posterOptions['id']) ? 0 : (int) $posterOptions['id'];
81 10
	$posterOptions['ip'] = empty($posterOptions['ip']) ? $user_info['ip'] : $posterOptions['ip'];
82
83
	// We need to know if the topic is approved. If we're told that's great - if not find out.
84 10
	if (!$modSettings['postmod_active'])
85 10
		$topicOptions['is_approved'] = true;
86
	elseif (!empty($topicOptions['id']) && !isset($topicOptions['is_approved']))
87
	{
88
		require_once(SUBSDIR . '/Topic.subs.php');
89
		$is_approved = topicAttribute($topicOptions['id'], array('approved'));
90
		$topicOptions['is_approved'] = $is_approved['approved'];
91
	}
92
93
	// If nothing was filled in as name/email address, try the member table.
94 10
	if (!isset($posterOptions['name']) || $posterOptions['name'] == '' || (empty($posterOptions['email']) && !empty($posterOptions['id'])))
95 10
	{
96
		if (empty($posterOptions['id']))
97
		{
98
			$posterOptions['id'] = 0;
99
			$posterOptions['name'] = $txt['guest_title'];
100
			$posterOptions['email'] = '';
101
		}
102
		elseif ($posterOptions['id'] != $user_info['id'])
103
		{
104
			require_once(SUBSDIR . '/Members.subs.php');
105
			$result = getBasicMemberData($posterOptions['id']);
106
			// Couldn't find the current poster?
107
			if (empty($result))
108
			{
109
				trigger_error('createPost(): Invalid member id ' . $posterOptions['id'], E_USER_NOTICE);
110
				$posterOptions['id'] = 0;
111
				$posterOptions['name'] = $txt['guest_title'];
112
				$posterOptions['email'] = '';
113
			}
114
			else
115
			{
116
				$posterOptions['name'] = $result['member_name'];
117
				$posterOptions['email'] = $result['email_address'];
118
			}
119
		}
120
		else
121
		{
122
			$posterOptions['name'] = $user_info['name'];
123
			$posterOptions['email'] = $user_info['email'];
124
		}
125
	}
126
127
	// It's do or die time: forget any user aborts!
128 10
	$previous_ignore_user_abort = ignore_user_abort(true);
129
130 10
	$new_topic = empty($topicOptions['id']);
131
132
	$message_columns = array(
133 10
		'id_board' => 'int',
134 10
		'id_topic' => 'int',
135 10
		'id_member' => 'int',
136 10
		'subject' => 'string-255',
137 10
		'body' => (!empty($modSettings['max_messageLength']) && $modSettings['max_messageLength'] > 65534 ? 'string-' . $modSettings['max_messageLength'] : (empty($modSettings['max_messageLength']) ? 'string' : 'string-65534')),
138 10
		'poster_name' => 'string-255',
139 10
		'poster_email' => 'string-255',
140 10
		'poster_time' => 'int',
141 10
		'poster_ip' => 'string-255',
142 10
		'smileys_enabled' => 'int',
143 10
		'modified_name' => 'string',
144 10
		'icon' => 'string-16',
145 10
		'approved' => 'int',
146 10
	);
147
148
	$message_parameters = array(
149 10
		'id_board' => $topicOptions['board'],
150 10
		'id_topic' => $topicOptions['id'],
151 10
		'id_member' => $posterOptions['id'],
152 10
		'subject' => $msgOptions['subject'],
153 10
		'body' => $msgOptions['body'],
154 10
		'poster_name' => $posterOptions['name'],
155 10
		'poster_email' => $posterOptions['email'],
156 10
		'poster_time' => empty($posterOptions['time']) ? time() : $posterOptions['time'],
157 10
		'poster_ip' => $posterOptions['ip'],
158 10
		'smileys_enabled' => $msgOptions['smileys_enabled'] ? 1 : 0,
159 10
		'modified_name' => '',
160 10
		'icon' => $msgOptions['icon'],
161 10
		'approved' => $msgOptions['approved'],
162 10
	);
163
164
	// What if we want to do anything with posts?
165 10
	call_integration_hook('integrate_before_create_post', array(&$msgOptions, &$topicOptions, &$posterOptions, &$message_columns, &$message_parameters));
166
167
	// Insert the post.
168 10
	$db->insert('',
169 10
		'{db_prefix}messages',
170 10
		$message_columns,
171 10
		$message_parameters,
172 10
		array('id_msg')
173 10
	);
174 10
	$msgOptions['id'] = $db->insert_id('{db_prefix}messages', 'id_msg');
175
176
	// Something went wrong creating the message...
177 10
	if (empty($msgOptions['id']))
178 10
		return false;
179
180
	// What if we want to export new posts out to a CMS?
181 10
	call_integration_hook('integrate_create_post', array($msgOptions, $topicOptions, $posterOptions, $message_columns, $message_parameters));
182
183
	// Insert a new topic (if the topicID was left empty.)
184
	if ($new_topic)
185 10
	{
186
		$topic_columns = array(
187 10
			'id_board' => 'int', 'id_member_started' => 'int',
188 10
			'id_member_updated' => 'int', 'id_first_msg' => 'int',
189 10
			'id_last_msg' => 'int', 'locked' => 'int',
190 10
			'is_sticky' => 'int', 'num_views' => 'int',
191 10
			'id_poll' => 'int',
192 10
			'unapproved_posts' => 'int', 'approved' => 'int',
193 10
			'redirect_expires' => 'int',
194 10
			'id_redirect_topic' => 'int',
195 10
		);
196
		$topic_parameters = array(
197 10
			'id_board' => $topicOptions['board'],
198 10
			'id_member_started' => $posterOptions['id'],
199 10
			'id_member_updated' => $posterOptions['id'],
200 10
			'id_first_msg' => $msgOptions['id'],
201 10
			'id_last_msg' => $msgOptions['id'],
202 10
			'locked' => $topicOptions['lock_mode'] === null ? 0 : $topicOptions['lock_mode'],
203 10
			'is_sticky' => $topicOptions['sticky_mode'] === null ? 0 : $topicOptions['sticky_mode'],
204 10
			'num_views' => 0,
205 10
			'id_poll' => $topicOptions['poll'] === null ? 0 : $topicOptions['poll'],
206 10
			'unapproved_posts' =>  $msgOptions['approved'] ? 0 : 1,
207 10
			'approved' => $msgOptions['approved'],
208 10
			'redirect_expires' => $topicOptions['redirect_expires'] === null ? 0 : $topicOptions['redirect_expires'],
209 10
			'id_redirect_topic' => $topicOptions['redirect_topic'] === null ? 0 : $topicOptions['redirect_topic'],
210 10
		);
211
212 10
		call_integration_hook('integrate_before_create_topic', array(&$msgOptions, &$topicOptions, &$posterOptions, &$topic_columns, &$topic_parameters));
213
214 10
		$db->insert('',
215 10
			'{db_prefix}topics',
216 10
			$topic_columns,
217 10
			$topic_parameters,
218 10
			array('id_topic')
219 10
		);
220 10
		$topicOptions['id'] = $db->insert_id('{db_prefix}topics', 'id_topic');
221
222
		// The topic couldn't be created for some reason.
223 10
		if (empty($topicOptions['id']))
224 10
		{
225
			// We should delete the post that did work, though...
226
			$db->query('', '
227
				DELETE FROM {db_prefix}messages
228
				WHERE id_msg = {int:id_msg}',
229
				array(
230
					'id_msg' => $msgOptions['id'],
231
				)
232
			);
233
234
			return false;
235
		}
236
237
		// Fix the message with the topic.
238 10
		$db->query('', '
239
			UPDATE {db_prefix}messages
240
			SET id_topic = {int:id_topic}
241 10
			WHERE id_msg = {int:id_msg}',
242
			array(
243 10
				'id_topic' => $topicOptions['id'],
244 10
				'id_msg' => $msgOptions['id'],
245
			)
246 10
		);
247
248
		// There's been a new topic AND a new post today.
249 10
		trackStats(array('topics' => '+', 'posts' => '+'));
250
251 10
		require_once(SUBSDIR . '/Topic.subs.php');
252 10
		updateTopicStats(true);
253 10
		require_once(SUBSDIR . '/Messages.subs.php');
254 10
		updateSubjectStats($topicOptions['id'], $msgOptions['subject']);
255
256
		// What if we want to export new topics out to a CMS?
257 10
		call_integration_hook('integrate_create_topic', array($msgOptions, $topicOptions, $posterOptions));
258 10
	}
259
	// The topic already exists, it only needs a little updating.
260
	else
261
	{
262
		$update_parameters = array(
263
			'poster_id' => $posterOptions['id'],
264
			'id_msg' => $msgOptions['id'],
265
			'locked' => $topicOptions['lock_mode'],
266
			'is_sticky' => $topicOptions['sticky_mode'],
267
			'id_topic' => $topicOptions['id'],
268
			'counter_increment' => 1,
269
		);
270
271
		if ($msgOptions['approved'])
272
			$topics_columns = array(
273
				'id_member_updated = {int:poster_id}',
274
				'id_last_msg = {int:id_msg}',
275
				'num_replies = num_replies + {int:counter_increment}',
276
			);
277
		else
278
			$topics_columns = array(
279
				'unapproved_posts = unapproved_posts + {int:counter_increment}',
280
			);
281
282
		if ($topicOptions['lock_mode'] !== null)
283
			$topics_columns[] = 'locked = {int:locked}';
284
285
		if ($topicOptions['sticky_mode'] !== null)
286
			$topics_columns[] = 'is_sticky = {int:is_sticky}';
287
288
		call_integration_hook('integrate_before_modify_topic', array(&$topics_columns, &$update_parameters, &$msgOptions, &$topicOptions, &$posterOptions));
289
290
		// Update the number of replies and the lock/sticky status.
291
		$db->query('', '
292
			UPDATE {db_prefix}topics
293
			SET
294
				' . implode(', ', $topics_columns) . '
295
			WHERE id_topic = {int:id_topic}',
296
			$update_parameters
297
		);
298
299
		// One new post has been added today.
300
		trackStats(array('posts' => '+'));
301
	}
302
303
	// Creating is modifying...in a way.
304
	// @todo id_msg_modified needs to be set when you create a post, now this query is
305
	// the only place it does get set for post creation.  Why not set it on the insert?
306 10
	$db->query('', '
307
		UPDATE {db_prefix}messages
308
		SET id_msg_modified = {int:id_msg}
309 10
		WHERE id_msg = {int:id_msg}',
310
		array(
311 10
			'id_msg' => $msgOptions['id'],
312
		)
313 10
	);
314
315
	// Increase the number of posts and topics on the board.
316 10
	if ($msgOptions['approved'])
317 10
		$db->query('', '
318
			UPDATE {db_prefix}boards
319 10
			SET num_posts = num_posts + 1' . ($new_topic ? ', num_topics = num_topics + 1' : '') . '
320 10
			WHERE id_board = {int:id_board}',
321
			array(
322 10
				'id_board' => $topicOptions['board'],
323
			)
324 10
		);
325
	else
326
	{
327
		$db->query('', '
328
			UPDATE {db_prefix}boards
329
			SET unapproved_posts = unapproved_posts + 1' . ($new_topic ? ', unapproved_topics = unapproved_topics + 1' : '') . '
330
			WHERE id_board = {int:id_board}',
331
			array(
332
				'id_board' => $topicOptions['board'],
333
			)
334
		);
335
336
		// Add to the approval queue too.
337
		$db->insert('',
338
			'{db_prefix}approval_queue',
339
			array(
340
				'id_msg' => 'int',
341
			),
342
			array(
343
				$msgOptions['id'],
344
			),
345
			array()
346
		);
347
	}
348
349
	// Mark inserted topic as read (only for the user calling this function).
350 10
	if (!empty($topicOptions['mark_as_read']) && !$user_info['is_guest'])
351 10
	{
352
		// Since it's likely they *read* it before replying, let's try an UPDATE first.
353
		if (!$new_topic)
354
		{
355
			$db->query('', '
356
				UPDATE {db_prefix}log_topics
357
				SET id_msg = {int:id_msg}
358
				WHERE id_member = {int:current_member}
359
					AND id_topic = {int:id_topic}',
360
				array(
361
					'current_member' => $posterOptions['id'],
362
					'id_msg' => $msgOptions['id'],
363
					'id_topic' => $topicOptions['id'],
364
				)
365
			);
366
367
			$flag = $db->affected_rows() != 0;
368
		}
369
370
		if (empty($flag))
371
		{
372
			require_once(SUBSDIR . '/Topic.subs.php');
373
			markTopicsRead(array($posterOptions['id'], $topicOptions['id'], $msgOptions['id'], 0), false);
374
		}
375
	}
376
377
	// If there's a custom search index, it may need updating...
378 10
	$search = new \ElkArte\Search\Search;
379 10
	$searchAPI = $search->findSearchAPI();
380 10
	if (is_callable(array($searchAPI, 'postCreated')))
381 10
		$searchAPI->postCreated($msgOptions, $topicOptions, $posterOptions);
0 ignored issues
show
Bug introduced by
The method postCreated() does not exist on ElkArte\Search\API\Standard. ( Ignorable by Annotation )

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

381
		$searchAPI->/** @scrutinizer ignore-call */ 
382
              postCreated($msgOptions, $topicOptions, $posterOptions);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
382
383
	// Increase the post counter for the user that created the post.
384 10
	if (!empty($posterOptions['update_post_count']) && !empty($posterOptions['id']) && $msgOptions['approved'])
385 10
	{
386
		// Are you the one that happened to create this post?
387
		if ($user_info['id'] == $posterOptions['id'])
388
			$user_info['posts']++;
389
390
		require_once(SUBSDIR . '/Members.subs.php');
391
		updateMemberData($posterOptions['id'], array('posts' => '+'));
392
	}
393
394
	// They've posted, so they can make the view count go up one if they really want. (this is to keep views >= replies...)
395 10
	$_SESSION['last_read_topic'] = 0;
396
397
	// Better safe than sorry.
398 10
	if (isset($_SESSION['topicseen_cache'][$topicOptions['board']]))
399 10
		$_SESSION['topicseen_cache'][$topicOptions['board']]--;
400
401
	// Update all the stats so everyone knows about this new topic and message.
402 10
	require_once(SUBSDIR . '/Messages.subs.php');
403 10
	updateMessageStats(true, $msgOptions['id']);
404
405
	// Update the last message on the board assuming it's approved AND the topic is.
406 10
	if ($msgOptions['approved'])
407 10
		updateLastMessages($topicOptions['board'], $new_topic || !empty($topicOptions['is_approved']) ? $msgOptions['id'] : 0);
408
409
	// Alright, done now... we can abort now, I guess... at least this much is done.
410 10
	ignore_user_abort($previous_ignore_user_abort);
0 ignored issues
show
Bug introduced by
$previous_ignore_user_abort of type integer is incompatible with the type boolean expected by parameter $value of ignore_user_abort(). ( Ignorable by Annotation )

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

410
	ignore_user_abort(/** @scrutinizer ignore-type */ $previous_ignore_user_abort);
Loading history...
411
412
	// Success.
413 10
	return true;
414
}
415
416
/**
417
 * Modifying a post...
418
 *
419
 * @package Posts
420
 * @param mixed[] $msgOptions
421
 * @param mixed[] $topicOptions
422
 * @param mixed[] $posterOptions
423
 * @throws Elk_Exception
424
 */
425
function modifyPost(&$msgOptions, &$topicOptions, &$posterOptions)
426
{
427
	global $user_info, $modSettings;
428
429
	$db = database();
430
431
	$topicOptions['poll'] = isset($topicOptions['poll']) ? (int) $topicOptions['poll'] : null;
432
	$topicOptions['lock_mode'] = isset($topicOptions['lock_mode']) ? $topicOptions['lock_mode'] : null;
433
	$topicOptions['sticky_mode'] = isset($topicOptions['sticky_mode']) ? $topicOptions['sticky_mode'] : null;
434
435
	// This is longer than it has to be, but makes it so we only set/change what we have to.
436
	$messages_columns = array();
437
	if (isset($posterOptions['name']))
438
		$messages_columns['poster_name'] = $posterOptions['name'];
439
	if (isset($posterOptions['email']))
440
		$messages_columns['poster_email'] = $posterOptions['email'];
441
	if (isset($msgOptions['icon']))
442
		$messages_columns['icon'] = $msgOptions['icon'];
443
	if (isset($msgOptions['subject']))
444
		$messages_columns['subject'] = $msgOptions['subject'];
445
	if (isset($msgOptions['body']))
446
	{
447
		$messages_columns['body'] = $msgOptions['body'];
448
449
		// using a custom search index, then lets get the old message so we can update our index as needed
450
		if (!empty($modSettings['search_custom_index_config']))
451
		{
452
			require_once(SUBSDIR . '/Messages.subs.php');
453
			$message = basicMessageInfo($msgOptions['id'], true);
454
			$msgOptions['old_body'] = $message['body'];
455
		}
456
	}
457
	if (!empty($msgOptions['modify_time']))
458
	{
459
		$messages_columns['modified_time'] = $msgOptions['modify_time'];
460
		$messages_columns['modified_name'] = $msgOptions['modify_name'];
461
		$messages_columns['id_msg_modified'] = $modSettings['maxMsgID'];
462
	}
463
	if (isset($msgOptions['smileys_enabled']))
464
		$messages_columns['smileys_enabled'] = empty($msgOptions['smileys_enabled']) ? 0 : 1;
465
466
	// Which columns need to be ints?
467
	$messageInts = array('modified_time', 'id_msg_modified', 'smileys_enabled');
468
	$update_parameters = array(
469
		'id_msg' => $msgOptions['id'],
470
	);
471
472
	call_integration_hook('integrate_before_modify_post', array(&$messages_columns, &$update_parameters, &$msgOptions, &$topicOptions, &$posterOptions, &$messageInts));
473
474
	foreach ($messages_columns as $var => $val)
475
	{
476
		$messages_columns[$var] = $var . ' = {' . (in_array($var, $messageInts) ? 'int' : 'string') . ':var_' . $var . '}';
477
		$update_parameters['var_' . $var] = $val;
478
	}
479
480
	// Nothing to do?
481
	if (empty($messages_columns))
482
		return true;
483
484
	// Change the post.
485
	$db->query('', '
486
		UPDATE {db_prefix}messages
487
		SET ' . implode(', ', $messages_columns) . '
488
		WHERE id_msg = {int:id_msg}',
489
		$update_parameters
490
	);
491
492
	$attributes = array();
493
	// Lock and or sticky the post.
494
	if ($topicOptions['sticky_mode'] !== null)
495
		$attributes['is_sticky'] = $topicOptions['sticky_mode'];
496
	if ($topicOptions['lock_mode'] !== null)
497
		$attributes['locked'] = $topicOptions['lock_mode'];
498
	if ($topicOptions['poll'] !== null)
499
		$attributes['id_poll'] = $topicOptions['poll'];
500
501
	// If anything to do, do it.
502
	if (!empty($attributes))
503
		setTopicAttribute($topicOptions['id'], $attributes);
504
505
	// Mark the edited post as read.
506
	if (!empty($topicOptions['mark_as_read']) && !$user_info['is_guest'])
507
	{
508
		// Since it's likely they *read* it before editing, let's try an UPDATE first.
509
		$db->query('', '
510
			UPDATE {db_prefix}log_topics
511
			SET id_msg = {int:id_msg}
512
			WHERE id_member = {int:current_member}
513
				AND id_topic = {int:id_topic}',
514
			array(
515
				'current_member' => $user_info['id'],
516
				'id_msg' => $modSettings['maxMsgID'],
517
				'id_topic' => $topicOptions['id'],
518
			)
519
		);
520
521
		$flag = $db->affected_rows() != 0;
522
523
		if (empty($flag))
524
		{
525
			require_once(SUBSDIR . '/Topic.subs.php');
526
			markTopicsRead(array($user_info['id'], $topicOptions['id'], $modSettings['maxMsgID'], 0), false);
527
		}
528
	}
529
530
	// If there's a custom search index, it needs to be modified...
531
	$search = new \ElkArte\Search\Search;
532
	$searchAPI = $search->findSearchAPI();
533
	if (is_callable(array($searchAPI, 'postModified')))
534
		$searchAPI->postModified($msgOptions, $topicOptions, $posterOptions);
0 ignored issues
show
Bug introduced by
The method postModified() does not exist on ElkArte\Search\API\Standard. ( Ignorable by Annotation )

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

534
		$searchAPI->/** @scrutinizer ignore-call */ 
535
              postModified($msgOptions, $topicOptions, $posterOptions);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
535
536
	if (isset($msgOptions['subject']))
537
	{
538
		// Only update the subject if this was the first message in the topic.
539
		$request = $db->query('', '
540
			SELECT id_topic
541
			FROM {db_prefix}topics
542
			WHERE id_first_msg = {int:id_first_msg}
543
			LIMIT 1',
544
			array(
545
				'id_first_msg' => $msgOptions['id'],
546
			)
547
		);
548
		if ($db->num_rows($request) == 1)
549
		{
550
			require_once(SUBSDIR . '/Messages.subs.php');
551
			updateSubjectStats($topicOptions['id'], $msgOptions['subject']);
552
		}
553
		$db->free_result($request);
554
	}
555
556
	// Finally, if we are setting the approved state we need to do much more work :(
557
	if ($modSettings['postmod_active'] && isset($msgOptions['approved']))
558
		approvePosts($msgOptions['id'], $msgOptions['approved']);
559
560
	return true;
561
}
562
563
/**
564
 * Approve (or not) some posts... without permission checks...
565
 *
566
 * @package Posts
567
 * @param int|int[] $msgs - array of message ids
568
 * @param bool $approve = true
569
 * @throws Elk_Exception
570
 */
571
function approvePosts($msgs, $approve = true)
572
{
573
	global $modSettings;
574
575
	$db = database();
576
577
	if (!is_array($msgs))
578
		$msgs = array($msgs);
579
580
	if (empty($msgs))
581
		return false;
582
583
	// May as well start at the beginning, working out *what* we need to change.
584
	$request = $db->query('', '
585
		SELECT m.id_msg, m.approved, m.id_topic, m.id_board, t.id_first_msg, t.id_last_msg,
586
			m.body, m.subject, COALESCE(mem.real_name, m.poster_name) AS poster_name, m.id_member,
587
			t.approved AS topic_approved, b.count_posts
588
		FROM {db_prefix}messages AS m
589
			INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
590
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
591
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
592
		WHERE m.id_msg IN ({array_int:message_list})
593
			AND m.approved = {int:approved_state}',
594
		array(
595
			'message_list' => $msgs,
596
			'approved_state' => $approve ? 0 : 1,
597
		)
598
	);
599
	$msgs = array();
600
	$topics = array();
601
	$topic_changes = array();
602
	$board_changes = array();
603
	$notification_topics = array();
604
	$notification_posts = array();
605
	$member_post_changes = array();
606
	while ($row = $db->fetch_assoc($request))
607
	{
608
		// Easy...
609
		$msgs[] = $row['id_msg'];
610
		$topics[] = $row['id_topic'];
611
612
		// Ensure our change array exists already.
613
		if (!isset($topic_changes[$row['id_topic']]))
614
			$topic_changes[$row['id_topic']] = array(
615
				'id_last_msg' => $row['id_last_msg'],
616
				'approved' => $row['topic_approved'],
617
				'replies' => 0,
618
				'unapproved_posts' => 0,
619
			);
620
		if (!isset($board_changes[$row['id_board']]))
621
			$board_changes[$row['id_board']] = array(
622
				'posts' => 0,
623
				'topics' => 0,
624
				'unapproved_posts' => 0,
625
				'unapproved_topics' => 0,
626
			);
627
628
		// If it's the first message then the topic state changes!
629
		if ($row['id_msg'] == $row['id_first_msg'])
630
		{
631
			$topic_changes[$row['id_topic']]['approved'] = $approve ? 1 : 0;
632
633
			$board_changes[$row['id_board']]['unapproved_topics'] += $approve ? -1 : 1;
634
			$board_changes[$row['id_board']]['topics'] += $approve ? 1 : -1;
635
636
			// Note we need to ensure we announce this topic!
637
			$notification_topics[] = array(
638
				'body' => $row['body'],
639
				'subject' => $row['subject'],
640
				'name' => $row['poster_name'],
641
				'board' => $row['id_board'],
642
				'topic' => $row['id_topic'],
643
				'msg' => $row['id_first_msg'],
644
				'poster' => $row['id_member'],
645
			);
646
		}
647
		else
648
		{
649
			$topic_changes[$row['id_topic']]['replies'] += $approve ? 1 : -1;
650
651
			// This will be a post... but don't notify unless it's not followed by approved ones.
652
			if ($row['id_msg'] > $row['id_last_msg'])
653
				$notification_posts[$row['id_topic']][] = array(
654
					'id' => $row['id_msg'],
655
					'body' => $row['body'],
656
					'subject' => $row['subject'],
657
					'name' => $row['poster_name'],
658
					'topic' => $row['id_topic'],
659
				);
660
		}
661
662
		// If this is being approved and id_msg is higher than the current id_last_msg then it changes.
663
		if ($approve && $row['id_msg'] > $topic_changes[$row['id_topic']]['id_last_msg'])
664
			$topic_changes[$row['id_topic']]['id_last_msg'] = $row['id_msg'];
665
		// If this is being unapproved, and it's equal to the id_last_msg we need to find a new one!
666
		elseif (!$approve)
667
			// Default to the first message and then we'll override in a bit ;)
668
			$topic_changes[$row['id_topic']]['id_last_msg'] = $row['id_first_msg'];
669
670
		$topic_changes[$row['id_topic']]['unapproved_posts'] += $approve ? -1 : 1;
671
		$board_changes[$row['id_board']]['unapproved_posts'] += $approve ? -1 : 1;
672
		$board_changes[$row['id_board']]['posts'] += $approve ? 1 : -1;
673
674
		// Post count for the user?
675
		if ($row['id_member'] && empty($row['count_posts']))
676
			$member_post_changes[$row['id_member']] = isset($member_post_changes[$row['id_member']]) ? $member_post_changes[$row['id_member']] + 1 : 1;
677
	}
678
	$db->free_result($request);
679
680
	if (empty($msgs))
681
		return;
682
683
	// Now we have the differences make the changes, first the easy one.
684
	$db->query('', '
685
		UPDATE {db_prefix}messages
686
		SET approved = {int:approved_state}
687
		WHERE id_msg IN ({array_int:message_list})',
688
		array(
689
			'message_list' => $msgs,
690
			'approved_state' => $approve ? 1 : 0,
691
		)
692
	);
693
694
	// If we were unapproving find the last msg in the topics...
695
	if (!$approve)
696
	{
697
		$request = $db->query('', '
698
			SELECT id_topic, MAX(id_msg) AS id_last_msg
699
			FROM {db_prefix}messages
700
			WHERE id_topic IN ({array_int:topic_list})
701
				AND approved = {int:approved}
702
			GROUP BY id_topic',
703
			array(
704
				'topic_list' => $topics,
705
				'approved' => 1,
706
			)
707
		);
708
		while ($row = $db->fetch_assoc($request))
709
			$topic_changes[$row['id_topic']]['id_last_msg'] = $row['id_last_msg'];
710
		$db->free_result($request);
711
	}
712
713
	// ... next the topics...
714
	foreach ($topic_changes as $id => $changes)
715
		$db->query('', '
716
			UPDATE {db_prefix}topics
717
			SET
718
				approved = {int:approved},
719
				unapproved_posts = CASE WHEN unapproved_posts + {int:unapproved_posts} < 0 THEN 0 ELSE unapproved_posts + {int:unapproved_posts} END,
720
				num_replies = CASE WHEN num_replies + {int:num_replies} < 0 THEN 0 ELSE num_replies + {int:num_replies} END,
721
				id_last_msg = {int:id_last_msg}
722
			WHERE id_topic = {int:id_topic}',
723
			array(
724
				'approved' => $changes['approved'],
725
				'unapproved_posts' => $changes['unapproved_posts'],
726
				'num_replies' => $changes['replies'],
727
				'id_last_msg' => $changes['id_last_msg'],
728
				'id_topic' => $id,
729
			)
730
		);
731
732
	// ... finally the boards...
733
	foreach ($board_changes as $id => $changes)
734
		$db->query('', '
735
			UPDATE {db_prefix}boards
736
			SET
737
				num_posts = num_posts + {int:num_posts},
738
				unapproved_posts = CASE WHEN unapproved_posts + {int:unapproved_posts} < 0 THEN 0 ELSE unapproved_posts + {int:unapproved_posts} END,
739
				num_topics = CASE WHEN num_topics + {int:num_topics} < 0 THEN 0 ELSE num_topics + {int:num_topics} END,
740
				unapproved_topics = CASE WHEN unapproved_topics + {int:unapproved_topics} < 0 THEN 0 ELSE unapproved_topics + {int:unapproved_topics} END
741
			WHERE id_board = {int:id_board}',
742
			array(
743
				'num_posts' => $changes['posts'],
744
				'unapproved_posts' => $changes['unapproved_posts'],
745
				'num_topics' => $changes['topics'],
746
				'unapproved_topics' => $changes['unapproved_topics'],
747
				'id_board' => $id,
748
			)
749
		);
750
751
	// Finally, least importantly, notifications!
752
	if ($approve)
753
	{
754
		require_once(SUBSDIR . '/Notification.subs.php');
755
756
		if (!empty($notification_topics))
757
			sendBoardNotifications($notification_topics);
758
759
		if (!empty($notification_posts))
760
			sendApprovalNotifications($notification_posts);
761
762
		$db->query('', '
763
			DELETE FROM {db_prefix}approval_queue
764
			WHERE id_msg IN ({array_int:message_list})
765
				AND id_attach = {int:id_attach}',
766
			array(
767
				'message_list' => $msgs,
768
				'id_attach' => 0,
769
			)
770
		);
771
	}
772
	// If unapproving add to the approval queue!
773
	else
774
	{
775
		$msgInserts = array();
776
		foreach ($msgs as $msg)
777
			$msgInserts[] = array($msg);
778
779
		$db->insert('ignore',
780
			'{db_prefix}approval_queue',
781
			array('id_msg' => 'int'),
782
			$msgInserts,
783
			array('id_msg')
784
		);
785
	}
786
787
	if (!empty($modSettings['mentions_enabled']))
788
	{
789
		require_once(SUBSDIR . '/Mentions.subs.php');
790
		toggleMentionsApproval($msgs, $approve);
791
	}
792
793
	// Update the last messages on the boards...
794
	updateLastMessages(array_keys($board_changes));
795
796
	// Post count for the members?
797
	if (!empty($member_post_changes))
798
	{
799
		require_once(SUBSDIR . '/Members.subs.php');
800
		foreach ($member_post_changes as $id_member => $count_change)
801
			updateMemberData($id_member, array('posts' => 'posts ' . ($approve ? '+' : '-') . ' ' . $count_change));
802
	}
803
804
	return true;
805
}
806
807
/**
808
 * Takes an array of board IDs and updates their last messages.
809
 *
810
 * - If the board has a parent, that parent board is also automatically updated.
811
 * - The columns updated are id_last_msg and last_updated.
812
 * - Note that id_last_msg should always be updated using this function,
813
 * and is not automatically updated upon other changes.
814
 *
815
 * @package Posts
816
 * @param int[]|int $setboards
817
 * @param int $id_msg = 0
818
 * @throws Elk_Exception
819
 */
820
function updateLastMessages($setboards, $id_msg = 0)
821
{
822 10
	global $board_info, $board;
823
824 10
	$db = database();
825
826
	// Please - let's be sane.
827 10
	if (empty($setboards))
828 10
		return false;
829
830 10
	if (!is_array($setboards))
831 10
		$setboards = array($setboards);
832
833 10
	$lastMsg = array();
834
835
	// If we don't know the id_msg we need to find it.
836 10
	if (!$id_msg)
837 10
	{
838
		// Find the latest message on this board (highest id_msg.)
839 6
		$request = $db->query('', '
840
			SELECT id_board, MAX(id_last_msg) AS id_msg
841
			FROM {db_prefix}topics
842
			WHERE id_board IN ({array_int:board_list})
843
				AND approved = {int:approved}
844 6
			GROUP BY id_board',
845
			array(
846 6
				'board_list' => $setboards,
847 6
				'approved' => 1,
848
			)
849 6
		);
850 6
		while ($row = $db->fetch_assoc($request))
851 6
			$lastMsg[$row['id_board']] = $row['id_msg'];
852 6
		$db->free_result($request);
853 6
	}
854
	else
855
	{
856
		// Just to note - there should only be one board passed if we are doing this.
857 10
		foreach ($setboards as $id_board)
858 10
			$lastMsg[$id_board] = $id_msg;
859
	}
860
861 10
	$parent_boards = array();
862
863
	// Keep track of last modified dates.
864 10
	$lastModified = $lastMsg;
865
866
	// Get all the sub-boards for the parents, if they have some...
867 10
	foreach ($setboards as $id_board)
868
	{
869 10
		if (!isset($lastMsg[$id_board]))
870 10
		{
871
			$lastMsg[$id_board] = 0;
872
			$lastModified[$id_board] = 0;
873
		}
874
875 10
		if (!empty($board) && $id_board == $board)
876 10
			$parents = $board_info['parent_boards'];
877
		else
878 10
			$parents = getBoardParents($id_board);
879
880
		// Ignore any parents on the top child level.
881 10
		foreach ($parents as $id => $parent)
882
		{
883 10
			if ($parent['level'] != 0)
884 10
			{
885
				// If we're already doing this one as a board, is this a higher last modified?
886
				if (isset($lastModified[$id]) && $lastModified[$id_board] > $lastModified[$id])
887
					$lastModified[$id] = $lastModified[$id_board];
888
				elseif (!isset($lastModified[$id]) && (!isset($parent_boards[$id]) || $parent_boards[$id] < $lastModified[$id_board]))
889
					$parent_boards[$id] = $lastModified[$id_board];
890
			}
891 10
		}
892 10
	}
893
894
	// Note to help understand what is happening here. For parents we update the timestamp of the last message for determining
895
	// whether there are sub-boards which have not been read. For the boards themselves we update both this and id_last_msg.
896 10
	$board_updates = array();
897 10
	$parent_updates = array();
898
899
	// Finally, to save on queries make the changes...
900 10
	foreach ($parent_boards as $id => $msg)
901
	{
902
		if (!isset($parent_updates[$msg]))
903
			$parent_updates[$msg] = array($id);
904
		else
905
			$parent_updates[$msg][] = $id;
906 10
	}
907
908 10
	foreach ($lastMsg as $id => $msg)
909
	{
910 10
		if (!isset($board_updates[$msg . '-' . $lastModified[$id]]))
911 10
			$board_updates[$msg . '-' . $lastModified[$id]] = array(
912 10
				'id' => $msg,
913 10
				'updated' => $lastModified[$id],
914 10
				'boards' => array($id)
915 10
			);
916
917
		else
918
			$board_updates[$msg . '-' . $lastModified[$id]]['boards'][] = $id;
919 10
	}
920
921
	// Now commit the changes!
922 10
	foreach ($parent_updates as $id_msg => $boards)
923
	{
924
		$db->query('', '
925
			UPDATE {db_prefix}boards
926
			SET id_msg_updated = {int:id_msg_updated}
927
			WHERE id_board IN ({array_int:board_list})
928
				AND id_msg_updated < {int:id_msg_updated}',
929
			array(
930
				'board_list' => $boards,
931
				'id_msg_updated' => $id_msg,
932
			)
933
		);
934 10
	}
935 10
	foreach ($board_updates as $board_data)
936
	{
937 10
		$db->query('', '
938
			UPDATE {db_prefix}boards
939
			SET id_last_msg = {int:id_last_msg}, id_msg_updated = {int:id_msg_updated}
940 10
			WHERE id_board IN ({array_int:board_list})',
941
			array(
942 10
				'board_list' => $board_data['boards'],
943 10
				'id_last_msg' => $board_data['id'],
944 10
				'id_msg_updated' => $board_data['updated'],
945
			)
946 10
		);
947 10
	}
948 10
}
949
950
/**
951
 * Get the latest post made on the system
952
 *
953
 * - respects approved, recycled, and board permissions
954
 *
955
 * @package Posts
956
 * @return array
957
 */
958
function lastPost()
959
{
960
	global $scripturl, $modSettings;
961
962
	$db = database();
963
964
	// Find it by the board - better to order by board than sort the entire messages table.
965
	$request = $db->query('substring', '
966
		SELECT ml.poster_time, ml.subject, ml.id_topic, ml.poster_name, SUBSTRING(ml.body, 1, 385) AS body,
967
			ml.smileys_enabled
968
		FROM {db_prefix}boards AS b
969
			INNER JOIN {db_prefix}messages AS ml ON (ml.id_msg = b.id_last_msg)
970
		WHERE {query_wanna_see_board}' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? '
971
			AND b.id_board != {int:recycle_board}' : '') . '
972
			AND ml.approved = {int:is_approved}
973
		ORDER BY b.id_msg_updated DESC
974
		LIMIT 1',
975
		array(
976
			'recycle_board' => $modSettings['recycle_board'],
977
			'is_approved' => 1,
978
		)
979
	);
980
	if ($db->num_rows($request) == 0)
981
		return array();
982
	$row = $db->fetch_assoc($request);
983
	$db->free_result($request);
984
985
	// Censor the subject and post...
986
	$row['subject'] = censor($row['subject']);
987
	$row['body'] = censor($row['body']);
988
989
	$bbc_parser = \BBC\ParserWrapper::instance();
990
991
	$row['body'] = strip_tags(strtr($bbc_parser->parseMessage($row['body'], $row['smileys_enabled']), array('<br />' => '&#10;')));
992
	$row['body'] = Util::shorten_text($row['body'], !empty($modSettings['lastpost_preview_characters']) ? $modSettings['lastpost_preview_characters'] : 128, true);
993
994
	// Send the data.
995
	return array(
996
		'topic' => $row['id_topic'],
997
		'subject' => $row['subject'],
998
		'short_subject' => Util::shorten_text($row['subject'], $modSettings['subject_length']),
999
		'preview' => $row['body'],
1000
		'time' => standardTime($row['poster_time']),
1001
		'html_time' => htmlTime($row['poster_time']),
1002
		'timestamp' => forum_time(true, $row['poster_time']),
1003
		'href' => $scripturl . '?topic=' . $row['id_topic'] . '.new;topicseen#new',
1004
		'link' => '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.new;topicseen#new">' . $row['subject'] . '</a>'
1005
	);
1006
}
1007
1008
/**
1009
 * Prepares a post subject for the post form
1010
 *
1011
 * What it does:
1012
 *
1013
 * - Will add the appropriate Re: to the post subject if its a reply to an existing post
1014
 * - If quoting a post, or editing a post, this function also prepares the message body
1015
 * - if editing is true, returns $message|$message[errors], else returns array($subject, $message)
1016
 *
1017
 * @package Posts
1018
 *
1019
 * @param int|bool       $editing
1020
 * @param int|null|false $topic
1021
 * @param string         $first_subject
1022
 * @param int            $msg_id
1023
 *
1024
 * @return false|mixed[]
1025
 * @throws Elk_Exception
1026
 */
1027
function getFormMsgSubject($editing, $topic, $first_subject = '', $msg_id = 0)
1028
{
1029
	global $modSettings;
1030
1031
	$db = database();
1032
1033
	$form_subject = '';
1034
	$form_message = '';
1035
	switch ($editing)
1036
	{
1037
		case 1:
1038
			require_once(SUBSDIR . '/Messages.subs.php');
1039
1040
			// Get the existing message.
1041
			$message = messageDetails((int) $msg_id, (int) $topic);
1042
1043
			// The message they were trying to edit was most likely deleted.
1044
			if ($message === false)
1045
				return false;
1046
1047
			$errors = checkMessagePermissions($message['message']);
1048
1049
			prepareMessageContext($message);
1050
1051
			if (!empty($errors))
1052
				$message['errors'] = $errors;
1053
1054
			return $message;
1055
		// Posting a quoted reply?
1056
		case 2:
1057
			$msg_id = !empty($_REQUEST['quote']) ? (int) $_REQUEST['quote'] : (int) $_REQUEST['followup'];
1058
1059
			// Make sure they _can_ quote this post, and if so get it.
1060
			$request = $db->query('', '
1061
				SELECT
1062
					m.subject, COALESCE(mem.real_name, m.poster_name) AS poster_name, m.poster_time, m.body
1063
				FROM {db_prefix}messages AS m
1064
					INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board})
1065
					LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
1066
				WHERE m.id_msg = {int:id_msg}' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
1067
					AND m.approved = {int:is_approved}') . '
1068
				LIMIT 1',
1069
				array(
1070
					'id_msg' => $msg_id,
1071
					'is_approved' => 1,
1072
				)
1073
			);
1074
			if ($db->num_rows($request) == 0)
1075
				throw new Elk_Exception('quoted_post_deleted', false);
1076
			list ($form_subject, $mname, $mdate, $form_message) = $db->fetch_row($request);
1077
			$db->free_result($request);
1078
1079
			// Add 'Re: ' to the front of the quoted subject.
1080
			$response_prefix = response_prefix();
1081
			if (trim($response_prefix) != '' && Util::strpos($form_subject, trim($response_prefix)) !== 0)
1082
				$form_subject = $response_prefix . $form_subject;
1083
1084
			// Censor the message and subject.
1085
			$form_message = censor($form_message);
1086
			$form_subject = censor($form_subject);
1087
1088
			$form_message = un_preparsecode($form_message);
1089
			$form_message = removeNestedQuotes($form_message);
1090
1091
			// Add a quote string on the front and end.
1092
			$form_message = '[quote author=' . $mname . ' link=msg=' . (int) $msg_id . ' date=' . $mdate . ']' . "\n" . rtrim($form_message) . "\n" . '[/quote]';
1093
1094
			break;
1095
		// Posting a reply without a quote?
1096
		case 3:
1097
			// Get the first message's subject.
1098
			$form_subject = $first_subject;
1099
1100
			// Add 'Re: ' to the front of the subject.
1101
			$response_prefix = response_prefix();
1102
			if (trim($response_prefix) != '' && $form_subject != '' && Util::strpos($form_subject, trim($response_prefix)) !== 0)
1103
				$form_subject = $response_prefix . $form_subject;
1104
1105
			// Censor the subject.
1106
			$form_subject = censor($form_subject);
1107
1108
			$form_message = '';
1109
1110
			break;
1111
		case 4:
1112
			$form_subject = isset($_GET['subject']) ? $_GET['subject'] : '';
1113
			$form_message = '';
1114
1115
			break;
1116
	}
1117
1118
	return array($form_subject, $form_message);
1119
}
1120
1121
/**
1122
 * Update topic subject.
1123
 *
1124
 * - If $all is true, for all messages in the topic, otherwise only the first message.
1125
 *
1126
 * @package Posts
1127
 * @param mixed[] $topic_info topic information as returned by getTopicInfo()
1128
 * @param string $custom_subject
1129
 * @param string $response_prefix = ''
1130
 * @param bool $all = false
1131
 */
1132
function topicSubject($topic_info, $custom_subject, $response_prefix = '', $all = false)
1133
{
1134
	$db = database();
1135
1136
	if ($all)
1137
	{
1138
		$db->query('', '
1139
			UPDATE {db_prefix}messages
1140
			SET subject = {string:subject}
1141
			WHERE id_topic = {int:current_topic}',
1142
			array(
1143
				'current_topic' => $topic_info['id_topic'],
1144
				'subject' => $response_prefix . $custom_subject,
1145
			)
1146
		);
1147
	}
1148
	$db->query('', '
1149
		UPDATE {db_prefix}messages
1150
		SET subject = {string:custom_subject}
1151
		WHERE id_msg = {int:id_first_msg}',
1152
		array(
1153
			'id_first_msg' => $topic_info['id_first_msg'],
1154
			'custom_subject' => $custom_subject,
1155
		)
1156
	);
1157
}
1158