modifyPost()   F
last analyzed

Complexity

Conditions 26
Paths > 20000

Size

Total Lines 163
Code Lines 68

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 54
CRAP Score 29.2745

Importance

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

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

426
	updateMessageStats(true, /** @scrutinizer ignore-type */ $msgOptions['id']);
Loading history...
427
428 26
	// Update the last message on the board assuming it's approved AND the topic is.
429 26
	if ($msgOptions['approved'])
430
	{
431
		updateLastMessages($topicOptions['board'], $new_topic || !empty($topicOptions['is_approved']) ? $msgOptions['id'] : 0);
0 ignored issues
show
Bug introduced by
It seems like $new_topic || ! empty($t...? $msgOptions['id'] : 0 can also be of type boolean; however, parameter $id_msg of updateLastMessages() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

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

431
		updateLastMessages($topicOptions['board'], /** @scrutinizer ignore-type */ $new_topic || !empty($topicOptions['is_approved']) ? $msgOptions['id'] : 0);
Loading history...
432 26
	}
433
434 26
	// Alright, done now... we can abort now, I guess... at least this much is done.
435
	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|null expected by parameter $enable 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

435
	ignore_user_abort(/** @scrutinizer ignore-type */ $previous_ignore_user_abort);
Loading history...
436
437
	// Success.
438 26
	return true;
439
}
440
441 26
/**
442
 * Modifying a post...
443
 *
444
 * @param array $msgOptions
445
 * @param array $topicOptions
446
 * @param array $posterOptions
447
 *
448
 * @return bool
449
 * @package Posts
450
 *
451
 */
