Passed
Pull Request — development (#3829)
by Spuds
08:22
created

basicMessageInfo()   C

Complexity

Conditions 13
Paths 7

Size

Total Lines 46
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 13.33

Importance

Changes 0
Metric Value
cc 13
eloc 25
nc 7
nop 4
dl 0
loc 46
ccs 14
cts 16
cp 0.875
crap 13.33
rs 6.6166
c 0
b 0
f 0

How to fix   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 functions for dealing with messages.
5
 * Low-level functions, i.e. database operations needed to perform.
6
 * These functions (probably) do NOT make permissions checks. (they assume
7
 * those were already made).
8
 *
9
 * @package   ElkArte Forum
10
 * @copyright ElkArte Forum contributors
11
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file)
12
 *
13
 * This file contains code covered by:
14
 * copyright: 2011 Simple Machines (http://www.simplemachines.org)
15
 *
16
 * @version 2.0 Beta 1
17
 *
18
 */
19
20
use ElkArte\MessagesDelete;
21
use ElkArte\User;
22
23
/**
24
 * Get message and attachments data, for a message ID. The function returns the data in an array.
25
 *
26
 * What it does:
27
 * - 'message' => array with all message data, body, subject, etc
28
 * - 'attachment_stuff' => array with attachments ordered by attachment id as keys
29
 *
30
 * @param int $id_msg
31
 * @param int $id_topic = 0
32
 * @param int $attachment_type = 0
33
 *
34
 * @return array|bool
35
 */
36
function messageDetails($id_msg, $id_topic = 0, $attachment_type = 0)
37
{
38
	global $modSettings;
39
40
	$db = database();
41
42
	if (empty($id_msg))
43
	{
44
		return false;
45
	}
46
47
	$message_data = $db->fetchQuery('
48
		SELECT
49
			m.id_member, m.modified_time, m.modified_name, m.smileys_enabled, m.body,
50
			m.poster_name, m.poster_email, m.subject, m.icon, m.approved,
51
			COALESCE(a.size, -1) AS filesize, a.filename, a.id_attach,
52
			a.approved AS attachment_approved, t.id_member_started AS id_member_poster,
53
			m.poster_time, log.id_action
54
		FROM {db_prefix}messages AS m
55
			INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:current_topic})
56
			LEFT JOIN {db_prefix}attachments AS a ON (a.id_msg = m.id_msg AND a.attachment_type = {int:attachment_type})
57
			LEFT JOIN {db_prefix}log_actions AS log ON (m.id_topic = log.id_topic AND log.action = {string:announce_action})
58
		WHERE m.id_msg = {int:id_msg}
59
			AND m.id_topic = {int:current_topic}',
60
		[
61
			'current_topic' => $id_topic,
62
			'attachment_type' => $attachment_type,
63
			'id_msg' => $id_msg,
64
			'announce_action' => 'announce_topic',
65
		]
66
	)->fetch_all();
67
68
	// The message they were trying to edit was most likely deleted.
69
	if (empty($message_data))
70
	{
71
		return false;
72
	}
73
74
	$temp = [];
75
76
	// Attachments enabled, then return the details for each
77
	if (!empty($modSettings['attachmentEnable']))
78
	{
79
		foreach ($message_data as $attachment)
80
		{
81
			if ($attachment['filesize'] >= 0)
82
			{
83
				$temp[$attachment['id_attach']] = $attachment;
84
			}
85
		}
86
87
		ksort($temp);
88
	}
89
90
	return ['message' => $message_data[0], 'attachment_stuff' => $temp];
91
}
92
93
/**
94
 * Get some basic info of a certain message
95
 * Will use query_see_board unless $override_permissions is set to true
96
 * Will return additional topic information if $detailed is set to true
97
 * Returns an associative array of the results or false on error
98
 *
99
 * @param int $id_msg
100
 * @param bool $override_permissions
101
 * @param bool $detailed
102
 * @param bool $approved only return approved messages
103
 *
104 14
 * @return array|false array of message details or false if no message found.
105
 */
