topicSubject()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 23
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

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

428
	updateMessageStats(true, /** @scrutinizer ignore-type */ $msgOptions['id']);
Loading history...
429 26
430
	// Update the last message on the board assuming it's approved AND the topic is.
431
	if ($msgOptions['approved'])
432 26
	{
433
		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

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

437
	ignore_user_abort(/** @scrutinizer ignore-type */ $previous_ignore_user_abort);
Loading history...
438 26
439
	// Success.
440
	return true;
441 26
}
442
443
/**
444
 * Modifying a post...
445
 *
446
 * @param mixed[] $msgOptions
447
 * @param mixed[] $topicOptions
448
 * @param mixed[] $posterOptions
449
 *
450
 * @return bool
451
 * @package Posts
452
 *
453
 */
454
function modifyPost(&$msgOptions, &$topicOptions, &$posterOptions)
455
{
456
	global $modSettings;
457
458 2
	$db = database();
459
460 2
	$topicOptions['poll'] = isset($topicOptions['poll']) ? (int) $topicOptions['poll'] : null;
461
	$topicOptions['lock_mode'] = $topicOptions['lock_mode'] ?? null;
462 2
	$topicOptions['sticky_mode'] = $topicOptions['sticky_mode'] ?? null;
463 2
464 2
	// This is longer than it has to be, but makes it so we only set/change what we have to.
465
	$messages_columns = array();
466
	if (isset($posterOptions['name']))
467 2
	{
468 2
		$messages_columns['poster_name'] = $posterOptions['name'];
469
	}
470 2
471
	if (isset($posterOptions['email']))
472
	{
473 2
		$messages_columns['poster_email'] = $posterOptions['email'];
474
	}
475 2
476
	if (isset($msgOptions['icon']))
477
	{
478 2
		$messages_columns['icon'] = $msgOptions['icon'];
479
	}
480 2
481
	if (isset($msgOptions['subject']))
482
	{
483 2
		$messages_columns['subject'] = $msgOptions['subject'];
484
	}
485 2
486
	if (isset($msgOptions['body']))
487
	{
488 2
		$messages_columns['body'] = $msgOptions['body'];
489
490 2
		// using a custom search index, then lets get the old message so we can update our index as needed
491
		if (!empty($modSettings['search_custom_index_config']))
492
		{
493 2
			require_once(SUBSDIR . '/Messages.subs.php');
494
			$message = basicMessageInfo($msgOptions['id'], true, false, false);
495
			$msgOptions['old_body'] = $message['body'];
496
		}
497
	}
498
499
	if (!empty($msgOptions['modify_time']))
500
	{
501 2
		$messages_columns['modified_time'] = $msgOptions['modify_time'];
502
		$messages_columns['modified_name'] = $msgOptions['modify_name'];
503
		$messages_columns['id_msg_modified'] = $modSettings['maxMsgID'];
504
	}
505
506
	if (isset($msgOptions['smileys_enabled']))
507
	{
508 2
		$messages_columns['smileys_enabled'] = empty($msgOptions['smileys_enabled']) ? 0 : 1;
509
	}
510 2
511
	// Which columns need to be ints?
512
	$messageInts = array('modified_time', 'id_msg_modified', 'smileys_enabled');
513
	$update_parameters = array(
514 2
		'id_msg' => $msgOptions['id'],
515
	);
516 2
517
	call_integration_hook('integrate_before_modify_post', array(&$messages_columns, &$update_parameters, &$msgOptions, &$topicOptions, &$posterOptions, &$messageInts));
518
519 2
	foreach ($messages_columns as $var => $val)
520
	{
521 2
		$messages_columns[$var] = $var . ' = {' . (in_array($var, $messageInts) ? 'int' : 'string') . ':var_' . $var . '}';
522
		$update_parameters['var_' . $var] = $val;
523 2
	}
524 2
525
	// Nothing to do?
526
	if (empty($messages_columns))
527
	{
528 2
		return true;
529
	}
530
531
	// Change the post.
532
	$db->query('', '
533
		UPDATE {db_prefix}messages
534 2
		SET ' . implode(', ', $messages_columns) . '
535
		WHERE id_msg = {int:id_msg}',
536 2
		$update_parameters
537
	);
538 1
539
	$attributes = array();
540
	// Lock and or sticky the post.
541 2
	if ($topicOptions['sticky_mode'] !== null)
542
	{
543 2
		$attributes['is_sticky'] = $topicOptions['sticky_mode'];
544
	}
545
546
	if ($topicOptions['lock_mode'] !== null)
547
	{
548 2
		$attributes['locked'] = $topicOptions['lock_mode'];
549
	}
550 2
551
	if ($topicOptions['poll'] !== null)
552
	{
553 2
		$attributes['id_poll'] = $topicOptions['poll'];
554
	}
555
556
	// If anything to do, do it.
557
	if (!empty($attributes))
558
	{
559 2
		setTopicAttribute($topicOptions['id'], $attributes);
560
	}
561 2
562
	// Mark the edited post as read.
563
	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...
564
	{
565 2
		// Since it's likely they *read* it before editing, let's try an UPDATE first.
566
		$flag = $db->query('', '
567
			UPDATE {db_prefix}log_topics
568 2
			SET id_msg = {int:id_msg}
569
			WHERE id_member = {int:current_member}
570
				AND id_topic = {int:id_topic}',
571
			array(
572
				'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...
573
				'id_msg' => $modSettings['maxMsgID'],
574 2
				'id_topic' => $topicOptions['id'],
575 2
			)
576 2
		)->affected_rows() != 0;
577
578 2
		if (empty($flag))
579
		{
580 2
			require_once(SUBSDIR . '/Topic.subs.php');
581
			markTopicsRead(array(User::$info->id, $topicOptions['id'], $modSettings['maxMsgID'], 0), false);
582
		}
583
	}
584
585
	// If there's a custom search index, it needs to be modified...
586
	$searchAPI = new SearchApiWrapper(!empty($modSettings['search_index']) ? $modSettings['search_index'] : '');
587
	$searchAPI->postModified($msgOptions, $topicOptions, $posterOptions);
588 2
589 2
	if (isset($msgOptions['subject']))
590
	{
591 2
		// Only update the subject if this was the first message in the topic.
592
		$request = $db->query('', '
593
			SELECT 
594 2
				id_topic
595
			FROM {db_prefix}topics
596
			WHERE id_first_msg = {int:id_first_msg}
597
			LIMIT 1',
598
			array(
599
				'id_first_msg' => $msgOptions['id'],
600
			)
601 2
		);
602
		if ($request->num_rows() === 1)
603
		{
604 2
			require_once(SUBSDIR . '/Messages.subs.php');
605
			updateSubjectStats($topicOptions['id'], $msgOptions['subject']);
606 2
		}
607 2
		$request->free_result();
608
	}
609 2
610
	// Finally, if we are setting the approved state we need to do much more work :(
611
	if ($modSettings['postmod_active'] && isset($msgOptions['approved']))
612
	{
613 2
		approvePosts($msgOptions['id'], $msgOptions['approved']);
614
	}
615
616
	return true;
617
}
618 2
619
/**
620
 * Approve (or not) some posts... without permission checks...
621
 *
622
 * @param int|int[] $msgs - array of message ids
623
 * @param bool $approve = true
624
 *
625
 * @return bool|void
626
 * @package Posts
627
 */