452
function modifyPost(&$msgOptions, &$topicOptions, &$posterOptions)
453
{
454
	global $modSettings;
455
456
	$db = database();
457
458 2
	$topicOptions['poll'] = isset($topicOptions['poll']) ? (int) $topicOptions['poll'] : null;
459
	$topicOptions['lock_mode'] = $topicOptions['lock_mode'] ?? null;
460 2
	$topicOptions['sticky_mode'] = $topicOptions['sticky_mode'] ?? null;
461
462 2
	// This is longer than it has to be, but makes it so we only set/change what we have to.
463 2
	$messages_columns = [];
464 2
	if (isset($posterOptions['name']))
465
	{
466
		$messages_columns['poster_name'] = $posterOptions['name'];
467 2
	}
468 2
469
	if (isset($posterOptions['email']))
470 2
	{
471
		$messages_columns['poster_email'] = $posterOptions['email'];
472
	}
473 2
474
	if (isset($msgOptions['icon']))
475 2
	{
476
		$messages_columns['icon'] = $msgOptions['icon'];
477
	}
478 2
479
	if (isset($msgOptions['subject']))
480 2
	{
481
		$messages_columns['subject'] = $msgOptions['subject'];
482
	}
483 2
484
	if (isset($msgOptions['body']))
485 2
	{
486
		$messages_columns['body'] = $msgOptions['body'];
487
488 2
		// using a custom search index, then let's get the old message so we can update our index as needed
489
		if (!empty($modSettings['search_custom_index_config']))
490 2
		{
491
			require_once(SUBSDIR . '/Messages.subs.php');
492
			$message = basicMessageInfo($msgOptions['id'], true, false, false);
493 2
			$msgOptions['old_body'] = $message['body'];
494
		}
495
	}
496
497
	if (!empty($msgOptions['modify_time']))
498
	{
499
		$messages_columns['modified_time'] = $msgOptions['modify_time'];
500
		$messages_columns['modified_name'] = $msgOptions['modify_name'];
501 2
		$messages_columns['id_msg_modified'] = $modSettings['maxMsgID'];
502
	}
503
504
	if (isset($msgOptions['smileys_enabled']))
505
	{
506
		$messages_columns['smileys_enabled'] = empty($msgOptions['smileys_enabled']) ? 0 : 1;
507
	}
508 2
509
	// Which columns need to be ints?
510 2
	$messageInts = ['modified_time', 'id_msg_modified', 'smileys_enabled'];
511
	$update_parameters = [
512
		'id_msg' => $msgOptions['id'],
513
	];
514 2
515
	call_integration_hook('integrate_before_modify_post', [&$messages_columns, &$update_parameters, &$msgOptions, &$topicOptions, &$posterOptions, &$messageInts]);
516 2
517
	foreach ($messages_columns as $var => $val)
518
	{
519 2
		$messages_columns[$var] = $var . ' = {' . (in_array($var, $messageInts) ? 'int' : 'string') . ':var_' . $var . '}';
520
		$update_parameters['var_' . $var] = $val;
521 2
	}
522
523 2
	// Nothing to do?
524 2
	if (empty($messages_columns))
525
	{
526
		return true;
527
	}
528 2
529
	// Change the post.
530
	$db->query('', '
531
		UPDATE {db_prefix}messages
532
		SET ' . implode(', ', $messages_columns) . '
533
		WHERE id_msg = {int:id_msg}',
534 2
		$update_parameters
535
	);
536 2
537
	$attributes = [];
538 1
	// Lock and or sticky the post.
539
	if ($topicOptions['sticky_mode'] !== null)
540
	{
541 2
		$attributes['is_sticky'] = (int) $topicOptions['sticky_mode'];
542
	}
543 2
544
	if ($topicOptions['lock_mode'] !== null)
545
	{
546
		$attributes['locked'] = (int) $topicOptions['lock_mode'];
547
	}
548 2
549
	if ($topicOptions['poll'] !== null)
550 2
	{
551
		$attributes['id_poll'] = $topicOptions['poll'];
552
	}
553 2
554
	// If anything to do, do it.
555
	if (!empty($attributes))
556
	{
557
		setTopicAttribute($topicOptions['id'], $attributes);
558
	}
559 2
560
	// Mark the edited post as read.
561 2
	if (!empty($topicOptions['mark_as_read']) && User::$info->is_guest === false)
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...
562
	{
563
		// Since it's likely they *read* it before editing, let's try an UPDATE first.
564
		$flag = $db->query('', '
565 2
			UPDATE {db_prefix}log_topics
566
			SET id_msg = {int:id_msg}
567
			WHERE id_member = {int:current_member}
568 2
				AND id_topic = {int:id_topic}',
569
			[
570
				'current_member' => User::$info->id,
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...
571
				'id_msg' => $modSettings['maxMsgID'],
572
				'id_topic' => $topicOptions['id'],
573
			]
574 2
		)->affected_rows() != 0;
575 2
576 2
		if (empty($flag))
577
		{
578 2
			require_once(SUBSDIR . '/Topic.subs.php');
579
			markTopicsRead([User::$info->id, $topicOptions['id'], $modSettings['maxMsgID'], 0]);
580 2
		}
581
	}
582
583
	// If there's a custom search index, it needs to be modified...
584
	$searchAPI = new SearchApiWrapper(!empty($modSettings['search_index']) ? $modSettings['search_index'] : '');
585
	$searchAPI->postModified($msgOptions, $topicOptions, $posterOptions);
586
587
	if (isset($msgOptions['subject']))
588 2
	{
589 2
		// Only update the subject if this was the first message in the topic.
590
		$request = $db->query('', '
591 2
			SELECT 
592
				id_topic
593
			FROM {db_prefix}topics
594 2
			WHERE id_first_msg = {int:id_first_msg}
595
			LIMIT 1',
596
			[
597
				'id_first_msg' => $msgOptions['id'],
598
			]
599
		);
600
		if ($request->num_rows() === 1)
601 2
		{
602
			require_once(SUBSDIR . '/Messages.subs.php');
603
			updateSubjectStats($topicOptions['id'], $msgOptions['subject']);
604 2
		}
605
		$request->free_result();
606 2
	}
607 2
608
	// Finally, if we are setting the approved state, we need to do much more work :(
609 2
	if ($modSettings['postmod_active'] && isset($msgOptions['approved']))
610
	{
611
		approvePosts($msgOptions['id'], $msgOptions['approved']);
612
	}
613 2
614
	return true;
615
}
616
617
/**
618 2
 * Approve (or not) some posts... without permission checks...
619
 *
620
 * @param int|int[] $msgs - array of message ids
621
 * @param bool $approve = true
622
 *
623
 * @return bool|void
624
 * @package Posts
625
 */