106 14
function basicMessageInfo($id_msg, $override_permissions = false, $detailed = false, $approved = true)
107
{
108 14
	global $modSettings;
109
110 2
	$db = database();
111
112
	if (empty($id_msg))
113 12
	{
114
		return false;
115
	}
116 12
117 12
	$request = $db->fetchQuery('
118 12
		SELECT
119 12
			m.id_member, m.id_topic, m.id_board, m.id_msg, m.body, m.subject,
120 12
			m.poster_name, m.poster_email, m.poster_time, m.approved' . ($detailed === false ? '' : ',
121 12
			t.id_first_msg, t.num_replies, t.unapproved_posts, t.id_last_msg, t.id_member_started, t.locked, t.approved AS topic_approved') . '
122 12
		FROM {db_prefix}messages AS m' . ($override_permissions === true ? '' : '
123
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board})') . ($detailed === false ? '' : '
124
			LEFT JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)') . '
125 12
		WHERE id_msg = {int:message}' . (empty($modSettings['postmod_active']) || allowedTo('approve_posts') || $approved === false ? '' : '
126
			AND m.approved = 1') . '
127
		LIMIT 1',
128 12
		[
129
			'message' => $id_msg,
130 12
		]
131
	);
132
	if ($request->hasResults())
133
	{
134
		$messageInfo = $request->fetch_assoc();
135
136
		$intKeys = ['id_member', 'id_topic', 'id_board', 'id_msg', 'poster_time', 'approved'];
137
		if ($detailed !== false) {
138
			$intKeys = array_merge($intKeys, ['id_first_msg', 'num_replies', 'unapproved_posts',
139
				'id_last_msg', 'id_member_started', 'locked', 'topic_approved'
140
			]);
141
		}
142
143
		foreach ($intKeys as $key) {
144
			if (isset($messageInfo[$key])) {
145
				$messageInfo[$key] = (int) $messageInfo[$key];
146
			}
147
		}
148
	}
149
	$request->free_result();
150
151
	return empty($messageInfo) ? false : $messageInfo;
152
}
153
154
/**
155
 * Get some basic info of a certain message, good to create a quote.
156
 * Very similar to many other queries, though slightly different.
157
 * Uses {query_see_board} and the 'moderate_board' permission
158
 *
159
 * @param int $id_msg
160
 * @param bool $modify
161
 *
162
 * @return array
163
 * @todo why it doesn't take into account post moderation?
164
 */
165
function quoteMessageInfo($id_msg, $modify)
166
{
167
	if (empty($id_msg))
168
	{
169
		return [];
170
	}
171
172
	$db = database();
173
174
	require_once(SUBSDIR . '/Post.subs.php');
175
176
	$moderate_boards = boardsAllowedTo('moderate_board');
177
178
	$request = $db->fetchQuery('
179
		SELECT 
180
			COALESCE(mem.real_name, m.poster_name) AS poster_name, m.poster_time, m.body, m.id_topic, m.subject,
181
			m.id_board, m.id_member, m.approved
182
		FROM {db_prefix}messages AS m
183
			INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
184
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board})
185
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
186
		WHERE m.id_msg = {int:id_msg}' . ($modify || (!empty($moderate_boards) && $moderate_boards[0] == 0) ? '' : '
187
			AND (t.locked = {int:not_locked}' . (empty($moderate_boards) ? '' : ' OR b.id_board IN ({array_int:moderation_board_list})') . ')') . '
188
		LIMIT 1',
189
		[
190
			'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...
191
			'moderation_board_list' => $moderate_boards,
192
			'id_msg' => $id_msg,
193
			'not_locked' => 0,
194
		]
195
	);
196
	if ($request->hasResults())
197
	{
198
		$quotedMessageInfo = $request->fetch_assoc();
199
		$intKeys = ['poster_time', 'id_topic', 'id_board', 'id_member', 'approved'];
200
		foreach ($intKeys as $key)
201
		{
202
			if (isset($quotedMessageInfo[$key]))
203
			{
204
				$quotedMessageInfo[$key] = (int) $quotedMessageInfo[$key];
205
			}
206
		}
207
	}
208
	$request->free_result();
209
210
	return empty($quotedMessageInfo) ? [] : $quotedMessageInfo;
