Passed
Push — development ( b889cb...c0e368 )
by Spuds
01:06 queued 27s
created

getMentionTypes()   B

Complexity

Conditions 7
Paths 13

Size

Total Lines 40
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 7
eloc 17
c 2
b 0
f 0
nc 13
nop 2
dl 0
loc 40
ccs 0
cts 0
cp 0
crap 56
rs 8.8333
1
<?php
2
3
/**
4
 * Functions that deal with the database work involved with mentions
5
 *
6
 * @package   ElkArte Forum
7
 * @copyright ElkArte Forum contributors
8
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file)
9
 *
10
 * @version 2.0 dev
11
 *
12
 */
13
14
use ElkArte\User;
15
16
/**
17
 * Counts the number of mentions for a user.
18
 *
19
 * @param bool $all Specifies whether to count all mentions or only unread.
20
 * @param string $type Specifies the type of mention to count. Empty string to count all types.
21
 * @param int|null $id_member Specifies the ID of the member. If null, the current user's ID will be used.
22
 *
23
 * @return int|array The total count of mentions if $type is empty, otherwise an array with counts for each type.
24
 */
25
function countUserMentions($all = false, $type = '', $id_member = null)
26
{
27
	static $counts;
28
29
	$db = database();
30
	$id_member = $id_member === null ? User::$info->id : (int) $id_member;
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...
31 4
32
	if (isset($counts[$id_member][$type]))
33 4
	{
34 4
		return $counts[$id_member][$type];
35
	}
36 4
37
	$allTypes = getMentionTypes($id_member, $all === true ? 'system' : 'user');
38 2
	foreach ($allTypes as $thisType)
39
	{
40
		$counts[$id_member][$thisType] = 0;
41 2
	}
42
	$counts[$id_member]['total'] = 0;
43
44
	$db->fetchQuery('
45
		SELECT 
46
			mention_type, COUNT(*) AS cnt
47 2
		FROM {db_prefix}log_mentions as mtn
48 2
		WHERE mtn.id_member = {int:current_user}
49
			AND mtn.is_accessible = {int:is_accessible}
50
			AND mtn.status IN ({array_int:status})
51 2
			AND mtn.mention_type IN ({array_string:all_type})
52 2
		GROUP BY mtn.mention_type',
53 2
		[
54 2
			'current_user' => $id_member,
55
			'status' => $all ? [0, 1] : [0],
56
			'is_accessible' => 1,
57 2
			'all_type' => empty($allTypes) ? [$type] : $allTypes,
58 2
		]
59
	)->fetch_callback(function ($row) use (&$counts, $id_member) {
60
		$counts[$id_member][$row['mention_type']] = (int) $row['cnt'];
61 2
		$counts[$id_member]['total'] += $row['cnt'];
62
	});
63
64
	// Counts as maintenance! :P
65
	if ($all === false)
66
	{
67 2
		require_once(SUBSDIR . '/Members.subs.php');
68
		updateMemberData($id_member, ['mentions' => $counts[$id_member]['total']]);
69
	}
70
71
	return empty($type) ? $counts[$id_member]['total'] : $counts[$id_member][$type] ?? 0;
72
}
73
74
/**
75
 * Retrieve all the info to render the mentions page for the current user
76
 * callback for createList in action_list of \ElkArte\Controller\Mentions
77
 *
78
 * @param int $start Query starts sending results from here
79
 * @param int $limit Number of mentions returned
80
 * @param string $sort Sorting
81
 * @param bool $all if show all mentions or only unread ones
82
 * @param string[]|string $type : the type of the mention can be a string or an array of strings.
83
 *
84
 * @return array
85
 * @package Mentions
86
 *
87 2
 */
