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
![]() |
|||
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
|
|||
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
The property
id does not exist on ElkArte\Helper\ValuesContainer . Since you implemented __get , consider adding a @property annotation.
![]() |
|||
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
The property
mentions does not exist on ElkArte\Helper\ValuesContainer . Since you implemented __get , consider adding a @property annotation.
![]() |
|||
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> '; |
||
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
The property
id does not exist on ElkArte\Helper\ValuesContainer . Since you implemented __get , consider adding a @property annotation.
![]() |
|||
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['breadcrumbs'][] = [ |
||
313 | 'url' => getUrl('action', ['action' => 'mentions']), |
||
314 | 'name' => $txt['my_mentions'], |
||
315 | ]; |
||
316 | |||
317 | if (!empty($this->_type)) |
||
318 | { |
||
319 | $context['breadcrumbs'][] = [ |
||
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 |