211
}
212
213
/**
214
 * Checks permissions to modify a message.
215
 * This function will give a fatal error if the current user
216
 * doesn't have permissions to modify the message.
217
 *
218
 * @param int $message
219
 *
220
 * @return array|bool
221
 * @throws \ElkArte\Exceptions\Exception modify_post_time_passed
222
 */
223
function checkMessagePermissions($message)
224
{
225
	global $modSettings, $context;
226
227
	if ($message['id_member'] == User::$info->id && !allowedTo('modify_any'))
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...
228
	{
229
		// Give an extra five minutes over the disable time threshold, so they can type - assuming the post is public.
230
		if ($message['approved'] && !empty($modSettings['edit_disable_time']) && $message['poster_time'] + ($modSettings['edit_disable_time'] + 5) * 60 < time())
231
		{
232
			throw new \ElkArte\Exceptions\Exception('modify_post_time_passed', false);
233
		}
234
235
		if ($message['id_member_poster'] == User::$info->id && !allowedTo('modify_own'))
236
		{
237
			isAllowedTo('modify_replies');
238
		}
239
		else
240
		{
241
			isAllowedTo('modify_own');
242
		}
243
	}
244
	elseif ($message['id_member_poster'] == User::$info->id && !allowedTo('modify_any'))
245
	{
246
		isAllowedTo('modify_replies');
247
	}
248
	else
249
	{
250
		isAllowedTo('modify_any');
251
	}
252
253
	if ($context['can_announce'] && !empty($message['id_action']))
254
	{
255
		return ['topic_already_announced'];
256
	}
257
258
	return false;
259
}
260
261
/**
262
 * Prepare context for a message.
263
 *
264
 * What it does:
265
 * - Loads data from messageDetails into $context array
266
 *
267
 * @param array $message the message array
268
 */
269
function prepareMessageContext($message)
270
{
271
	global $context, $txt;
272
273
	// Load up 'em attachments!
274
	foreach ($message['attachment_stuff'] as $attachment)
275
	{
276
		$context['attachments']['current'][] = [
277
			'name' => htmlspecialchars($attachment['filename'], ENT_COMPAT, 'UTF-8'),
278
			'size' => $attachment['filesize'],
279
			'id' => $attachment['id_attach'],
280
			'approved' => $attachment['attachment_approved'],
281
		];
282
	}
283
284
	// Allow moderators to change names....
285
	if (allowedTo('moderate_forum') && empty($message['message']['id_member']))
286
	{
287
		$context['name'] = htmlspecialchars($message['message']['poster_name'], ENT_COMPAT, 'UTF-8');
288
		$context['email'] = htmlspecialchars($message['message']['poster_email'], ENT_COMPAT, 'UTF-8');
289
	}
290
291
	// When was it last modified?
292
	if (!empty($message['message']['modified_time']))
293
	{
294
		$context['last_modified'] = standardTime($message['message']['modified_time']);
295
		$context['last_modified_text'] = sprintf($txt['last_edit_by'], $context['last_modified'], $message['message']['modified_name']);
296
	}
297
298
	// Show an "approve" box if the user can approve it, and the message isn't approved.
299
	if (!$message['message']['approved'] && !$context['show_approval'])
300
	{
301
		$context['show_approval'] = allowedTo('approve_posts');
302
	}
303
}
304
305
/**
306
 * This function removes all the messages of a certain user that are *not*
307
 * first messages of a topic
308
 *
309
 * @param int $memID The member id
310
 */
311
function removeNonTopicMessages($memID)
312
{
313
	$db = database();
314
315
	$db->fetchQuery('
316
		SELECT 
317
			m.id_msg
318
		FROM {db_prefix}messages AS m
319
			INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic
320
				AND t.id_first_msg != m.id_msg)
321
		WHERE m.id_member = {int:selected_member}',
322
		[
323
			'selected_member' => $memID,
324
		]
325
	)->fetch_callback(
326
		function ($row) {
327
			// This could take a while... but ya know it's gonna be worth it in the end.
328
			detectServer()->setTimeLimit(300);
329
			removeMessage($row['id_msg']);
330
		}
331
	);
