forked from elkarte/Elkarte
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMentioning.php
319 lines (273 loc) · 7.86 KB
/
Mentioning.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
<?php
/**
* Handles all the mentions actions so members are notified of mentionable actions
*
* @package ElkArte Forum
* @copyright ElkArte Forum contributors
* @license BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file)
*
* @version 2.0 dev
*
*/
namespace ElkArte\Mentions;
use ElkArte\AbstractModel;
use ElkArte\Database\QueryInterface;
use ElkArte\Helper\DataValidator;
use ElkArte\Languages\Txt;
use ElkArte\Mentions\MentionType\NotificationInterface;
/**
* Takes care of validating and inserting mention notifications in the database
*
* @package Mentions
*/
class Mentioning extends AbstractModel
{
/** @const int Value assumed by a new mention */
public const MNEW = 0;
/** @const int Value assumed by a mention that has been read */
public const READ = 1;
/** @const int Value assumed by a mention that has been deleted */
public const DELETED = 2;
/** @const int Value assumed by an unapproved mention */
public const UNAPPROVED = 3;
/** @var array Will hold all available mention types */
protected $_known_mentions = [];
/** @var array Will hold all available mention status constants
* 'new' => 0, 'read' => 1, 'deleted' => 2, 'unapproved' => 3 */
protected $_known_status = [];
/** @var array Holds the passed data for this instance, is passed through the validator */
protected $_data;
/**
* Mentioning constructor.
*
* @param QueryInterface $db
* @param DataValidator $_validator
* @param string $enabled_mentions
*/
public function __construct($db, $user, protected $_validator, $enabled_mentions = '')
{
$this->_known_status = [
'new' => self::MNEW,
'read' => self::READ,
'deleted' => self::DELETED,
'unapproved' => self::UNAPPROVED,
];
$this->_known_mentions = array_filter(array_unique(explode(',', $enabled_mentions)));
parent::__construct($db, $user);
}
/**
* Inserts a new mention.
*
* @param NotificationInterface $mention_obj The object that knows how to store the mention in the database
* @param array $data must contain uid, type and msg at a minimum
*
* @return int[]
*/
public function create($mention_obj, $data)
{
$this->_data = $this->_prepareData($data);
// Common checks to determine if we can go on
if (!$this->_isValid())
{
return [];
}
// Cleanup, validate and remove the invalid values (0 and $this->_data['id_member_from'])
$id_targets = array_diff(array_map('intval', array_unique($this->_validator->uid)), array(0, $this->_data['id_member_from']));
if (empty($id_targets))
{
return [];
}
$actually_mentioned = $mention_obj->insert($this->_data['id_member_from'], $id_targets, $this->_validator->msg, $this->_validator->log_time, $this->_data['status']);
// Update the member mention count
foreach ($actually_mentioned as $id_target)
{
$this->_updateMenuCount($this->_data['status'], $id_target);
}
return $actually_mentioned;
}
/**
* Prepares the data sent to Mentioning::create to be ready for the actual insert.
*
* @param array $data must contain uid, type and msg at a minimum
*
* @return array
*/
protected function _prepareData($data)
{
if (isset($data['id_member']))
{
$_data = [
'uid' => is_array($data['id_member']) ? $data['id_member'] : [$data['id_member']],
'type' => $data['type'],
'msg' => $data['id_msg'],
'status' => isset($data['status'], $this->_known_status[$data['status']]) ? $this->_known_status[$data['status']] : 0,
];
if (isset($data['id_member_from']))
{
$_data['id_member_from'] = $data['id_member_from'];
}
if (isset($data['log_time']))
{
$_data['log_time'] = $data['log_time'];
}
}
else
{
$_data = $data;
}
return $_data;
}
/**
* Check if the user can do what he is supposed to do, and validates the input.
*
* @return bool
*/
protected function _isValid()
{
$sanitization = [
'type' => 'trim',
'msg' => 'intval',
];
$validation = [
'type' => 'required|contains[' . implode(',', $this->_known_mentions) . ']',
'uid' => 'isarray',
];
// Any optional fields we need to check?
if (isset($this->_data['id_member_from']))
{
$sanitization['id_member_from'] = 'intval';
$validation['id_member_from'] = 'required|notequal[0]';
}
if (isset($this->_data['log_time']))
{
$sanitization['log_time'] = 'intval';
$validation['log_time'] = 'required|notequal[0]';
}
$this->_validator->sanitation_rules($sanitization);
$this->_validator->validation_rules($validation);
if (!$this->_validator->validate($this->_data))
{
return false;
}
// If everything is fine, let's prepare for the fun!
Txt::load('Mentions');
return true;
}
/**
* Updates the mention count as a result of an action, read, new, delete, etc
*
* @param int $status
* @param int $member_id
* @package Mentions
*/
protected function _updateMenuCount($status, $member_id)
{
require_once(SUBSDIR . '/Members.subs.php');
// If its new add to our menu count
if ($status === 0)
{
updateMemberData($member_id, ['mentions' => '+']);
}
// Mark as read we decrease the count
elseif ($status === 1)
{
updateMemberData($member_id, ['mentions' => '-']);
}
// Deleting or un-approving may have been read or not, so a count is required
else
{
countUserMentions(false, '', $member_id);
}
}
/**
* Did you read the mention? Then let's move it to the graveyard.
* Used in Display.controller.php, it may be merged to action_updatestatus
* though that would require to add an optional parameter to avoid the redirect
*
* @param int|int[] $mention_id
* @return bool if successfully changed or not
*/
public function markread($mention_id)
{
return $this->updateStatus($mention_id, 'readall');
}
/**
* Updating the status from the listing?
*
* @param int|int[] $items
* @param string $mark
* @return bool if successfully changed or not
*/
public function updateStatus($items, $mark)
{
// Make sure it is all good
$own_id = $this->_getAccessible((array) $items, $mark);
if (!empty($own_id))
{
switch ($mark)
{
case 'read':
case 'readall':
return $this->_changeStatus($own_id, 'read');
case 'unread':
return $this->_changeStatus($own_id, 'new');
case 'delete':
return $this->_changeStatus($own_id, 'deleted');
}
}
return false;
}
/**
* Of the passed IDs returns those accessible to the user.
*
* @param int[] $mention_ids
* @param string $action
* @return int[]
*/
protected function _getAccessible($mention_ids, $action)
{
require_once(SUBSDIR . '/Mentions.subs.php');
$sanitization = [
'id_mention' => 'intval',
'mark' => 'trim',
];
$validation = [
'id_mention' => 'validate_own_mention',
'mark' => 'contains[read,unread,delete,readall]',
];
$this->_validator->sanitation_rules($sanitization);
$this->_validator->validation_rules($validation);
$own = [];
foreach ($mention_ids as $id)
{
if ($this->_validator->validate(['id_mention' => $id, 'mark' => $action]))
{
$own[] = $id;
}
}
return $own;
}
/**
* Changes a specific mention status for a member.
*
* - Can be used to mark as read, new, deleted, etc
* - note that delete is a "soft-delete" because otherwise anyway we have to remember
* - when a user was already mentioned for a certain message (e.g. in case of editing)
*
* @param int|int[] $id_mentions the mention(s) id in the db
* @param string $status status to update, 'new', 'read', 'deleted', 'unapproved'
* @return bool if successfully changed or not
* @package Mentions
*/
protected function _changeStatus($id_mentions, $status = 'read')
{
require_once(SUBSDIR . '/Mentions.subs.php');
$success = changeStatus($id_mentions, $this->user->id, $this->_known_status[$status], false);
// Update the top level mentions count
if ($success)
{
$this->_updateMenuCount($this->_known_status[$status], $this->user->id);
}
return $success;
}
}