626
function approvePosts($msgs, $approve = true)
627
{
628
	global $modSettings;
629
630
	$db = database();
631
632
	if (!is_array($msgs))
633
	{
634
		$msgs = [$msgs];
635
	}
636
637
	if (empty($msgs))
638
	{
639
		return false;
640
	}
641
642
	// May as well start at the beginning, working out *what* we need to change.
643
	$request = $db->query('', '
644
		SELECT 
645
			m.id_msg, m.approved, m.id_topic, m.id_board, m.body, m.subject, m.id_member,
646
			t.id_first_msg, t.id_last_msg, t.approved AS topic_approved,
647
			COALESCE(mem.real_name, m.poster_name) AS poster_name, mem.signature,
648
			b.count_posts
649
		FROM {db_prefix}messages AS m
650
			INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
651
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
652
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
653
		WHERE m.id_msg IN ({array_int:message_list})
654
			AND m.approved = {int:approved_state}',
655
		[
656
			'message_list' => $msgs,
657
			'approved_state' => $approve ? 0 : 1,
658
		]
659
	);
660
	$msgs = [];
661
	$topics = [];
662
	$topic_changes = [];
663
	$board_changes = [];
664
	$notification_topics = [];
665
	$notification_posts = [];
666
	$member_post_changes = [];
667
	while ($row = $request->fetch_assoc())
668
	{
669
		// Easy...
670
		$msgs[] = $row['id_msg'];
671
		$topics[] = $row['id_topic'];
672
673
		// Ensure our change array exists already.
674
		if (!isset($topic_changes[$row['id_topic']]))
675
		{
676
			$topic_changes[$row['id_topic']] = [
677
				'id_last_msg' => $row['id_last_msg'],
678
				'approved' => $row['topic_approved'],
679
				'replies' => 0,
680
				'unapproved_posts' => 0,
681
			];
682
		}
683
		if (!isset($board_changes[$row['id_board']]))
684
		{
685
			$board_changes[$row['id_board']] = [
686
				'posts' => 0,
687
				'topics' => 0,
688
				'unapproved_posts' => 0,
689
				'unapproved_topics' => 0,
690
			];
691
		}
692
693
		// If it's the first message, then the topic state changes!
694
		if ($row['id_msg'] === $row['id_first_msg'])
695
		{
696
			$topic_changes[$row['id_topic']]['approved'] = $approve ? 1 : 0;
697
698
			$board_changes[$row['id_board']]['unapproved_topics'] += $approve ? -1 : 1;
699
			$board_changes[$row['id_board']]['topics'] += $approve ? 1 : -1;
700
701
			// Note we need to ensure we announce this topic!
702
			$notification_topics[] = [
703
				'body' => $row['body'],
704
				'subject' => $row['subject'],
705
				'name' => $row['poster_name'],
706
				'board' => $row['id_board'],
707
				'topic' => $row['id_topic'],
708
				'msg' => $row['id_first_msg'],
709
				'poster' => $row['id_member'],
710
				'signature' => $row['signature'],
711
			];
712
		}
713
		else
714
		{
715
			$topic_changes[$row['id_topic']]['replies'] += $approve ? 1 : -1;
716
717
			// This will be a post... but don't notify unless it's not followed by approved ones.
718
			if ($row['id_msg'] > $row['id_last_msg'])
719
			{
720
				$notification_posts[$row['id_topic']][] = [
721
					'id' => $row['id_msg'],
722
					'body' => $row['body'],
723
					'subject' => $row['subject'],
724
					'name' => $row['poster_name'],
725
					'topic' => $row['id_topic'],
726
					'signature' => $row['signature'],
727
				];
728
			}
729
		}
730
731
		// If this is being approved and id_msg is higher than the current id_last_msg, then it changes.
732
		if ($approve && $row['id_msg'] > $topic_changes[$row['id_topic']]['id_last_msg'])
733
		{
734
			$topic_changes[$row['id_topic']]['id_last_msg'] = $row['id_msg'];
735
		}
736
		// If this is being unapproved, and it's equal to the id_last_msg, we need to find a new one!
737
		elseif (!$approve)
738
			// Default to the first message, and then we'll override in a bit ;)
739
		{
740
			$topic_changes[$row['id_topic']]['id_last_msg'] = $row['id_first_msg'];
741
		}
742
743
		$topic_changes[$row['id_topic']]['unapproved_posts'] += $approve ? -1 : 1;
744
		$board_changes[$row['id_board']]['unapproved_posts'] += $approve ? -1 : 1;
745
		$board_changes[$row['id_board']]['posts'] += $approve ? 1 : -1;
746
747
		// Post count for the user?
748
		if ($row['id_member'] && empty($row['count_posts']))
749
		{
750
			$member_post_changes[$row['id_member']] = isset($member_post_changes[$row['id_member']]) ? $member_post_changes[$row['id_member']] + 1 : 1;
751
		}
752
	}
753
	$request->free_result();
754
755
	if (empty($msgs))
756
	{
757
		return;
758
	}
759
760
	// Now we have the differences make the changes, first the easy one.
761
	$db->query('', '
762
		UPDATE {db_prefix}messages
763
		SET approved = {int:approved_state}
764
		WHERE id_msg IN ({array_int:message_list})',
765
		[
766
			'message_list' => $msgs,
767
			'approved_state' => $approve ? 1 : 0,
768
		]
769
	);
770
771
	// If we were unapproving, find the last msg in the topics...
772
	if (!$approve)
773
	{
774
		$db->fetchQuery('
775
			SELECT 
776
				id_topic, MAX(id_msg) AS id_last_msg
777
			FROM {db_prefix}messages
778
			WHERE id_topic IN ({array_int:topic_list})
779
				AND approved = {int:approved}
780
			GROUP BY id_topic',
781
			[
782
				'topic_list' => $topics,
783
				'approved' => 1,
784
			]
785
		)->fetch_callback(
786
			function ($row) use (&$topic_changes) {
787
				$topic_changes[$row['id_topic']]['id_last_msg'] = $row['id_last_msg'];
788
			}
789
		);
790
	}
791
792
	// ... next the topics...
793
	foreach ($topic_changes as $id => $changes)
794
	{
795
		$db->query('', '
796
			UPDATE {db_prefix}topics
797
			SET
798
				approved = {int:approved},
799
				unapproved_posts = CASE WHEN unapproved_posts + {int:unapproved_posts} < 0 THEN 0 ELSE unapproved_posts + {int:unapproved_posts} END,
800
				num_replies = CASE WHEN num_replies + {int:num_replies} < 0 THEN 0 ELSE num_replies + {int:num_replies} END,
801
				id_last_msg = {int:id_last_msg}
802
			WHERE id_topic = {int:id_topic}',
803
			[
804
				'approved' => $changes['approved'],
805
				'unapproved_posts' => $changes['unapproved_posts'],
806
				'num_replies' => $changes['replies'],
807
				'id_last_msg' => $changes['id_last_msg'],
808
				'id_topic' => $id,
809
			]
810
		);
811
	}
812
813
	// ... finally the boards...
814
	foreach ($board_changes as $id => $changes)
815
	{
816
		$db->query('', '
817
			UPDATE {db_prefix}boards
818
			SET
819
				num_posts = num_posts + {int:num_posts},
820
				unapproved_posts = CASE WHEN unapproved_posts + {int:unapproved_posts} < 0 THEN 0 ELSE unapproved_posts + {int:unapproved_posts} END,
821
				num_topics = CASE WHEN num_topics + {int:num_topics} < 0 THEN 0 ELSE num_topics + {int:num_topics} END,
822
				unapproved_topics = CASE WHEN unapproved_topics + {int:unapproved_topics} < 0 THEN 0 ELSE unapproved_topics + {int:unapproved_topics} END
823
			WHERE id_board = {int:id_board}',
824
			[
825
				'num_posts' => $changes['posts'],
826
				'unapproved_posts' => $changes['unapproved_posts'],
827
				'num_topics' => $changes['topics'],
828
				'unapproved_topics' => $changes['unapproved_topics'],
829
				'id_board' => $id,
830
			]
831
		);
832
	}
833
834
	// Finally, least importantly, notifications!
835
	if ($approve)
836
	{
837
		require_once(SUBSDIR . '/Notification.subs.php');
838
839
		if (!empty($notification_topics))
840
		{
841
			sendBoardNotifications($notification_topics);
842
		}
843
844
		if (!empty($notification_posts))
845
		{
846
			sendApprovalNotifications($notification_posts);
847
		}
848
849
		$db->query('', '
850
			DELETE FROM {db_prefix}approval_queue
851
			WHERE id_msg IN ({array_int:message_list})
852
				AND id_attach = {int:id_attach}',
853
			[
854
				'message_list' => $msgs,
855
				'id_attach' => 0,
856
			]
857
		);
858
	}
859
	// If unapproving, add to the approval queue!
860
	else
861
	{
862
		$msgInserts = [];
863
		foreach ($msgs as $msg)
864
		{
865
			$msgInserts[] = [$msg];
866
		}
867
868
		$db->insert('ignore',
869
			'{db_prefix}approval_queue',
870
			['id_msg' => 'int'],
871
			$msgInserts,
872
			['id_msg']
873
		);
874
	}
875
876
	if (!empty($modSettings['mentions_enabled']))
877
	{
878
		require_once(SUBSDIR . '/Mentions.subs.php');
879
		toggleMentionsApproval($msgs, $approve);
880
	}
881
882
	// Update the last messages on the boards...
883
	updateLastMessages(array_keys($board_changes));
884
885
	// Post count for the members?
886
	if (!empty($member_post_changes))
887
	{
888
		require_once(SUBSDIR . '/Members.subs.php');
889
		foreach ($member_post_changes as $id_member => $count_change)
890
		{
891
			updateMemberData($id_member, ['posts' => 'posts ' . ($approve ? '+' : '-') . ' ' . $count_change]);
892
		}
893
	}
894
895
	return true;
896
}
897
898
/**
899
 * Takes an array of board IDs and updates their last messages.
900
 *
901
 * - If the board has a parent, that parent board is also automatically updated.
902
 * - The columns updated are id_last_msg and last_updated.
903
 * - Note that id_last_msg should always be updated using this function
904
 * and is not automatically updated upon other changes.
905
 *
906
 * @param int[]|int $setboards
907
 * @param int $id_msg = 0
908
 *
909
 * @return bool
910
 * @package Posts
911
 *
912
 */