628
function approvePosts($msgs, $approve = true)
629
{
630
	global $modSettings;
631
632
	$db = database();
633
634
	if (!is_array($msgs))
635
	{
636
		$msgs = [$msgs];
637
	}
638
639
	if (empty($msgs))
640
	{
641
		return false;
642
	}
643
644
	// May as well start at the beginning, working out *what* we need to change.
645
	$request = $db->query('', '
646
		SELECT 
647
			m.id_msg, m.approved, m.id_topic, m.id_board, m.body, m.subject, m.id_member,
648
			t.id_first_msg, t.id_last_msg, t.approved AS topic_approved,
649
			COALESCE(mem.real_name, m.poster_name) AS poster_name, mem.signature,
650
			b.count_posts
651
		FROM {db_prefix}messages AS m
652
			INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
653
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
654
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
655
		WHERE m.id_msg IN ({array_int:message_list})
656
			AND m.approved = {int:approved_state}',
657
		array(
658
			'message_list' => $msgs,
659
			'approved_state' => $approve ? 0 : 1,
660
		)
661
	);
662
	$msgs = [];
663
	$topics = [];
664
	$topic_changes = [];
665
	$board_changes = [];
666
	$notification_topics = [];
667
	$notification_posts = [];
668
	$member_post_changes = [];
669
	while ($row = $request->fetch_assoc())
670
	{
671
		// Easy...
672
		$msgs[] = $row['id_msg'];
673
		$topics[] = $row['id_topic'];
674
675
		// Ensure our change array exists already.
676
		if (!isset($topic_changes[$row['id_topic']]))
677
		{
678
			$topic_changes[$row['id_topic']] = [
679
				'id_last_msg' => $row['id_last_msg'],
680
				'approved' => $row['topic_approved'],
681
				'replies' => 0,
682
				'unapproved_posts' => 0,
683
			];
684
		}
685
		if (!isset($board_changes[$row['id_board']]))
686
		{
687
			$board_changes[$row['id_board']] = [
688
				'posts' => 0,
689
				'topics' => 0,
690
				'unapproved_posts' => 0,
691
				'unapproved_topics' => 0,
692
			];
693
		}
694
695
		// If it's the first message then the topic state changes!
696
		if ($row['id_msg'] === $row['id_first_msg'])
697
		{
698
			$topic_changes[$row['id_topic']]['approved'] = $approve ? 1 : 0;
699
700
			$board_changes[$row['id_board']]['unapproved_topics'] += $approve ? -1 : 1;
701
			$board_changes[$row['id_board']]['topics'] += $approve ? 1 : -1;
702
703
			// Note we need to ensure we announce this topic!
704
			$notification_topics[] = [
705
				'body' => $row['body'],
706
				'subject' => $row['subject'],
707
				'name' => $row['poster_name'],
708
				'board' => $row['id_board'],
709
				'topic' => $row['id_topic'],
710
				'msg' => $row['id_first_msg'],
711
				'poster' => $row['id_member'],
712
				'signature' => $row['signature'],
713
			];
714
		}
715
		else
716
		{
717
			$topic_changes[$row['id_topic']]['replies'] += $approve ? 1 : -1;
718
719
			// This will be a post... but don't notify unless it's not followed by approved ones.
720
			if ($row['id_msg'] > $row['id_last_msg'])
721
			{
722
				$notification_posts[$row['id_topic']][] = [
723
					'id' => $row['id_msg'],
724
					'body' => $row['body'],
725
					'subject' => $row['subject'],
726
					'name' => $row['poster_name'],
727
					'topic' => $row['id_topic'],
728
					'signature' => $row['signature'],
729
				];
730
			}
731
		}
732
733
		// If this is being approved and id_msg is higher than the current id_last_msg then it changes.
734
		if ($approve && $row['id_msg'] > $topic_changes[$row['id_topic']]['id_last_msg'])
735
		{
736
			$topic_changes[$row['id_topic']]['id_last_msg'] = $row['id_msg'];
737
		}
738
		// If this is being unapproved, and it's equal to the id_last_msg we need to find a new one!
739
		elseif (!$approve)
740
			// Default to the first message and then we'll override in a bit ;)
741
		{
742
			$topic_changes[$row['id_topic']]['id_last_msg'] = $row['id_first_msg'];
743
		}
744
745
		$topic_changes[$row['id_topic']]['unapproved_posts'] += $approve ? -1 : 1;
746
		$board_changes[$row['id_board']]['unapproved_posts'] += $approve ? -1 : 1;
747
		$board_changes[$row['id_board']]['posts'] += $approve ? 1 : -1;
748
749
		// Post count for the user?
750
		if ($row['id_member'] && empty($row['count_posts']))
751
		{
752
			$member_post_changes[$row['id_member']] = isset($member_post_changes[$row['id_member']]) ? $member_post_changes[$row['id_member']] + 1 : 1;
753
		}
754
	}
755
	$request->free_result();
756
757
	if (empty($msgs))
758
	{
759
		return;
760
	}
761
762
	// Now we have the differences make the changes, first the easy one.
763
	$db->query('', '
764
		UPDATE {db_prefix}messages
765
		SET approved = {int:approved_state}
766
		WHERE id_msg IN ({array_int:message_list})',
767
		[
768
			'message_list' => $msgs,
769
			'approved_state' => $approve ? 1 : 0,
770
		]
771
	);
772
773
	// If we were un-approving find the last msg in the topics...
774
	if (!$approve)
775
	{
776
		$db->fetchQuery('
777
			SELECT 
778
				id_topic, MAX(id_msg) AS id_last_msg
779
			FROM {db_prefix}messages
780
			WHERE id_topic IN ({array_int:topic_list})
781
				AND approved = {int:approved}
782
			GROUP BY id_topic',
783
			array(
784
				'topic_list' => $topics,
785
				'approved' => 1,
786
			)
787
		)->fetch_callback(
788
			function ($row) use (&$topic_changes) {
789
				$topic_changes[$row['id_topic']]['id_last_msg'] = $row['id_last_msg'];
790
			}
791
		);
792
	}
793
794
	// ... next the topics...
795
	foreach ($topic_changes as $id => $changes)
796
	{
797
		$db->query('', '
798
			UPDATE {db_prefix}topics
799
			SET
800
				approved = {int:approved},
801
				unapproved_posts = CASE WHEN unapproved_posts + {int:unapproved_posts} < 0 THEN 0 ELSE unapproved_posts + {int:unapproved_posts} END,
802
				num_replies = CASE WHEN num_replies + {int:num_replies} < 0 THEN 0 ELSE num_replies + {int:num_replies} END,
803
				id_last_msg = {int:id_last_msg}
804
			WHERE id_topic = {int:id_topic}',
805
			[
806
				'approved' => $changes['approved'],
807
				'unapproved_posts' => $changes['unapproved_posts'],
808
				'num_replies' => $changes['replies'],
809
				'id_last_msg' => $changes['id_last_msg'],
810
				'id_topic' => $id,
811
			]
812
		);
813
	}
814
815
	// ... finally the boards...
816
	foreach ($board_changes as $id => $changes)
817
	{
818
		$db->query('', '
819
			UPDATE {db_prefix}boards
820
			SET
821
				num_posts = num_posts + {int:num_posts},
822
				unapproved_posts = CASE WHEN unapproved_posts + {int:unapproved_posts} < 0 THEN 0 ELSE unapproved_posts + {int:unapproved_posts} END,
823
				num_topics = CASE WHEN num_topics + {int:num_topics} < 0 THEN 0 ELSE num_topics + {int:num_topics} END,
824
				unapproved_topics = CASE WHEN unapproved_topics + {int:unapproved_topics} < 0 THEN 0 ELSE unapproved_topics + {int:unapproved_topics} END
825
			WHERE id_board = {int:id_board}',
826
			array(
827
				'num_posts' => $changes['posts'],
828
				'unapproved_posts' => $changes['unapproved_posts'],
829
				'num_topics' => $changes['topics'],
830
				'unapproved_topics' => $changes['unapproved_topics'],
831
				'id_board' => $id,
832
			)
833
		);
834
	}
835
836
	// Finally, least importantly, notifications!
837
	if ($approve)
838
	{
839
		require_once(SUBSDIR . '/Notification.subs.php');
840
841
		if (!empty($notification_topics))
842
		{
843
			sendBoardNotifications($notification_topics);
844
		}
845
846
		if (!empty($notification_posts))
847
		{
848
			sendApprovalNotifications($notification_posts);
849
		}
850
851
		$db->query('', '
852
			DELETE FROM {db_prefix}approval_queue
853
			WHERE id_msg IN ({array_int:message_list})
854
				AND id_attach = {int:id_attach}',
855
			[
856
				'message_list' => $msgs,
857
				'id_attach' => 0,
858
			]
859
		);
860
	}
861
	// If un-approving add to the approval queue!
862
	else
863
	{
864
		$msgInserts = array();
865
		foreach ($msgs as $msg)
866
		{
867
			$msgInserts[] = array($msg);
868
		}
869
870
		$db->insert('ignore',
871
			'{db_prefix}approval_queue',
872
			['id_msg' => 'int'],
873
			$msgInserts,
874
			['id_msg']
875
		);
876
	}
877
878
	if (!empty($modSettings['mentions_enabled']))
879
	{
880
		require_once(SUBSDIR . '/Mentions.subs.php');
881
		toggleMentionsApproval($msgs, $approve);
882
	}
883
884
	// Update the last messages on the boards...
885
	updateLastMessages(array_keys($board_changes));
886
887
	// Post count for the members?
888
	if (!empty($member_post_changes))
889
	{
890
		require_once(SUBSDIR . '/Members.subs.php');
891
		foreach ($member_post_changes as $id_member => $count_change)
892
		{
893
			updateMemberData($id_member, ['posts' => 'posts ' . ($approve ? '+' : '-') . ' ' . $count_change]);
894
		}
895
	}
896
897
	return true;
898
}
899
900
/**
901
 * Takes an array of board IDs and updates their last messages.
902
 *
903
 * - If the board has a parent, that parent board is also automatically updated.
904
 * - The columns updated are id_last_msg and last_updated.
905
 * - Note that id_last_msg should always be updated using this function,
906
 * and is not automatically updated upon other changes.
907
 *
908
 * @param int[]|int $setboards
909
 * @param int $id_msg = 0
910
 *
911
 * @return bool
912
 * @package Posts
913
 *
914
 */
