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

Mentions::action_index()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 4
c 0
b 0
f 0
nc 2
nop 0
dl 0
loc 10
ccs 0
cts 5
cp 0
crap 6
rs 10
1
<?php
2
3
/**
4
 * Handles all the mentions actions so members are notified of mentionable actions
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
namespace ElkArte\Controller;
15
16
use ElkArte\AbstractController;
17
use ElkArte\EventManager;
18
use ElkArte\Exceptions\Exception;
19
use ElkArte\Helper\DataValidator;
20
use ElkArte\Languages\Txt;
21
use ElkArte\Mentions\Mentioning;
22
use ElkArte\User;
23
24
/**
25
 * as liking a post, adding a buddy, @ calling a member in a post
26
 *
27
 * @package Mentions
28
 */
29
class Mentions extends AbstractController
30
{
31
	/** @var array Will hold all available mention types */
32
	protected $_known_mentions = [];
33
34
	/** @var string The type of the mention we are looking at (if empty means all of them) */
35
	protected $_type = '';
36
37
	/** @var string The url of the display mentions button (all, unread, etc) */
38
	protected $_url_param = '';
39
40
	/** @var int Used for pagination, keeps track of the current start point */
41
	protected $_page = 0;
42
43
	/** @var int Number of items per page */
44
	protected $_items_per_page = 20;
45
46
	/** @var string Default sorting column */
47
	protected $_default_sort = 'log_time';
48
49
	/** @var string User chosen sorting column */
50
	protected $_sort = '';
51
52
	/** @var string[] The sorting methods we know */
53
	protected $_known_sorting = [];
54
55
	/** @var bool Determine if we are looking only at unread mentions or any kind of */
56
	protected $_all = false;
57
58
	/**
59
	 * Good old constructor
60
	 *
61
	 * @param EventManager $eventManager
62
	 */
63
	public function __construct($eventManager)
64
	{
65
		$this->_known_sorting = ['id_member_from', 'type', 'log_time'];
66
67
		parent::__construct($eventManager);
68
	}
69
70
	/**
71
	 * Set up the data for the mention based on what was requested
72
	 * This function is called before the flow is redirected to action_index().
73
	 */
74
	public function pre_dispatch()
75
	{
76
		global $modSettings;
77
78
		// I'm not sure if this is needed, though better have it. :P
79
		if (empty($modSettings['mentions_enabled']))
80
		{
81
			throw new Exception('no_access', false);
82
		}
83
84
		require_once(SUBSDIR . '/Mentions.subs.php');
85
86
		$this->_known_mentions = getMentionTypes(User::$info->id, 'system');
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...
87
	}
88
89
	/**
90
	 * The default action is to show the list of mentions
91
	 * This allows ?action=mention to be forwarded to action_list()
92
	 */
93
	public function action_index()
94
	{
95
		if ($this->_req->getQuery('sa') === 'fetch')
96
		{
97
			$this->action_fetch();
98
		}
99
		else
100
		{
101
			// Default action to execute
102
			$this->action_list();
103
		}
104
	}
105
106
	/**
107
	 * Fetches number of notifications and number of recently added ones for use
108
	 * in favicon and desktop notifications.
109
	 *
110
	 * @todo probably should be placed somewhere else.
111
	 */
112
	public function action_fetch()
113
	{
114
		global $context, $txt, $modSettings;
115
116
		if (empty($modSettings['usernotif_favicon_enable']) && empty($modSettings['usernotif_desktop_enable']))
117
		{
118
			die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
119
		}
120
121
		setJsonTemplate();
122
123
		require_once(SUBSDIR . '/Mentions.subs.php');
124
125
		$lastsent = $this->_req->getQuery('lastsent', 'intval', 0);
126
		if (empty($lastsent) && !empty($_SESSION['notifications_lastseen']))
127
		{
128
			$lastsent = (int) $_SESSION['notifications_lastseen'];
129
		}
130
131
		// We only know AJAX for this particular action
132
		$context['json_data'] = [
133
			'timelast' => getTimeLastMention($this->user->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...
134
		];
135
136
		// Data to be supplied to FavIco via favicon-notify.js
137
		if (!empty($modSettings['usernotif_favicon_enable']))
138
		{
139
			$context['json_data']['mentions'] = (int) $this->user->mentions;
0 ignored issues
show
Bug Best Practice introduced by
The property mentions does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
140
		}
141
142
		// Data to be supplied to Push via desktop-notify.js
143
		if (!empty($modSettings['usernotif_desktop_enable']))
144
		{
145
			$context['json_data']['desktop_notifications'] = [
146
				'new_from_last' => getNewMentions($this->user->id, $lastsent),
147
				'title' => sprintf($txt['forum_notification'], strip_tags(un_htmlspecialchars($context['forum_name']))),
148
				'link' => '/index.php?action=mentions',
149
			];
150
			$context['json_data']['desktop_notifications']['message'] = sprintf($txt[$lastsent === 0 ? 'unread_notifications' : 'new_from_last_notifications'], $context['json_data']['desktop_notifications']['new_from_last']);
151
		}
152
153
		$_SESSION['notifications_lastseen'] = $context['json_data']['timelast'];
154
	}
155
156
	/**
157
	 * Display a list of mentions for the current user.
158
	 *
159
	 *  - Allows them to mark them read or unread
160
	 *  - Can sort the various forms of mentions, such as likes, buddies, quoted, etc.
161
	 */
162
	public function action_list()
163
	{
164
		global $context, $txt, $scripturl;
165
166
		// Only registered members can be mentioned
167
		is_not_guest();
168
169
		require_once(SUBSDIR . '/Mentions.subs.php');
170
		Txt::load('Mentions');
171
172
		$this->_buildUrl();
173
174
		$list_options = [
175
			'id' => 'list_mentions',
176
			'title' => empty($this->_all) ? $txt['my_unread_mentions'] : $txt['my_mentions'],
177
			'items_per_page' => $this->_items_per_page,
178
			'base_href' => $scripturl . '?action=mentions;sa=list' . $this->_url_param,
179
			'default_sort_col' => $this->_default_sort,
180
			'default_sort_dir' => 'default',
181
			'no_items_label' => $this->_all ? $txt['no_mentions_yet'] : $txt['no_new_mentions'],
182
			'get_items' => [
183
				'function' => fn(int $start, int $limit, string $sort, bool $all, string $type): array => $this->list_loadMentions($start, $limit, $sort, $all, $type),
184
				'params' => [
185
					$this->_all,
186
					$this->_type,
187
				],
188
			],
189
			'get_count' => [
190
				'function' => fn(bool $all, string $type) => $this->list_getMentionCount($all, $type),
191
				'params' => [
192
					$this->_all,
193
					$this->_type,
194
				],
195
			],
196
			'columns' => [
197
				'id_member_from' => [
198
					'header' => [
199
						'value' => $txt['mentions_from'],
200
					],
201
					'data' => [
202
						'function' => static function ($row) {
203
							global $settings;
204
205
							if (isset($settings['mentions']['mentioner_template']))
206
							{
207
								return str_replace(
208
									[
209
										'{avatar_img}',
210
										'{mem_url}',
211
										'{mem_name}',
212
									],
213
									[
214
										$row['avatar']['image'],
215
										empty($row['id_member_from']) ? '#' : getUrl('action', ['action' => 'profile', 'u' => $row['id_member_from']]),
216
										$row['mentioner'],
217
									],
218
									$settings['mentions']['mentioner_template']);
219
							}
220
221
							return '';
222
						},
223
					],
224
					'sort' => [
225
						'default' => 'mtn.id_member_from',
226
						'reverse' => 'mtn.id_member_from DESC',
227
					],
228
				],
229
				'type' => [
230
					'header' => [
231
						'value' => $txt['mentions_what'],
232
					],
233
					'data' => [
234
						'db' => 'message',
235
					],
236
					'sort' => [
237
						'default' => 'mtn.mention_type',
238
						'reverse' => 'mtn.mention_type DESC',
239
					],
240
				],
241
				'log_time' => [
242
					'header' => [
243
						'value' => $txt['mentions_when'],
244
						'class' => 'mention_log_time',
245
					],
246
					'data' => [
247
						'db' => 'log_time',
248
						'timeformat' => 'html_time',
249
						'class' => 'mention_log_time',
250
					],
251
					'sort' => [
252
						'default' => 'mtn.log_time DESC',
253
						'reverse' => 'mtn.log_time',
254
					],
255
				],
256
				'action' => [
257
					'header' => [
258
						'value' => $txt['mentions_action'],
259
						'class' => 'listaction grid8',
260
					],
261
					'data' => [
262
						'function' => static function ($row) {
263
							global $txt;
264
265
							$mark = empty($row['status']) ? 'read' : 'unread';
266
							$opts = '<a href="' . getUrl('action', ['action' => 'mentions', 'sa' => 'updatestatus', 'mark' => $mark, 'item' => $row['id_mention'], '{session_data}']) . '"><i class="icon i-mark_' . $mark . '" title="' . $txt['mentions_mark' . $mark] . '" /><s>' . $txt['mentions_mark' . $mark] . '</s></i></a>&nbsp;';
267
268
							return $opts . '<a href="' . getUrl('action', ['action' => 'mentions', 'sa' => 'updatestatus', 'mark' => 'delete', 'item' => $row['id_mention'], '{session_data}']) . '"><i class="icon i-remove" title="' . $txt['delete'] . '"><s>' . $txt['delete'] . '</s></i></a>';
269
						},
270
						'class' => 'listaction grid8',
271
					],
272
				],
273
			],
274
			'list_menu' => [
275
				'show_on' => 'top',
276
				'links' => [
277
					[
278
						'href' => getUrl('action', ['action' => 'mentions'] + (empty($this->_all) ? [] : ['all'])),
279
						'is_selected' => empty($this->_type),
280
						'label' => $txt['mentions_type_all']
281
					],
282
				],
283
			],
284
			'additional_rows' => [
285
				[
286
					'position' => 'above_column_headers',
287
					'class' => 'flow_flex_right',
288
					'value' => '<a class="linkbutton" href="' . $scripturl . '?action=mentions' . (empty($this->_all) ? ';all' : '') . str_replace(';all', '', $this->_url_param) . '">' . (empty($this->_all) ? $txt['mentions_all'] : $txt['mentions_unread']) . '</a>',
289
				],
290
				[
291
					'class' => 'submitbutton',
292
					'position' => 'below_table_data',
293
					'value' => '<a class="linkbutton" href="' . $scripturl . '?action=mentions;sa=updatestatus;mark=readall' . str_replace(';all', '', $this->_url_param) . ';' . $context['session_var'] . '=' . $context['session_id'] . '">' . $txt['mentions_mark_all_read'] . '</a>',
294
				],
295
			],
296
		];
297
298
		// Build the available mention tabs
299
		$this->_known_mentions = $this->_all === true ? getMentionTypes(User::$info->id, 'system') : getMentionTypes(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...
300
		foreach ($this->_known_mentions as $mention)
301
		{
302
			$list_options['list_menu']['links'][] = [
303
				'href' => getUrl('action', ['action' => 'mentions', 'type' => $mention] + (empty($this->_all) ? [] : ['all'])),
304
				'is_selected' => $this->_type === $mention,
305
				'label' => $txt['mentions_type_' . $mention]
306
			];
307
		}
308
309
		createList($list_options);
310
311
		$context['page_title'] = $txt['my_mentions'] . (empty($this->_page) ? '' : ' - ' . sprintf($txt['my_mentions_pages'], $this->_page));
312
		$context['linktree'][] = [
313
			'url' => getUrl('action', ['action' => 'mentions']),
314
			'name' => $txt['my_mentions'],
315
		];
316
317
		if (!empty($this->_type))
318
		{
319
			$context['linktree'][] = [
320
				'url' => getUrl('action', ['action' => 'mentions', 'type' => $this->_type]),
321
				'name' => $txt['mentions_type_' . $this->_type],
322
			];
323
		}
324
	}
325
326
	/**
327
	 * Builds the link back, so you return to the right list of mentions
328
	 */
329
	protected function _buildUrl()
330
	{
331
		$this->_all = $this->_req->getQuery('all') !== null;
332
		$this->_sort = in_array($this->_req->getQuery('sort', 'trim'), $this->_known_sorting, true) ? $this->_req->getQuery('sort', 'trim') : $this->_default_sort;
333
		$this->_type = in_array($this->_req->getQuery('type', 'trim'), $this->_known_mentions, true) ? $this->_req->getQuery('type', 'trim') : '';
334
		$this->_page = $this->_req->getQuery('start', 'trim', '');
335
336
		$this->_url_param = ($this->_all ? ';all' : '') . (empty($this->_type) ? '' : ';type=' . $this->_type) . ($this->_req->getQuery('start') !== null ? ';start=' . $this->_req->getQuery('start') : '');
337
	}
338
339
	/**
340
	 * Callback for createList(),
341
	 * Returns the number of mentions of $type that a member has
342
	 *
343
	 * @param bool $all : if true counts all the mentions, otherwise only the unread
344
	 * @param string $type : the type of mention
345
	 *
346
	 * @return mixed
347
	 */
348
	public function list_getMentionCount($all, $type)
349
	{
350
		return countUserMentions($all, $type);
351
	}
352
353
	/**
354
	 * Did you read the mention? Then let's move it to the graveyard.
355
	 * Used by Events registered to the prepare_context event of the Display controller
356
	 */
357
	public function action_markread()
358
	{
359
		global $modSettings;
360
361
		checkSession('request');
362
363
		$this->_buildUrl();
364
365
		$id_mention = $this->_req->getQuery('item', 'intval', 0);
366
		$mentioning = new Mentioning(database(), $this->user, new DataValidator(), $modSettings['enabled_mentions']);
367
		$mentioning->updateStatus($id_mention, 'read');
368
	}
369
370
	/**
371
	 * Updating the status from the listing?
372
	 */
373
	public function action_updatestatus()
374
	{
375
		global $modSettings;
376
377
		checkSession('request');
378
379
		$mentioning = new Mentioning(database(), $this->user, new DataValidator(), $modSettings['enabled_mentions']);
380
381
		$id_mention = $this->_req->getQuery('item', 'intval', 0);
382
		$mark = $this->_req->getQuery('mark');
383
384
		$this->_buildUrl();
385
386
		switch ($mark)
387
		{
388
			case 'read':
389
			case 'unread':
390
			case 'delete':
391
				$mentioning->updateStatus($id_mention, $mark);
392
				break;
393
			case 'readall':
394
				Txt::load('Mentions');
395
				$mentions = $this->list_loadMentions((int) $this->_page, $this->_items_per_page, $this->_sort, $this->_all, $this->_type);
396
				$mentioning->markread(array_column($mentions, 'id_mention'));
397
				break;
398
		}
399
400
		redirectexit('action=mentions;sa=list' . $this->_url_param);
401
	}
402
403
	/**
404
	 * Callback for createList(),
405
	 * Returns the mentions of a give type (like/buddy/etc.) & (unread or all)
406
	 *
407
	 * @param int $start start list number
408
	 * @param int $limit how many to show on a page
409
	 * @param string $sort which direction are we showing this
410
	 * @param bool $all : if true load all the mentions or type, otherwise only the unread
411
	 * @param string $type : the type of mention
412
	 *
413
	 * @event view_mentions
414
	 * @return array
415
	 */
416
	public function list_loadMentions($start, $limit, $sort, $all, $type)
417
	{
418
		$totalMentions = countUserMentions($all, $type);
419
		$mentions = [];
420
		$round = 0;
421
		Txt::load('Mentions');
422
423
		// Register the view_mentions event
424
		$this->_registerEvents($type);
425
426
		while ($round < 2)
427
		{
428
			$possible_mentions = getUserMentions($start, $limit, $sort, $all, $type);
429
			$count_possible = count($possible_mentions);
430
431
			$this->_events->trigger('view_mentions', [$type, &$possible_mentions]);
432
433
			foreach ($possible_mentions as $mention)
434
			{
435
				if (count($mentions) < $limit)
436
				{
437
					$mentions[] = $mention;
438
				}
439
				else
440
				{
441
					break;
442
				}
443
			}
444
445
			$round++;
446
447
			// If nothing has been removed OR there are not enough
448
			if (($totalMentions - $start < $limit) || count($mentions) !== $count_possible || count($mentions) === $limit)
449
			{
450
				break;
451
			}
452
453
			// Let's start a bit further into the list
454
			$start += $limit;
455
		}
456
457
		// Trigger an unread count update when needed
458
		if ($all !== false)
459
		{
460
			countUserMentions();
461
		}
462
463
		return $mentions;
464
	}
465
466
	/**
467
	 * Register the listeners for a mention type or for all the mentions.
468
	 *
469
	 * @param string|null $type Specific mention type
470
	 */
471
	protected function _registerEvents($type)
472
	{
473
		if (!empty($type))
474
		{
475
			$to_register = ['\\ElkArte\\Mentions\\MentionType\\Event\\' . ucfirst($type)];
476
		}
477
		else
478
		{
479
			$to_register = array_map(static fn($name) => '\\ElkArte\\Mentions\\MentionType\\Event\\' . ucfirst($name), $this->_known_mentions);
480
		}
481
482
		$this->_registerEvent('view_mentions', 'view', $to_register);
483
	}
484
}
485