913
function updateLastMessages($setboards, $id_msg = 0)
914
{
915
	global $board_info, $board;
916
917
	$db = database();
918
919 26
	// Please - let's be sane.
920
	if (empty($setboards))
921 26
	{
922
		return false;
923
	}
924 26
925
	if (!is_array($setboards))
926
	{
927
		$setboards = [$setboards];
928
	}
929 26
930
	$lastMsg = [];
931 26
932
	// If we don't know the id_msg, we need to find it.
933
	if (!$id_msg)
934 26
	{
935
		// Find the latest message on this board (highest id_msg.)
936
		$db->fetchQuery('
937 26
			SELECT 
938
				id_board, MAX(id_last_msg) AS id_msg
939
			FROM {db_prefix}topics
940 12
			WHERE id_board IN ({array_int:board_list})
941
				AND approved = {int:approved}
942
			GROUP BY id_board',
943
			[
944
				'board_list' => $setboards,
945
				'approved' => 1,
946
			]
947
		)->fetch_callback(
948 12
			function ($row) use (&$lastMsg) {
949 12
				$lastMsg[$row['id_board']] = $row['id_msg'];
950
			}
951 12
		);
952
	}
953 12
	else
954 12
	{
955
		// Just to note - there should only be one board passed if we are doing this.
956
		foreach ($setboards as $id_board)
957
		{
958
			$lastMsg[$id_board] = $id_msg;
959
		}
960 26
	}
961
962 26
	$parent_boards = [];
963
964
	// Keep track of the last modified dates.
965
	$lastModified = $lastMsg;
966 26
967
	// Get all the sub-boards for the parents if they have some...
968
	foreach ($setboards as $id_board)
969 26
	{
970
		if (!isset($lastMsg[$id_board]))
971
		{
972 26
			$lastMsg[$id_board] = 0;
973
			$lastModified[$id_board] = 0;
974 26
		}
975
976
		if (!empty($board) && $id_board == $board)
977
		{
978
			$parents = $board_info['parent_boards'];
979
		}
980 26
		else
981
		{
982 4
			$parents = getBoardParents($id_board);
983
		}
984
985
		// Ignore any parents on the top child level.
986 22
		foreach ($parents as $id => $parent)
987
		{
988
			if ($parent['level'] != 0)
989
			{
990 26
				// If we're already doing this one as a board, is this a higher last modifier?
991
				if (isset($lastModified[$id]) && $lastModified[$id_board] > $lastModified[$id])
992 22
				{
993
					$lastModified[$id] = $lastModified[$id_board];
994
				}
995
				elseif (!isset($lastModified[$id]) && (!isset($parent_boards[$id]) || $parent_boards[$id] < $lastModified[$id_board]))
996
				{
997
					$parent_boards[$id] = $lastModified[$id_board];
998
				}
999
			}
1000
		}
1001 13
	}
1002
1003
	// Note to help understand what is happening here. For parents, we update the timestamp of the last message to determine
1004
	// whether there are sub-boards that have not been read. For the boards themselves we update both this and id_last_msg.
1005
	$board_updates = [];
1006
	$parent_updates = [];
1007
1008
	// Finally, to save on queries, make the changes...
1009 26
	foreach ($parent_boards as $id => $msg)
1010 26
	{
1011
		if (!isset($parent_updates[$msg]))
1012
		{
1013 26
			$parent_updates[$msg] = [$id];
1014
		}
1015
		else
1016
		{
1017
			$parent_updates[$msg][] = $id;
1018
		}
1019
	}
1020
1021
	foreach ($lastMsg as $id => $msg)
1022
	{
1023
		if (!isset($board_updates[$msg . '-' . $lastModified[$id]]))
1024
		{
1025 26
			$board_updates[$msg . '-' . $lastModified[$id]] = [
1026
				'id' => $msg,
1027 26
				'updated' => $lastModified[$id],
1028
				'boards' => [$id]
1029 26
			];
1030 26
		}
1031 26
		else
1032 26
		{
1033
			$board_updates[$msg . '-' . $lastModified[$id]]['boards'][] = $id;
1034
		}
1035
	}
1036
1037 13
	// Now commit the changes!
1038
	foreach ($parent_updates as $id_msg => $boards)
1039
	{
1040
		$db->query('', '
1041
			UPDATE {db_prefix}boards
1042 26
			SET id_msg_updated = {int:id_msg_updated}
1043
			WHERE id_board IN ({array_int:board_list})
1044
				AND id_msg_updated < {int:id_msg_updated}',
1045
			[
1046
				'board_list' => $boards,
1047
				'id_msg_updated' => $id_msg,
1048
			]
1049
		);
1050
	}
1051
	foreach ($board_updates as $board_data)
1052
	{
1053
		$db->query('', '
1054
			UPDATE {db_prefix}boards
1055 26
			SET id_last_msg = {int:id_last_msg}, id_msg_updated = {int:id_msg_updated}
1056
			WHERE id_board IN ({array_int:board_list})',
1057 26
			[
1058
				'board_list' => $board_data['boards'],
1059
				'id_last_msg' => $board_data['id'],
1060
				'id_msg_updated' => $board_data['updated'],
1061
			]
1062 26
		);
1063 26
	}
1064 26
1065
	return true;
1066
}
1067
1068 26
/**
1069
 * Get the latest post made on the system
1070
 *
1071
 * - Respects approved, recycled, and board permissions
1072
 *
1073
 * @return array
1074
 * @package Posts
1075
 */
