Issues (1686)

sources/ElkArte/Mentions/Mentioning.php (5 issues)

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\Mentions;
15
16
use ElkArte\AbstractModel;
17
use ElkArte\Database\QueryInterface;
18
use ElkArte\Helper\DataValidator;
19
use ElkArte\Languages\Txt;
20
use ElkArte\Mentions\MentionType\NotificationInterface;
21
22
/**
23
 * Takes care of validating and inserting mention notifications in the database
24
 *
25
 * @package Mentions
26
 */
27
class Mentioning extends AbstractModel
28
{
29
	/** @const int Value assumed by a new mention */
30
	public const MNEW = 0;
31
32
	/** @const int Value assumed by a mention that has been read */
33
	public const READ = 1;
34
35
	/** @const int Value assumed by a mention that has been deleted */
36
	public const DELETED = 2;
37
38
	/** @const int Value assumed by an unapproved mention */
39
	public const UNAPPROVED = 3;
40
41
	/** @var array Will hold all available mention types */
42
	protected $_known_mentions = [];
43
44
	/** @var array Will hold all available mention status constants
45
	 * 'new' => 0, 'read' => 1, 'deleted' => 2, 'unapproved' => 3 */
46
	protected $_known_status = [];
47
48
	/** @var array Holds the passed data for this instance, is passed through the validator */
49
	protected $_data;
50
51
	/**
52
	 * Mentioning constructor.
53
	 *
54
	 * @param QueryInterface $db
55
	 * @param DataValidator $_validator
56
	 * @param string $enabled_mentions
57
	 */
58
	public function __construct($db, $user, protected $_validator, $enabled_mentions = '')
59
	{
60
		$this->_known_status = [
61
			'new' => self::MNEW,
62
			'read' => self::READ,
63
			'deleted' => self::DELETED,
64
			'unapproved' => self::UNAPPROVED,
65
		];
66
67
		$this->_known_mentions = array_filter(array_unique(explode(',', $enabled_mentions)));
68
69
		parent::__construct($db, $user);
70
	}
71
72
	/**
73
	 * Inserts a new mention.
74
	 *
75
	 * @param NotificationInterface $mention_obj The object that knows how to store the mention in the database
76
	 * @param array $data must contain uid, type and msg at a minimum
77
	 *
78
	 * @return int[]
79
	 */
80
	public function create($mention_obj, $data)
81
	{
82
		$this->_data = $this->_prepareData($data);
83
84
		// Common checks to determine if we can go on
85
		if (!$this->_isValid())
86
		{
87
			return [];
88
		}
89 6
90
		// Cleanup, validate and remove the invalid values (0 and $this->_data['id_member_from'])
91 6
		$id_targets = array_diff(array_map('intval', array_unique($this->_validator->uid)), array(0, $this->_data['id_member_from']));
0 ignored issues
show
It seems like $this->_validator->uid can also be of type null; however, parameter $array of array_unique() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

91
		$id_targets = array_diff(array_map('intval', array_unique(/** @scrutinizer ignore-type */ $this->_validator->uid)), array(0, $this->_data['id_member_from']));
Loading history...
Bug Best Practice introduced by
The property uid does not exist on ElkArte\Helper\DataValidator. Since you implemented __get, consider adding a @property annotation.
Loading history...
92 6
93 6
		if (empty($id_targets))
94 6
		{
95 6
			return [];
96
		}
97
98 6
		$actually_mentioned = $mention_obj->insert($this->_data['id_member_from'], $id_targets, $this->_validator->msg, $this->_validator->log_time, $this->_data['status']);
0 ignored issues
show
Bug Best Practice introduced by
The property msg does not exist on ElkArte\Helper\DataValidator. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug Best Practice introduced by
The property log_time does not exist on ElkArte\Helper\DataValidator. Since you implemented __get, consider adding a @property annotation.
Loading history...
99
100 6
		// Update the member mention count
101
		foreach ($actually_mentioned as $id_target)
102 6
		{
103 6
			$this->_updateMenuCount($this->_data['status'], $id_target);
104
		}
105
106
		return $actually_mentioned;
107
	}
108
109
	/**
110
	 * Prepares the data sent to Mentioning::create to be ready for the actual insert.
111
	 *
112
	 * @param array $data must contain uid, type and msg at a minimum
113
	 *
114
	 * @return array
115 4
	 */
116
	protected function _prepareData($data)
117 4
	{
118
		if (isset($data['id_member']))
119
		{
120 4
			$_data = [
121
				'uid' => is_array($data['id_member']) ? $data['id_member'] : [$data['id_member']],
122
				'type' => $data['type'],
123
				'msg' => $data['id_msg'],
124
				'status' => isset($data['status'], $this->_known_status[$data['status']]) ? $this->_known_status[$data['status']] : 0,
125
			];
126 4
127
			if (isset($data['id_member_from']))
128 4
			{
129
				$_data['id_member_from'] = $data['id_member_from'];
130
			}
131
132
			if (isset($data['log_time']))
133 4
			{
134
				$_data['log_time'] = $data['log_time'];
135
			}
136 4
		}
137
		else
138 4
		{
139
			$_data = $data;
140
		}
141 4
142
		return $_data;
143
	}
144
145
	/**
146
	 * Check if the user can do what he is supposed to do, and validates the input.
147
	 *
148
	 * @return bool
149
	 */
150
	protected function _isValid()
151
	{
152 4
		$sanitization = [
153
			'type' => 'trim',
154 4
			'msg' => 'intval',
155
		];
156
157 4
		$validation = [
158 4
			'type' => 'required|contains[' . implode(',', $this->_known_mentions) . ']',
159 4
			'uid' => 'isarray',
160 4
		];
161
162
		// Any optional fields we need to check?
163 4
		if (isset($this->_data['id_member_from']))
164
		{
165 4
			$sanitization['id_member_from'] = 'intval';
166
			$validation['id_member_from'] = 'required|notequal[0]';
167
		}
168 4
169
		if (isset($this->_data['log_time']))
170 4
		{
171
			$sanitization['log_time'] = 'intval';
172
			$validation['log_time'] = 'required|notequal[0]';
173
		}
174
175
		$this->_validator->sanitation_rules($sanitization);
176
		$this->_validator->validation_rules($validation);
177
178 4
		if (!$this->_validator->validate($this->_data))
179
		{
180
			return false;
181
		}
182
183
		// If everything is fine, let's prepare for the fun!
184
		Txt::load('Mentions');
185
186 4
		return true;
187
	}
188
189 4
	/**
190
	 * Updates the mention count as a result of an action, read, new, delete, etc
191
	 *
192
	 * @param int $status
193
	 * @param int $member_id
194 4
	 * @package Mentions
195 4
	 */
196
	protected function _updateMenuCount($status, $member_id)
197
	{
198
		require_once(SUBSDIR . '/Members.subs.php');
199 4
200
		// If its new add to our menu count
201 4
		if ($status === 0)
202 4
		{
203
			updateMemberData($member_id, ['mentions' => '+']);
204
		}
205 4
		// Mark as read we decrease the count
206
		elseif ($status === 1)
207 4
		{
208 4
			updateMemberData($member_id, ['mentions' => '-']);
209
		}
210
		// Deleting or un-approving may have been read or not, so a count is required
211 4
		else
212 4
		{
213
			countUserMentions(false, '', $member_id);
214 4
		}
215
	}
216
217
	/**
218
	 * Did you read the mention? Then let's move it to the graveyard.
219
	 * Used in Display.controller.php, it may be merged to action_updatestatus
220 4
	 * though that would require to add an optional parameter to avoid the redirect
221
	 *
222 4
	 * @param int|int[] $mention_id
223
	 * @return bool if successfully changed or not
224
	 */
225
	public function markread($mention_id)
226
	{
227
		return $this->updateStatus($mention_id, 'readall');
228
	}
229
230
	/**
231
	 * Updating the status from the listing?
232
	 *
233 6
	 * @param int|int[] $items
234
	 * @param string $mark
235 6
	 * @return bool if successfully changed or not
236
	 */
237
	public function updateStatus($items, $mark)
238 6
	{
239
		// Make sure it is all good
240 4
		$own_id = $this->_getAccessible((array) $items, $mark);
241
242
		if (!empty($own_id))
243 2
		{
244
			switch ($mark)
245 2
			{
246
				case 'read':
247
				case 'readall':
248
					return $this->_changeStatus($own_id, 'read');
249
				case 'unread':
250
					return $this->_changeStatus($own_id, 'new');
251
				case 'delete':
252 6
					return $this->_changeStatus($own_id, 'deleted');
253
			}
254
		}
255
256
		return false;
257
	}
258
259
	/**
260
	 * Of the passed IDs returns those accessible to the user.
261
	 *
262
	 * @param int[] $mention_ids
263 2
	 * @param string $action
264
	 * @return int[]
265 2
	 */
266
	protected function _getAccessible($mention_ids, $action)
267
	{
268
		require_once(SUBSDIR . '/Mentions.subs.php');
269
		$sanitization = [
270
			'id_mention' => 'intval',
271
			'mark' => 'trim',
272
		];
273
		$validation = [
274
			'id_mention' => 'validate_own_mention',
275
			'mark' => 'contains[read,unread,delete,readall]',
276 2
		];
277
278
		$this->_validator->sanitation_rules($sanitization);
279 2
		$this->_validator->validation_rules($validation);
280
281 2
		$own = [];
282
		foreach ($mention_ids as $id)
283 1
		{
284
			if ($this->_validator->validate(['id_mention' => $id, 'mark' => $action]))
285 2
			{
286 2
				$own[] = $id;
287 2
			}
288
		}
289
290
		return $own;
291
	}
292
293
	/**
294
	 * Changes a specific mention status for a member.
295
	 *
296
	 * - Can be used to mark as read, new, deleted, etc
297
	 * - note that delete is a "soft-delete" because otherwise anyway we have to remember
298
	 * - when a user was already mentioned for a certain message (e.g. in case of editing)
299
	 *
300
	 * @param int|int[] $id_mentions the mention(s) id in the db
301
	 * @param string $status status to update, 'new', 'read', 'deleted', 'unapproved'
302
	 * @return bool if successfully changed or not
303
	 * @package Mentions
304
	 */
305
	protected function _changeStatus($id_mentions, $status = 'read')
306
	{
307
		require_once(SUBSDIR . '/Mentions.subs.php');
308 2
309
		$success = changeStatus($id_mentions, $this->user->id, $this->_known_status[$status], false);
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on ElkArte\UserInfo. Since you implemented __get, consider adding a @property annotation.
Loading history...
310 2
311
		// Update the top level mentions count
312 2
		if ($success)
313
		{
314
			$this->_updateMenuCount($this->_known_status[$status], $this->user->id);
315
		}
316 2
317
		return $success;
318
	}
319
}
320