332
}
333
334
/**
335
 * Remove a specific message.
336
 * !! This includes permission checks.
337
 *
338
 * - normally, local and global should be the localCookies and globalCookies settings, respectively.
339
 * - uses boardurl to determine these two things.
340
 *
341
 * @param int $message The message id
342
 * @param bool $decreasePostCount if true users' post count will be reduced
343
 */
344
function removeMessage($message, $decreasePostCount = true)
345
{
346
	global $modSettings;
347
348
	$remover = new MessagesDelete($modSettings['recycle_enable'], $modSettings['recycle_board']);
349
	$remover->removeMessage($message, $decreasePostCount, true);
350
}
351
352
/**
353
 * This function deals with the topic associated to a message.
354
 * It allows retrieving or updating the topic to which the message belongs.
355
 *
356
 * If $topicID is not passed, the current topic ID of the message is returned.
357
 * If $topicID is passed, the message is updated to point to the new topic.
358
 *
359
 * @param int $msg_id message ID
360
 * @param int|null $topicID = null topic ID, if null is passed the ID of the topic is retrieved and returned
361
 * @return int|false int topic ID if any, or false
362
 */
363
function associatedTopic($msg_id, $topicID = null)
364
{
365
	$db = database();
366
367
	if ($topicID === null)
368
	{
369
		$request = $db->query('', '
370
			SELECT 
371
				id_topic
372
			FROM {db_prefix}messages
373
			WHERE id_msg = {int:msg}',
374
			[
375
				'msg' => $msg_id,
376
			]
377
		);
378
		if ($request->num_rows() !== 1)
379
		{
380
			$topic = false;
381
		}
382
		else
383 2
		{
384
			list ($topic) = $request->fetch_row();
385
		}
386 2
		$request->free_result();
387
388
		return $topic;
389
	}
390
391
	$db->query('', '
392 2
		UPDATE {db_prefix}messages
393
		SET 
394
			id_topic = {int:topic}
395 2
		WHERE id_msg = {int:msg}',
396
		[
397
			'msg' => $msg_id,
398
			'topic' => $topicID,
399
		]
400
	);
401
}
402
403
/**
404
 * Small function that simply verifies if the current
405
 * user can access a specific message
406
 *
407
 * @param int $id_msg a message id
408
 * @param bool $check_approval if true messages are checked for approval (default true)
409
 * @return bool
410
 */