1076
function lastPost()
1077
{
1078
	global $scripturl, $modSettings;
1079
1080
	$db = database();
1081
1082
	// Find it by the board - better to order by board than sort the entire messages table.
1083
	$request = $db->fetchQuery('
1084
		SELECT 
1085
			ml.poster_time, ml.subject, ml.id_topic, ml.poster_name, SUBSTRING(ml.body, 1, 385) AS body,
1086
			ml.smileys_enabled
1087
		FROM {db_prefix}boards AS b
1088
			INNER JOIN {db_prefix}messages AS ml ON (ml.id_msg = b.id_last_msg)
1089
		WHERE {query_wanna_see_board}' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? '
1090
			AND b.id_board != {int:recycle_board}' : '') . '
1091
			AND ml.approved = {int:is_approved}
1092
		ORDER BY b.id_msg_updated DESC
1093
		LIMIT 1',
1094
		[
1095
			'recycle_board' => $modSettings['recycle_board'],
1096
			'is_approved' => 1,
1097
		]
1098
	);
1099
	if ($request->num_rows() === 0)
1100
	{
1101
		return [];
1102
	}
1103
	$row = $request->fetch_assoc();
1104
	$request->free_result();
1105
1106
	// Censor the subject and post...
1107
	$row['subject'] = censor($row['subject']);
1108
	$row['body'] = censor($row['body']);
1109
1110
	$bbc_parser = ParserWrapper::instance();
1111
1112
	$row['body'] = strip_tags(strtr($bbc_parser->parseMessage($row['body'], $row['smileys_enabled']), ['<br />' => '&#10;']));
1113
	$row['body'] = Util::shorten_text($row['body'], !empty($modSettings['lastpost_preview_characters']) ? $modSettings['lastpost_preview_characters'] : 128, true);
1114
1115
	// Send the data.
1116
	return [
1117
		'topic' => $row['id_topic'],
1118
		'subject' => $row['subject'],
1119
		'short_subject' => Util::shorten_text($row['subject'], !empty($modSettings['subject_length']) ? $modSettings['subject_length'] : 32) . '</a>',
1120
		'preview' => $row['body'],
1121
		'time' => standardTime($row['poster_time']),
1122
		'html_time' => htmlTime($row['poster_time']),
1123
		'timestamp' => forum_time(true, $row['poster_time']),
1124
		'href' => $scripturl . '?topic=' . $row['id_topic'] . '.new;topicseen#new',
1125
		'link' => '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.new;topicseen#new">' . $row['subject'] . '</a>'
1126
	];
1127
}
1128
1129
/**
1130
 * Prepares a post for the post form
1131
 *
1132
 * What it does:
1133
 *
1134
 * - Will add the appropriate Re: to the post-subject if it's a reply to an existing post
1135
 * - If quoting a post or editing a post, this function also prepares the message body
1136
 * - returns array($subject, $message) or false on error
1137
 *
1138
 * @param int $editing
1139
 * @param int|null|false $topic
1140
 * @param string $first_subject
1141
 * @param int $msg_id
1142
 *
1143
 * @return false|array
1144
 * @throws \ElkArte\Exceptions\Exception quoted_post_deleted
1145
 * @package Posts
1146
 *
1147
 */