88
function getUserMentions($start, $limit, $sort, $all = false, $type = '')
89 2
{
90
	global $txt;
91 2
92
	$db = database();
93
94
	return $db->fetchQuery('
95
		SELECT
96
			mtn.id_mention, mtn.id_target, mtn.id_member_from, mtn.log_time, mtn.mention_type, mtn.status,
97
			m.subject, m.id_topic, m.id_board,
98
			COALESCE(mem.real_name, {string:guest_text}) as mentioner, mem.avatar, mem.email_address,
99
			COALESCE(a.id_attach, 0) AS id_attach, a.filename, a.attachment_type
100
		FROM {db_prefix}log_mentions AS mtn
101
			LEFT JOIN {db_prefix}messages AS m ON (mtn.id_target = m.id_msg)
102
			LEFT JOIN {db_prefix}members AS mem ON (mtn.id_member_from = mem.id_member)
103 2
			LEFT JOIN {db_prefix}attachments AS a ON (a.id_member = mem.id_member)
104 2
		WHERE mtn.id_member = {int:current_user}
105 2
			AND mtn.is_accessible = {int:is_accessible}
106
			AND mtn.status IN ({array_int:status})' . (empty($type) ? '' : (is_array($type) ? '
107
			AND mtn.mention_type IN ({array_string:current_type})' : '
108
			AND mtn.mention_type = {string:current_type}')) . '
109 2
		ORDER BY {raw:sort}
110 2
		LIMIT {int:limit} OFFSET {int:start} ',
111 2
		array(
112 2
			'current_user' => 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...
113 2
			'current_type' => $type,
114 2
			'status' => $all ? array(0, 1) : array(0),
115 2
			'guest_text' => $txt['guest'],
116 2
			'is_accessible' => 1,
117
			'start' => $start,
118 2
			'limit' => $limit,
119
			'sort' => $sort,
120 2
		)
121
	)->fetch_callback(
122 2
		function ($row) {
123 2
			$row['avatar'] = determineAvatar($row);
124
125
			return $row;
126
		}
127
	);
128
}
129
130
/**
131
 * Completely remove from the database a set of mentions.
132
 *
133
 * Doesn't check permissions, access, anything. It just deletes everything.
134
 *
135
 * @param int[] $id_mentions the mention ids
136
 *
137
 * @return bool
138
 * @package Mentions
139
 *
140
 */