915
function updateLastMessages($setboards, $id_msg = 0)
916
{
917
	global $board_info, $board;
918
919 26
	$db = database();
920
921 26
	// Please - let's be sane.
922
	if (empty($setboards))
923
	{
924 26
		return false;
925
	}
926
927
	if (!is_array($setboards))
928
	{
929 26
		$setboards = array($setboards);
930
	}
931 26
932
	$lastMsg = array();
933
934 26
	// If we don't know the id_msg we need to find it.
935
	if (!$id_msg)
936
	{
937 26
		// Find the latest message on this board (highest id_msg.)
938
		$db->fetchQuery('
939
			SELECT 
940 12
				id_board, MAX(id_last_msg) AS id_msg
941
			FROM {db_prefix}topics
942
			WHERE id_board IN ({array_int:board_list})
943
				AND approved = {int:approved}
944
			GROUP BY id_board',
945
			array(
946
				'board_list' => $setboards,
947
				'approved' => 1,
948 12
			)
949 12
		)->fetch_callback(
950
			function ($row) use (&$lastMsg) {
951 12
				$lastMsg[$row['id_board']] = $row['id_msg'];
952
			}
953 12
		);
954 12
	}
955
	else
956
	{
957
		// Just to note - there should only be one board passed if we are doing this.
958
		foreach ($setboards as $id_board)
959
		{
960 26
			$lastMsg[$id_board] = $id_msg;
961
		}
962 26
	}
963
964
	$parent_boards = array();
965
966 26
	// Keep track of last modified dates.
967
	$lastModified = $lastMsg;
968
969 26
	// Get all the sub-boards for the parents, if they have some...
970
	foreach ($setboards as $id_board)
971
	{
972 26
		if (!isset($lastMsg[$id_board]))
973
		{
974 26
			$lastMsg[$id_board] = 0;
975
			$lastModified[$id_board] = 0;
976
		}
977
978
		if (!empty($board) && $id_board == $board)
979
		{
980 26
			$parents = $board_info['parent_boards'];
981
		}
982 4
		else
983
		{
984
			$parents = getBoardParents($id_board);
985
		}
986 22
987
		// Ignore any parents on the top child level.
988
		foreach ($parents as $id => $parent)
989
		{
990 26
			if ($parent['level'] != 0)
991
			{
992 22
				// If we're already doing this one as a board, is this a higher last modified?
993
				if (isset($lastModified[$id]) && $lastModified[$id_board] > $lastModified[$id])
994
				{
995
					$lastModified[$id] = $lastModified[$id_board];
996
				}
997
				elseif (!isset($lastModified[$id]) && (!isset($parent_boards[$id]) || $parent_boards[$id] < $lastModified[$id_board]))
998
				{
999
					$parent_boards[$id] = $lastModified[$id_board];
1000
				}
1001 13
			}
1002
		}
1003
	}
1004
1005
	// Note to help understand what is happening here. For parents we update the timestamp of the last message for determining
1006
	// whether there are sub-boards which have not been read. For the boards themselves we update both this and id_last_msg.
1007
	$board_updates = array();
1008
	$parent_updates = array();
1009 26
1010 26
	// Finally, to save on queries make the changes...
1011
	foreach ($parent_boards as $id => $msg)
1012
	{
1013 26
		if (!isset($parent_updates[$msg]))
1014
		{
1015
			$parent_updates[$msg] = array($id);
1016
		}
1017
		else
1018
		{
1019
			$parent_updates[$msg][] = $id;
1020
		}
1021
	}
1022
1023
	foreach ($lastMsg as $id => $msg)
1024
	{
1025 26
		if (!isset($board_updates[$msg . '-' . $lastModified[$id]]))
1026
		{
1027 26
			$board_updates[$msg . '-' . $lastModified[$id]] = array(
1028
				'id' => $msg,
1029 26
				'updated' => $lastModified[$id],
1030 26
				'boards' => array($id)
1031 26
			);
1032 26
		}
1033
		else
1034
		{
1035
			$board_updates[$msg . '-' . $lastModified[$id]]['boards'][] = $id;
1036
		}
1037 13
	}
1038
1039
	// Now commit the changes!
1040
	foreach ($parent_updates as $id_msg => $boards)
1041
	{
1042 26
		$db->query('', '
1043
			UPDATE {db_prefix}boards
1044
			SET id_msg_updated = {int:id_msg_updated}
1045
			WHERE id_board IN ({array_int:board_list})
1046
				AND id_msg_updated < {int:id_msg_updated}',
1047
			array(
1048
				'board_list' => $boards,
1049
				'id_msg_updated' => $id_msg,
1050
			)
1051
		);
1052
	}
1053
	foreach ($board_updates as $board_data)
1054
	{
1055 26
		$db->query('', '
1056
			UPDATE {db_prefix}boards
1057 26
			SET id_last_msg = {int:id_last_msg}, id_msg_updated = {int:id_msg_updated}
1058
			WHERE id_board IN ({array_int:board_list})',
1059
			array(
1060
				'board_list' => $board_data['boards'],
1061
				'id_last_msg' => $board_data['id'],
1062 26
				'id_msg_updated' => $board_data['updated'],
1063 26
			)
1064 26
		);
1065
	}
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
		array(
1095
			'recycle_board' => $modSettings['recycle_board'],
1096
			'is_approved' => 1,
1097
		)
1098
	);
1099
	if ($request->num_rows() === 0)
1100
	{
1101
		return array();
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']), array('<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 array(
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 its 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|mixed[]
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
				array(
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 (trim($response_prefix) != '' && $form_subject != '' && 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 array($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
			array(
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
		array(
1282
			'id_first_msg' => $topic_info['id_first_msg'],
1283
			'custom_subject' => $custom_subject,
1284
		)
1285
	);
1286
}
1287