Completed
Pull Request — development (#2330)
by Joshua
10:25
created

Mentioning::_isValid()   B

Complexity

Conditions 4
Paths 8

Size

Total Lines 34
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20
Metric Value
dl 0
loc 34
ccs 0
cts 27
cp 0
rs 8.5806
cc 4
eloc 19
nc 8
nop 0
crap 20
1
<?php
2
3
/**
4
 * Handles all the mentions actions so members are notified of mentionable actions
5
 *
6
 * @name      ElkArte Forum
7
 * @copyright ElkArte Forum contributors
8
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause
9
 *
10
 * @version 1.0
11
 *
12
 */
13
14
if (!defined('ELK'))
15
	die('No access...');
16
17
/**
18
 * Takes care of validating and inserting mention notifications in the database
19
 *
20
 * @package Mentions
21
 */
22
class Mentioning extends AbstractModel
23
{
24
	/**
25
	 * Value assumed by a new mention
26
	 *
27
	 * @const int
28
	 */
29
	const MNEW = 0;
30
31
	/**
32
	 * Value assumed by a mention that has been read
33
	 *
34
	 * @const int
35
	 */
36
	const READ = 1;
37
38
	/**
39
	 * Value assumed by a mention that has been deleted
40
	 *
41
	 * @const int
42
	 */
43
	const DELETED = 2;
44
45
	/**
46
	 * Value assumed by an unapproved mention
47
	 *
48
	 * @const int
49
	 */
50
	const UNAPPROVED = 3;
51
52
	/**
53
	 * Will hold all available mention types
54
	 *
55
	 * @var array
56
	 */
57
	protected $_known_mentions = array();
58
59
	/**
60
	 * Will hold all available mention status
61
	 * 'new' => 0, 'read' => 1, 'deleted' => 2, 'unapproved' => 3,
62
	 *
63
	 * @var array
64
	 */
65
	protected $_known_status = array();
66
67
	/**
68
	 * Holds the instance of the data validation class
69
	 *
70
	 * @var object
71
	 */
72
	protected $_validator = null;
73
74
	/**
75
	 * Holds the passed data for this instance, is passed through the validator
76
	 *
77
	 * @var array
78
	 */
79
	protected $_data = null;
80
81
	/**
82
	 * Start things up, what else does a constructor do
83
	 */
84
	public function __construct($db, $validator, $enabled_mentions = '')
85
	{
86
		$this->_known_status = array(
87
			'new' => Mentioning::MNEW,
88
			'read' => Mentioning::READ,
89
			'deleted' => Mentioning::DELETED,
90
			'unapproved' => Mentioning::UNAPPROVED,
91
		);
92
93
		$this->_validator = $validator;
94
95
		$this->_known_mentions = array_filter(array_unique(explode(',', $enabled_mentions)));
96
97
		parent::__construct($db);
98
	}
99
100
	/**
101
	 * Inserts a new mention.
102
	 *
103
	 * @param Mention_Type_Interface $mention_obj The object that knows how to store
104
	 *                                the mention in the database
105
	 * @param mixed[] $data must contain uid, type and msg at a minimum
106
	 */
107
	public function create($mention_obj, $data)
108
	{
109
		global $user_info;
110
111
		$this->_data = $this->_prepareData($data);
112
113
		// Common checks to determine if we can go on
114
		if (!$this->_isValid())
115
			return false;
116
117
		// Cleanup, validate and remove the invalid values (0 and $user_info['id'])
0 ignored issues
show
Unused Code Comprehensibility introduced by
39% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

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