141
function removeMentions($id_mentions)
142
{
143
	$db = database();
144
145
	$request = $db->query('', '
146
		DELETE FROM {db_prefix}log_mentions
147
		WHERE id_mention IN ({array_int:id_mentions})',
148
		array(
149
			'id_mentions' => $id_mentions,
150
		)
151
	);
152
	$success = $request->affected_rows() !== 0;
153
154
	// Update the top level mentions count
155
	if ($success)
156
	{
157
		updateMentionMenuCount(null, 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...
158
	}
159
160
	return $success;
161
}
162
163
/**
164
 * Toggles a mention on/off
165
 *
166
 * - This is used to turn mentions on when a message is approved
167
 *
168
 * @param int[] $msgs array of messages that you want to toggle
169
 * @param bool $approved direction of the toggle read / unread
170
 * @package Mentions
171
 */
172
function toggleMentionsApproval($msgs, $approved)
173
{
174
	$db = database();
175
176
	$db->query('', '
177
		UPDATE {db_prefix}log_mentions
178
		SET 
179
			status = {int:status}
180
		WHERE id_target IN ({array_int:messages})',
181
		array(
182
			'messages' => $msgs,
183
			'status' => $approved ? 0 : 3,
184
		)
185
	);
186
187
	// Update the mentions menu count for the members that have this message
188
	$status = $approved ? 0 : 3;
189
	$db->fetchQuery('
190
		SELECT id_member, status
191
		FROM {db_prefix}log_mentions
192
		WHERE id_target IN ({array_int:messages})',
193
		array(
194
			'messages' => $msgs,
195
		)
196
	)->fetch_callback(
197
		function ($row) use ($status) {
198
			updateMentionMenuCount($status, $row['id_member']);
199
		}
200
	);
201
}
202
203
/**
204
 * Toggles a mention visibility on/off
205
 *
206
 * - if off is restored to visible,
207
 * - if on is switched to invisible for all the users
208
 *
209
 * @param string $type type of the mention that you want to toggle
210
 * @param bool $enable if true enables the mentions, otherwise disables them
211
 * @package Mentions
212
 */
213
function toggleMentionsVisibility($type, $enable)
214
{
215
	$db = database();
216
217
	$db->query('', '
218
		UPDATE {db_prefix}log_mentions
219
		SET
220
			status = status ' . ($enable ? '-' : '+') . ' {int:toggle}
221
		WHERE mention_type = {string:type}
222
			AND status ' . ($enable ? '>=' : '<') . ' {int:toggle}
223
			AND is_accessible = 1',
224
		array(
225
			'type' => $type,
226
			'toggle' => 10,
227
		)
228
	);
229
230
	$db->query('', '
231
		UPDATE {db_prefix}log_mentions
232
		SET
233
			status = status ' . ($enable ? '+' : '-') . ' {int:toggle}
234
		WHERE mention_type = {string:type}
235
			AND status ' . ($enable ? '<' : '>=') . ' 0
236
			AND is_accessible = 0',
237
		array(
238
			'type' => $type,
239
			'toggle' => 10,
240
		)
241
	);
242
}
243
244
/**
245
 * Toggles a bunch of mentions accessibility on/off
246
 *
247
 * @param int[] $mentions an array of mention id
248
 * @param bool $access if true make the mentions accessible (if visible and other things), otherwise marks them as inaccessible
249
 * @package Mentions
250
 */
251
function toggleMentionsAccessibility($mentions, $access)
252
{
253
	$db = database();
254
255
	$db->query('', '
256
		UPDATE {db_prefix}log_mentions
257
		SET
258
			is_accessible = CASE WHEN is_accessible = 1 THEN 0 ELSE 1 END
259
		WHERE id_mention IN ({array_int:mentions})
260
			AND is_accessible ' . ($access ? '=' : '!=') . ' 0',
261
		array(
262
			'mentions' => $mentions,
263
		)
264
	);
265
}
266
267
/**
268
 * To validate access to read/unread/delete mentions
269
 *
270
 * - Called from the validation class via Mentioning.php
271
 *
272
 * @param string $field
273
 * @param array $input
274
 * @param string|null $validation_parameters
275
 *
276
 * @return array|void
277
 * @package Mentions
278
 *
279
 */
280
function validate_own_mention($field, $input, $validation_parameters = null)
281
{
282
	if (!isset($input[$field]))
283 2
	{
284
		return;
285 2
	}
286
287
	if (!findMemberMention($input[$field], 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...
288
	{
289
		return array(
290
			'field' => $field,
291
			'input' => $input[$field],
292
			'function' => __FUNCTION__,
293
			'param' => $validation_parameters
294
		);
295
	}
296
}
297
298
/**
299
 * Provided a mentions id and a member id, checks if the mentions belongs to that user
300
 *
301
 * @param int $id_mention the id of an existing mention
302
 * @param int $id_member id of a member
303
 * @return bool true if the mention belongs to the member, false otherwise
304
 * @package Mentions
305
 */
306
function findMemberMention($id_mention, $id_member)
307
{
308
	$db = database();
309
310 2
	$request = $db->query('', '
311
		SELECT 
312 2
			id_mention
313
		FROM {db_prefix}log_mentions
314
		WHERE id_mention = {int:id_mention}
315
			AND id_member = {int:id_member}
316
		LIMIT 1',
317
		array(
318
			'id_mention' => $id_mention,
319
			'id_member' => $id_member,
320 2
		)
321 2
	);
322
	$return = $request->num_rows();
323
	$request->free_result();
324 2
325 2
	return !empty($return);
326
}
327 2
328
/**
329
 * Updates the mention count as a result of an action, read, new, delete, etc
330
 *
331
 * @param int|null $status
332
 * @param int $member_id
333
 * @package Mentions
334
 */
335
function updateMentionMenuCount($status, $member_id)
336
{
337
	require_once(SUBSDIR . '/Members.subs.php');
338
339
	// If its new add to our menu count
340
	if ($status === 0)
341
	{
342
		updateMemberData($member_id, array('mentions' => '+'));
343
	}
344
	// Mark as read we decrease the count
345
	elseif ($status === 1)
346
	{
347
		updateMemberData($member_id, array('mentions' => '-'));
348
	}
349
	// Deleting or un-approving may have been read or not, so a count is required
350
	else
351
	{
352
		countUserMentions(false, '', $member_id);
353
	}
354
}
355
356
/**
357
 * Retrieves the time the last notification of a certain member was added.
358
 *
359
 * @param int $id_member
360
 * @return int A timestamp (log_time)
361
 * @package Mentions
362
 */
363
function getTimeLastMention($id_member)
364
{
365
	$db = database();
366
367
	$request = $db->fetchQuery('
368
		SELECT 
369
			log_time
370
		FROM {db_prefix}log_mentions
371
		WHERE status = {int:status}
372
			AND id_member = {int:member}
373
		ORDER BY id_mention DESC
374
		LIMIT 1',
375
		array(
376
			'status' => 0,
377
			'member' => $id_member
378
		)
379
	);
380
	list ($log_time) = $request->fetch_row();
381
	$request->free_result();
382
383
	return empty($log_time) ? 0 : $log_time;
384
}
385
386
/**
387
 * Counts all the notifications received by a certain member after a certain time.
388
 *
389
 * @param int $id_member
390
 * @param int $timestamp
391
 * @return int Number of new mentions
392
 * @package Mentions
393
 */
394
function getNewMentions($id_member, $timestamp)
395
{
396
	$db = database();
397
398
	if (empty($timestamp))
399
	{
400
		$result = $db->fetchQuery('
401
			SELECT 
402
				COUNT(*) AS c
403
			FROM {db_prefix}log_mentions
404
			WHERE status = {int:status}
405
				AND id_member = {int:member}
406
				AND is_accessible = {int:has_access}',
407
			array(
408
				'status' => 0,
409
				'has_access' => 1,
410
				'member' => $id_member
411
			)
412
		)->fetch_assoc();
413
	}
414
	else
415
	{
416
		$result = $db->fetchQuery('
417
			SELECT 
418
				COUNT(*) AS c
419
			FROM {db_prefix}log_mentions
420
			WHERE status = {int:status}
421
				AND log_time > {int:last_seen}
422
				AND id_member = {int:member}
423
				AND is_accessible = {int:has_access}',
424
			array(
425
				'status' => 0,
426
				'has_access' => 1,
427
				'last_seen' => $timestamp,
428
				'member' => $id_member
429
			)
430
		)->fetch_assoc();
431
	}
432
433
	return $result['c'];
434
}
435
436
/**
437
 * Get the available mention types for a user.
438
 *
439
 * @param int|null $user The user ID. If null, User::$info->id will be used.
440
 * @param string $type The type of mentions.  user will return only those that the user has enabled and set
441
 * as notification.  If not user, returns the system level enabled mentions.
442
 *
443
 * By default, will filter out notification types with a method set to none, e.g. the user had disable that
444
 * type of mention.  Use type=all to return everything, or type=notification to return only those
445
 * that they want on-site notifications.
446
 *
447
 * @return array The available mention types.
448
 */
449
function getMentionTypes($user, $type = 'user')
450
{
451
	require_once(SUBSDIR . '/Notification.subs.php');
452
453
	$user = $user ?? 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...
454
455
	$enabled = getEnabledNotifications();
456
457
	if ($type !== 'user')
458
	{
459
		sort($enabled);
460
		return $enabled;
461
	}
462
463
	$userAllEnabled = getUsersNotificationsPreferences($enabled, $user);
464
465
	// Drop ones they do not have enabled (primarily used to drop watchedtopic / watched board)
466
	foreach ($enabled as $key => $notificationType)
467
	{
468
		if (!isset($userAllEnabled[$user][$notificationType]))
469
		{
470
			unset($enabled[$key]);
471
		}
472
	}
473
474
	// Filter the remaining as requested
475
	foreach ($userAllEnabled[$user] as $notificationType => $allowedMethods)
476
	{
477
		if (!in_array('notification', $allowedMethods, true))
478
		{
479
			$key = array_search($notificationType, $enabled, true);
480
			if ($key !== false)
481
			{
482
				unset($enabled[$key]);
483
			}
484
		}
485
	}
486
487
	sort($enabled);
488
	return $enabled;
489
}
490