411
function canAccessMessage($id_msg, $check_approval = true)
412
{
413
	$message_info = basicMessageInfo($id_msg);
414
415
	// Do we even have a message to speak of?
416
	if (empty($message_info))
417
	{
418
		return false;
419
	}
420
421
	// Check for approval status?
422
	if ($check_approval)
423
	{
424
		// The user can access this message if it's approved or they're owner
425
		return (!empty($message_info['approved']) || $message_info['id_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...
426
	}
427
428
	// Otherwise, nope.
429
	return false;
430
}
431
432
/**
433
 * Advance message pointer in a topic.
434
 * (in either direction)
435
 * This function is used by previousMessage() and nextMessage().
436
 * The boolean parameter $next determines the direction.
437
 *
438
 * @param int $id_msg origin message id
439
 * @param int $id_topic topic
440
 * @param bool $next = true if true, it increases the pointer, otherwise it decreases it
441
 *
442
 * @return int
443
 */
444
function messagePointer($id_msg, $id_topic, $next = true)
445
{
446
	$db = database();
447
448
	$result = $db->query('', '
449
		SELECT ' . ($next ? 'MIN(id_msg)' : 'MAX(id_msg)') . '
450
		FROM {db_prefix}messages
451
		WHERE id_topic = {int:current_topic}
452
			AND id_msg {raw:strictly} {int:topic_msg_id}',
453
		[
454
			'current_topic' => $id_topic,
455
			'topic_msg_id' => $id_msg,
456
			'strictly' => $next ? '>' : '<'
457
		]
458
	);
459
	list ($msg) = $result->fetch_row();
460
	$result->free_result();
461
462
	return $msg;
463
}
464
465
/**
466
 * Get previous message from where we were in the topic.
467
 *
468
 * @param int $id_msg
469
 * @param int $id_topic
470
 *
471
 * @return int
472
 */
473
function previousMessage($id_msg, $id_topic)
474
{
475
	return messagePointer($id_msg, $id_topic, false);
476
}
477
478
/**
479
 * Get next message from where we were in the topic.
480
 *
481
 * @param int $id_msg
482
 * @param int $id_topic
483
 *
484
 * @return int
485
 */
486
function nextMessage($id_msg, $id_topic)
487
{
488
	return messagePointer($id_msg, $id_topic);
489
}
490
491
/**
492
 * Retrieve the message id/s at a certain position in a topic
493
 *
494
 * @param int $start the offset of the message/s
495
 * @param int $id_topic the id of the topic
496
 * @param array $params an (optional) array of params, includes:
497
 *      - 'not_in' => array - of messages to exclude
498
 *      - 'include' => array - of messages to explicitly include
499
 *      - 'only_approved' => true/false - include or exclude the unapproved messages
500
 *      - 'limit' => mixed - the number of values to return (if false, no limits applied)
501
 *
502
 * @return array
503
 * @todo very similar to selectMessages in Topics.subs.php
504
 */
505
function messageAt($start, $id_topic, $params = [])
506
{
507
	$db = database();
508
509
	$params = array_merge(
510
	// Defaults
511
		[
512
			'not_in' => false,
513
			'include' => false,
514
			'only_approved' => false,
515
			'limit' => 1,
516
		],
517
		// Passed arguments
518
		$params,
519
		// Others
520
		[
521
			'current_topic' => $id_topic,
522
			'start' => $start,
523
			'is_approved' => 1,
524
		]
525
	);
526
527
	$msg = [];
528
	$db->fetchQuery('
529
		SELECT 
530
			id_msg
531
		FROM {db_prefix}messages
532
		WHERE id_topic = {int:current_topic}' . (!$params['include'] ? '' : '
533
			AND id_msg IN ({array_int:include})') . (!$params['not_in'] ? '' : '
534 2
			AND id_msg NOT IN ({array_int:not_in})') . (!$params['only_approved'] ? '' : '
535
			AND approved = {int:is_approved}') . '
536 2
		ORDER BY id_msg DESC' . ($params['limit'] === false ? '' : '
537
		LIMIT {int:limit} OFFSET {int:start} '),
538
		$params
539
	)->fetch_callback(
540
		function ($row) use (&$msg) {
541
			$msg[] = $row['id_msg'];
542
		}
543
	);
544
545 2
	return $msg;
546 2
}
547 2
548 2
/**
549
 * Finds an open report for a certain message if it exists and increase the
550
 * number of reports for that message, otherwise it creates one
551 2
 *
552
 * @param array $message array of several message details (id_msg, id_topic, etc.)
553
 * @param string $poster_comment the comment made by the reporter
554
 *
555 2
 * @return bool
556
 */
557 2
function recordReport($message, $poster_comment)
558
{
559
	$db = database();
560
561
	$request = $db->query('', '
562
		SELECT 
563 2
			id_report, ignore_all
564
		FROM {db_prefix}log_reported
565
		WHERE id_msg = {int:id_msg}
566
			AND type = {string:type}
567
			AND (closed = {int:not_closed} OR ignore_all = {int:ignored})
568
		ORDER BY ignore_all DESC',
569
		[
570
			'id_msg' => $message['id_msg'],
571
			'type' => $message['type'] ?? 'msg',
572
			'not_closed' => 0,
573
			'ignored' => 1,
574
		]
575
	);
576
	if ($request->num_rows() !== 0)
577
	{
578
		list ($id_report, $ignore_all) = $request->fetch_row();
579 2
	}
580
	$request->free_result();
581 2
582
	if (!empty($ignore_all))
583
	{
584 2
		return false;
585 2
	}
586
587 2
	// Already reported? My god, we could be dealing with a real rogue here...
588
	if (!empty($id_report))
589
	{
590
		$db->query('', '
591
			UPDATE {db_prefix}log_reported
592
			SET 
593 2
				num_reports = num_reports + 1, time_updated = {int:current_time}
594 2
			WHERE id_report = {int:id_report}',
595 2
			[
596
				'current_time' => time(),
597 2
				'id_report' => $id_report,
598
			]
599 2
		);
600
	}
601
	// Otherwise, we shall make one!
602
	else
603 2
	{
604
		if (empty($message['real_name']))
605 2
		{
606 2
			$message['real_name'] = $message['poster_name'];
607
		}
608 2
609
		$db->insert('',
610
			'{db_prefix}log_reported',
611
			[
612 2
				'id_msg' => 'int', 'id_topic' => 'int', 'id_board' => 'int', 'id_member' => 'int', 'membername' => 'string',
613 2
				'subject' => 'string', 'body' => 'string', 'time_started' => 'int', 'time_updated' => 'int',
614
				'num_reports' => 'int', 'closed' => 'int',
615 2
				'type' => 'string-5', 'time_message' => 'int'
616
			],
617
			[
618
				$message['id_msg'], $message['id_topic'], $message['id_board'], $message['id_poster'], $message['real_name'],
619 2
				$message['subject'], $message['body'], time(), time(), 1, 0,
620
				$message['type'], $message['time_message'] ?? 0
621
			],
622
			['id_report']
623
		);
624
		$id_report = $db->insert_id('{db_prefix}log_reported');
625
	}
626
627
	// Now just add our report...
628
	if (!empty($id_report))
629
	{
630
		$db->insert('',
631
			'{db_prefix}log_reported_comments',
632
			[
633
				'id_report' => 'int', 'id_member' => 'int', 'membername' => 'string', 'email_address' => 'string',
634
				'member_ip' => 'string', 'comment' => 'string', 'time_sent' => 'int',
635
			],
636
			[
637
				$id_report, User::$info->id, User::$info->name, User::$info->email,
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug Best Practice introduced by
The property email does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug Best Practice introduced by
The property name does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
638
				User::$info->ip, $poster_comment, time(),
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...
639
			],
640
			['id_comment']
641
		);
642
	}
643
644
	return $id_report;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $id_report does not seem to be defined for all execution paths leading up to this point.
Loading history...
645
}
646
647
/**
648
 * Count the new posts for a specific topic
649
 *
650
 * @param int $topic
651
 * @param array $topicinfo
652
 * @param int $timestamp
653
 * @return int
654
 */
655
function countNewPosts($topic, $topicinfo, $timestamp)
656
{
657
	global $modSettings;
658
659
	$db = database();
660
661
	// Find the number of messages posted before said time...
662
	$request = $db->query('', '
663
		SELECT 
664
			COUNT(*)
665
		FROM {db_prefix}messages
666
		WHERE poster_time < {int:timestamp}
667
			AND id_topic = {int:current_topic}' . ($modSettings['postmod_active'] && $topicinfo['unapproved_posts'] && !allowedTo('approve_posts') ? '
668
			AND (approved = {int:is_approved}' . (User::$info->is_guest ? '' : ' OR id_member = {int:current_member}') . ')' : ''),
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...
669
		[
670
			'current_topic' => $topic,
671 2
			'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...
672
			'is_approved' => 1,
673 2
			'timestamp' => $timestamp,
674
		]
675
	);
676
	list ($start) = $request->fetch_row();
677 2
	$request->free_result();
678 2
679 2
	return $start;
680
}
681 2
682
/**
683 2
 * Loads the details from a message
684
 *
685 1
 * @param string[] $msg_selects
686
 * @param string[] $msg_tables
687
 * @param array $msg_parameters
688
 * @param array $optional
689
 *
690
 * @return \ElkArte\Database\AbstractResult A request object
691
 */
692
function loadMessageRequest($msg_selects, $msg_tables, $msg_parameters, $optional = [])
693
{
694
	$db = database();
695
696
	return $db->query('', '
697
		SELECT
698
			m.id_msg, m.icon, m.subject, m.poster_time, m.poster_ip, m.id_member,
699
			m.modified_time, m.modified_name, m.body, m.smileys_enabled,
700
			m.poster_name, m.poster_email, m.approved' . (isset($msg_parameters['new_from']) ? ',
701
			m.id_msg_modified < {int:new_from} AS is_read' : '') . '
702
			' . (!empty($msg_selects) ? ',' . implode(',', $msg_selects) : '') . '
703
		FROM {db_prefix}messages AS m
704
			' . (!empty($msg_tables) ? implode("\n\t\t\t", $msg_tables) : '') . '
705
		WHERE m.id_msg IN ({array_int:message_list})
706
			' . (!empty($optional['additional_conditions']) ? $optional['additional_conditions'] : '') . '
707
		ORDER BY m.id_msg',
708
		$msg_parameters
709
	);
710
}
711
712
/**
713
 * Returns the details from a message or several messages
714
 * Uses loadMessageRequest to query the database
715
 *
716
 * @param string[] $msg_selects
717
 * @param string[] $msg_tables
718
 * @param array $msg_parameters
719
 * @param array $optional
720
 * @return array
721
 */
722
function loadMessageDetails($msg_selects, $msg_tables, $msg_parameters, $optional = [])
723
{
724
	if (!is_array($msg_parameters['message_list']))
725
	{
726
		$single = true;
727
		$msg_parameters['message_list'] = [$msg_parameters['message_list']];
728
	}
729
	else
730
	{
731
		$single = false;
732
	}
733
734
	$request = loadMessageRequest($msg_selects, $msg_tables, $msg_parameters, $optional);
735
736
	$return = [];
737
	while (($row = $request->fetch_assoc()))
738
	{
739
		$return[] = $row;
740
	}
741
	$request->free_result();
742
743
	if ($single)
744
	{
745
		return $return[0];
746
	}
747
748
	return $return;
749
}
750
751
/**
752
 * Checks, which messages can be removed from a certain member.
753
 *
754
 * @param int $topic
755
 * @param int[] $messages
756
 * @param bool $allowed_all
757
 * @return array
758
 */
759
function determineRemovableMessages($topic, $messages, $allowed_all)
760
{
761
	$db = database();
762
763
	// Allowed to remove which messages?
764
	$messages_list = [];
765
	$db->fetchQuery('
766
		SELECT 
767
			id_msg, subject, id_member, poster_time
768
		FROM {db_prefix}messages
769
		WHERE id_msg IN ({array_int:message_list})
770
			AND id_topic = {int:current_topic}' . (!$allowed_all ? '
771
			AND id_member = {int:current_member}' : '') . '
772
		LIMIT ' . count($messages),
773
		[
774
			'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...
775
			'current_topic' => $topic,
776
			'message_list' => $messages,
777
		]
778
	)->fetch_callback(
779
		function ($row) use (&$messages_list, $allowed_all) {
780
			global $modSettings;
781
782
			if (!$allowed_all && !empty($modSettings['edit_disable_time']) && $row['poster_time'] + $modSettings['edit_disable_time'] * 60 < time())
783
			{
784
				return;
785
			}
786
787
			$messages_list[$row['id_msg']] = [$row['subject'], $row['id_member']];
788
		}
789
	);
790
791
	return $messages_list;
792
}
793
794
/**
795
 * Returns the number of messages that are being split to a new topic
796
 *
797
 * @param int $topic
798
 * @param bool $include_unapproved
799
 * @param int[] $selection
800
 *
801
 * @return array
802
 */
803
function countSplitMessages($topic, $include_unapproved, $selection = [])
804
{
805
	$db = database();
806
807
	$return = ['not_selected' => 0, 'selected' => 0];
808
	$db->fetchQuery('
809
		SELECT ' . (empty($selection) ? '0' : 'm.id_msg IN ({array_int:split_msgs})') . ' AS is_selected, COUNT(*) AS num_messages
810
		FROM {db_prefix}messages AS m
811
		WHERE m.id_topic = {int:current_topic}' . ($include_unapproved ? '' : '
812
			AND approved = {int:is_approved}') . (empty($selection) ? '' : '
813
		GROUP BY is_selected'),
814
		[
815
			'current_topic' => $topic,
816
			'split_msgs' => $selection,
817
			'is_approved' => 1,
818
		]
819
	)->fetch_callback(
820
		function ($row) use (&$return) {
821
			$return[empty($row['is_selected']) || $row['is_selected'] == 'f' ? 'not_selected' : 'selected'] = $row['num_messages'];
822
		}
823
	);
824
825
	return $return;
826
}
827
828
/**
829
 * Returns an email (and few other things) associated with a message,
830
 * either the member's email or the poster_email (for example in case of guests)
831
 *
832
 * @param int $id_msg the id of a message
833
 * @return array
834
 * @todo very similar to posterDetails
835
 *
836
 */
837
function mailFromMessage($id_msg)
838
{
839
	$db = database();
840
841
	$request = $db->query('', '
842
		SELECT 
843
			COALESCE(mem.email_address, m.poster_email) AS email_address, COALESCE(mem.real_name, m.poster_name) AS real_name, COALESCE(mem.id_member, 0) AS id_member
844
		FROM {db_prefix}messages AS m
845
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
846
		WHERE m.id_msg = {int:id_msg}',
847
		[
848
			'id_msg' => $id_msg,
849
		]
850
	);
851 26
	$row = $request->fetch_assoc();
852
	$request->free_result();
853 26
854
	return $row;
855 26
}
856
857 26
/**
858
 * This function changes the total number of messages,
859
 * and the highest message id by id_msg - which can be
860
 * parameters 1 and 2, respectively.
861
 *
862 12
 * @param bool|null $increment = null If true and $max_msg_id != null, then increment the total messages by one, otherwise recount all messages and get the max message id
863
 * @param int|null $max_msg_id = null, Only used if $increment === true
864
 */
865
function updateMessageStats($increment = null, $max_msg_id = null)
866 12
{
867 12
	global $modSettings;
868
869 12
	$db = database();
870 12
871
	if ($increment === true && $max_msg_id !== null)
872
	{
873 12
		updateSettings(['totalMessages' => true, 'maxMsgID' => $max_msg_id], true);
874 12
	}
875
	else
876 12
	{
877 12
		// SUM and MAX on a smaller table is better for InnoDB tables.
878 12
		$request = $db->query('', '
879
			SELECT 
880
				SUM(num_posts + unapproved_posts) AS total_messages, MAX(id_last_msg) AS max_msg_id
881 26
			FROM {db_prefix}boards
882
			WHERE redirect = {string:blank_redirect}' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? '
883
				AND id_board != {int:recycle_board}' : ''),
884
			[
885
				'recycle_board' => $modSettings['recycle_board'] ?? 0,
886
				'blank_redirect' => '',
887
			]
888
		);
889
		$row = $request->fetch_assoc();
890
		$request->free_result();
891
892
		updateSettings([
893
			'totalMessages' => $row['total_messages'] ?? 0,
894 26
			'maxMsgID' => $row['max_msg_id'] ?? 0
895
		]);
896
	}
897 26
}
898
899
/**
900
 * This function updates the log_search_subjects in the event of a topic being
901 26
 * moved, removed or split. It is being sent the topic id, and optionally
902
 * the new subject.
903
 *
904
 * @param int $id_topic
905
 * @param string|null $subject
906 26
 */
907
function updateSubjectStats($id_topic, $subject = null)
908 26
{
909 26
	$db = database();
910
911 26
	// Remove the previous subject (if any).
912 26
	$db->query('', '
913
		DELETE FROM {db_prefix}log_search_subjects
914 26
		WHERE id_topic = {int:id_topic}',
915
		[
916
			'id_topic' => (int) $id_topic,
917 26
		]
918
	);
919 26
920 26
	// Insert the new subject.
921 26
	if ($subject !== null)
922 13
	{
923 26
		$id_topic = (int) $id_topic;
924
		$subject_words = text2words($subject);
925
926
		$inserts = [];
927 26
		foreach ($subject_words as $word)
928
		{
929
			$inserts[] = [$word, $id_topic];
930
		}
931
932
		if (!empty($inserts))
933
		{
934
			$db->insert('ignore',
935
				'{db_prefix}log_search_subjects',
936
				['word' => 'string', 'id_topic' => 'int'],
937
				$inserts,
938
				['word', 'id_topic']
939
			);
940
		}
941
	}
942
}
943