1148
function getFormMsgSubject($editing, $topic, $first_subject = '', $msg_id = 0)
1149
{
1150
	global $modSettings;
1151
1152
	$db = database();
1153
1154
	switch ($editing)
1155
	{
1156
		// Modifying an existing message
1157
		case 1:
1158
			require_once(SUBSDIR . '/Messages.subs.php');
1159
1160
			// Get the existing message.
1161
			$message = messageDetails((int) $msg_id, (int) $topic);
1162
1163
			// The message they were trying to edit was most likely deleted.
1164
			if ($message === false)
0 ignored issues
show
introduced by
The condition $message === false is always false.
Loading history...
1165
			{
1166
				return false;
1167
			}
1168
1169
			$errors = checkMessagePermissions($message['message']);
1170
1171
			prepareMessageContext($message);
1172
1173
			if (!empty($errors))
1174
			{
1175
				$message['errors'] = $errors;
1176
			}
1177
1178
			return $message;
1179
		// Posting a quoted reply?
1180
		case 2:
1181
			// Make sure they _can_ quote this post, and if so, get it.
1182
			$request = $db->query('', '
1183
				SELECT
1184
					m.subject, COALESCE(mem.real_name, m.poster_name) AS poster_name, m.poster_time, m.body
1185
				FROM {db_prefix}messages AS m
1186
					INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board})
1187
					LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
1188
				WHERE m.id_msg = {int:id_msg}' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
1189
					AND m.approved = {int:is_approved}') . '
1190
				LIMIT 1',
1191
				[
1192
					'id_msg' => $msg_id,
1193
					'is_approved' => 1,
1194
				]
1195
			);
1196
			if ($request->num_rows() === 0)
1197
			{
1198
				throw new \ElkArte\Exceptions\Exception('quoted_post_deleted', false);
1199
			}
1200
			list ($form_subject, $mname, $mdate, $form_message) = $request->fetch_row();
1201
			$request->free_result();
1202
1203
			// Add 'Re: ' to the front of the quoted subject.
1204
			$response_prefix = response_prefix();
1205
			if (trim($response_prefix) !== '' && Util::strpos($form_subject, trim($response_prefix)) !== 0)
1206
			{
1207
				$form_subject = $response_prefix . $form_subject;
1208
			}
1209
1210
			// Censor the message and subject.
1211
			$form_message = censor($form_message);
1212
			$form_subject = censor($form_subject);
1213
1214
			$form_message = un_preparsecode($form_message);
1215
			$form_message = removeNestedQuotes($form_message);
1216
1217
			// Add a quote string on the front and end.
1218
			$form_message = '[quote author=' . $mname . ' link=msg=' . (int) $msg_id . ' date=' . $mdate . ']' . "\n" . rtrim($form_message) . "\n" . '[/quote]';
1219
1220
			break;
1221
		// Posting a reply without a quote?
1222
		case 3:
1223
			// Get the first message's subject.
1224
			$form_subject = $first_subject;
1225
1226
			// Add 'Re: ' to the front of the subject.
1227
			$response_prefix = response_prefix();
1228
			if ($form_subject !== '' && trim($response_prefix) !== '' && Util::strpos($form_subject, trim($response_prefix)) !== 0)
1229
			{
1230
				$form_subject = $response_prefix . $form_subject;
1231
			}
1232
1233
			// Censor the subject.
1234
			$form_subject = censor($form_subject);
1235
1236
			$form_message = '';
1237
1238
			break;
1239
		case 4:
1240
		default:
1241
			$form_subject = $first_subject;
1242
			$form_message = '';
1243
1244
			break;
1245
	}
1246
1247
	return [$form_subject, $form_message];
1248
}
1249
1250
/**
1251
 * Update topic subject.
1252
 *
1253
 * - If $all is true, for all messages in the topic, otherwise only the first message.
1254
 *
1255
 * @param array $topic_info topic information as returned by getTopicInfo()
1256
 * @param string $custom_subject
1257
 * @param string $response_prefix = ''
1258
 * @param bool $all = false
1259
 * @package Posts
1260
 */
1261
function topicSubject($topic_info, $custom_subject, $response_prefix = '', $all = false)
1262
{
1263
	$db = database();
1264
1265
	if ($all)
1266
	{
1267
		$db->query('', '
1268
			UPDATE {db_prefix}messages
1269
			SET subject = {string:subject}
1270
			WHERE id_topic = {int:current_topic}',
1271
			[
1272
				'current_topic' => $topic_info['id_topic'],
1273
				'subject' => $response_prefix . $custom_subject,
1274
			]
1275
		);
1276
	}
1277
	$db->query('', '
1278
		UPDATE {db_prefix}messages
1279
		SET subject = {string:custom_subject}
1280
		WHERE id_msg = {int:id_first_msg}',
1281
		[
1282
			'id_first_msg' => $topic_info['id_first_msg'],
1283
			'custom_subject' => $custom_subject,
1284
		]
1285
	);
1286
}
1287