Completed
Pull Request — development (#3050)
by John
23:37
created

Mentioning   A

Complexity

Total Complexity 31

Size/Duplication

Total Lines 316
Duplicated Lines 7.59 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 88.64%

Importance

Changes 0
Metric Value
dl 24
loc 316
ccs 78
cts 88
cp 0.8864
rs 9.8
c 0
b 0
f 0
wmc 31
lcom 1
cbo 3

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 15 1
B create() 0 25 4
C _prepareData() 0 22 7
A markread() 0 4 1
B updateStatus() 0 23 6
B _getAccessible() 0 24 3
B _isValid() 10 34 4
A _changeStatus() 0 23 2
A _updateMenuCount() 14 14 3

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

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 2.0 dev
11
 *
12
 */
13
14
/**
15
 * Takes care of validating and inserting mention notifications in the database
16
 *
17
 * @package Mentions
18
 */
19
class Mentioning extends AbstractModel
20
{
21
	/**
22
	 * Value assumed by a new mention
23
	 *
24
	 * @const int
25
	 */
26
	const MNEW = 0;
27
28
	/**
29
	 * Value assumed by a mention that has been read
30
	 *
31
	 * @const int
32
	 */
33
	const READ = 1;
34
35
	/**
36
	 * Value assumed by a mention that has been deleted
37
	 *
38
	 * @const int
39
	 */
40
	const DELETED = 2;
41
42
	/**
43
	 * Value assumed by an unapproved mention
44
	 *
45
	 * @const int
46
	 */
47
	const UNAPPROVED = 3;
48
49
	/**
50
	 * Will hold all available mention types
51
	 *
52
	 * @var array
53
	 */
54
	protected $_known_mentions = array();
55
56
	/**
57
	 * Will hold all available mention status
58
	 * 'new' => 0, 'read' => 1, 'deleted' => 2, 'unapproved' => 3,
59
	 *
60
	 * @var array
61
	 */
62
	protected $_known_status = array();
63
64
	/**
65
	 * Holds the instance of the data validation class
66
	 *
67
	 * @var object
68
	 */
69
	protected $_validator = null;
70
71
	/**
72
	 * Holds the passed data for this instance, is passed through the validator
73
	 *
74
	 * @var array
75
	 */
76
	protected $_data = null;
77
78
	/**
79
	 * Mentioning constructor.
80
	 *
81
	 * @param Database $db
82
	 * @param Data_Validator $validator
83
	 * @param string $enabled_mentions
84
	 */
85 3
	public function __construct($db, $validator, $enabled_mentions = '')
86
	{
87 3
		$this->_known_status = array(
88
			'new' => self::MNEW,
89
			'read' => self::READ,
90
			'deleted' => self::DELETED,
91
			'unapproved' => self::UNAPPROVED,
92
		);
93
94 3
		$this->_validator = $validator;
95
96 3
		$this->_known_mentions = array_filter(array_unique(explode(',', $enabled_mentions)));
97
98 3
		parent::__construct($db);
99 3
	}
100
101
	/**
102
	 * Inserts a new mention.
103
	 *
104
	 * @param ElkArte\sources\subs\MentionType\Mention_Type_Interface $mention_obj The object that knows how to store
105
	 *  the mention in the database
106
	 * @param mixed[] $data must contain uid, type and msg at a minimum
107
	 */
108 2
	public function create($mention_obj, $data)
109
	{
110 2
		global $user_info;
111
112 2
		$this->_data = $this->_prepareData($data);
113
114
		// Common checks to determine if we can go on
115 2
		if (!$this->_isValid())
116
			return false;
117
118
		// Cleanup, validate and remove the invalid values (0 and $user_info['id'])
119 2
		$id_targets = array_diff(array_map('intval', array_unique($this->_validator->uid)), array(0, $user_info['id']));
120
121 2
		if (empty($id_targets))
122
			return false;
123
124 2
		$mention_obj->setDb($this->_db);
125 2
		$mention_obj->insert($user_info['id'], $id_targets, $this->_validator->msg, $this->_validator->log_time, $this->_data['status']);
126
127
		// Update the member mention count
128 2
		foreach ($id_targets as $id_target)
129 2
			$this->_updateMenuCount($this->_data['status'], $id_target);
130
131 2
		return true;
132
	}
133
134
	/**
135
	 * Prepares the data send through Mentioning::create to be ready for the
136
	 * actual insert.
137
	 *
138
	 * @param mixed[] $data must contain uid, type and msg at a minimum
139
	 */
140 2
	protected function _prepareData($data)
141
	{
142 2
		if (isset($data['id_member']))
143
		{
144
			$_data = array(
145 2
				'uid' => is_array($data['id_member']) ? $data['id_member'] : array($data['id_member']),
146 2
				'type' => $data['type'],
147 2
				'msg' => $data['id_msg'],
148 2
				'status' => isset($data['status']) && isset($this->_known_status[$data['status']]) ? $this->_known_status[$data['status']] : 0,
149
			);
150
151 2
			if (isset($data['id_member_from']))
152 2
				$_data['id_member_from'] = $data['id_member_from'];
153
154 2
			if (isset($data['log_time']))
155 2
				$_data['log_time'] = $data['log_time'];
156
		}
157
		else
158
			$_data = $data;
159
160 2
		return $_data;
161
	}
162
163
	/**
164
	 * Did you read the mention? Then let's move it to the graveyard.
165
	 * Used in Display.controller.php, it may be merged to action_updatestatus
166
	 * though that would require to add an optional parameter to avoid the redirect
167
	 *
168
	 * @param int $mention_id
169
	 * @return bool if successfully changed or not
170
	 */
171 1
	public function markread($mention_id)
172
	{
173 1
		return $this->updateStatus($mention_id, 'readall');
174
	}
175
176
	/**
177
	 * Updating the status from the listing?
178
	 *
179
	 * @param int|int[] $items
180
	 * @param string $mark
181
	 * @return bool if successfully changed or not
182
	 */
183 1
	public function updateStatus($items, $mark)
184
	{
185
		// Make sure its all good
186 1
		$own_id = $this->_getAccessible((array) $items, $mark);
187
188 1
		if (!empty($own_id))
189
		{
190
			switch ($mark)
191
			{
192 1
				case 'read':
193 1
				case 'readall':
194 1
					return $this->_changeStatus($own_id, 'read');
195
					break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
196
				case 'unread':
197
					return $this->_changeStatus($own_id, 'new');
198
					break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
199
				case 'delete':
200
					return $this->_changeStatus($own_id, 'deleted');
201
					break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
202
			}
203
		}
204
		return false;
205
	}
206
207
	/**
208
	 * Of the passed IDs returns those accessible to the user.
209
	 *
210
	 * @param int[] $mention_ids
211
	 * @param string $action
212
	 * @return int[]
213
	 */
214 1
	protected function _getAccessible($mention_ids, $action)
215
	{
216 1
		require_once(SUBSDIR . '/Mentions.subs.php');
217
		$sanitization = array(
218 1
			'id_mention' => 'intval',
219
			'mark' => 'trim',
220
		);
221
		$validation = array(
222 1
			'id_mention' => 'validate_ownmention',
223
			'mark' => 'contains[read,unread,delete,readall]',
224
		);
225
226 1
		$this->_validator->sanitation_rules($sanitization);
227 1
		$this->_validator->validation_rules($validation);
228
229 1
		$own = array();
230 1
		foreach ($mention_ids as $id)
231
		{
232 1
			if ($this->_validator->validate(array('id_mention' => $id, 'mark' => $action)))
233 1
				$own[] = $id;
234
		}
235
236 1
		return $own;
237
	}
238
239
	/**
240
	 * Check if the user can do what he is supposed to do, and validates the input.
241
	 */
242 2
	protected function _isValid()
243
	{
244
		$sanitization = array(
245 2
			'type' => 'trim',
246
			'msg' => 'intval',
247
		);
248
		$validation = array(
249 2
			'type' => 'required|contains[' . implode(',', $this->_known_mentions) . ']',
250 2
			'uid' => 'isarray',
251
		);
252
253
		// Any optional fields we need to check?
254 2 View Code Duplication
		if (isset($this->_data['id_member_from']))
255
		{
256 2
			$sanitization['id_member_from'] = 'intval';
257 2
			$validation['id_member_from'] = 'required|notequal[0]';
258
		}
259 2 View Code Duplication
		if (isset($this->_data['log_time']))
260
		{
261 2
			$sanitization['log_time'] = 'intval';
262 2
			$validation['log_time'] = 'required|notequal[0]';
263
		}
264
265 2
		$this->_validator->sanitation_rules($sanitization);
266 2
		$this->_validator->validation_rules($validation);
267
268 2
		if (!$this->_validator->validate($this->_data))
269
			return false;
270
271
		// If everything is fine, let's prepare for the fun!
272 2
		theme()->getTemplates()->loadLanguageFile('Mentions');
273
274 2
		return true;
275
	}
276
277
	/**
278
	 * Changes a specific mention status for a member.
279
	 *
280
	 * - Can be used to mark as read, new, deleted, etc
281
	 * - note that delete is a "soft-delete" because otherwise anyway we have to remember
282
	 * - when a user was already mentioned for a certain message (e.g. in case of editing)
283
	 *
284
	 * @package Mentions
285
	 * @param int|int[] $id_mentions the mention id in the db
286
	 * @param string $status status to update, 'new', 'read', 'deleted', 'unapproved'
287
	 * @return bool if successfully changed or not
288
	 */
289 1
	protected function _changeStatus($id_mentions, $status = 'read')
290
	{
291 1
		global $user_info;
292
293 1
		$this->_db->query('', '
294
			UPDATE {db_prefix}log_mentions
295
			SET status = {int:status}
296
			WHERE id_mention IN ({array_int:id_mentions})',
297
			array(
298 1
				'id_mentions' => (array) $id_mentions,
299 1
				'status' => $this->_known_status[$status],
300
			)
301
		);
302 1
		$success = $this->_db->affected_rows() != 0;
303
304
		// Update the top level mentions count
305 1
		if ($success)
306
		{
307 1
			$this->_updateMenuCount($this->_known_status[$status], $user_info['id']);
308
		}
309
310 1
		return $success;
311
	}
312
313
	/**
314
	 * Updates the mention count as a result of an action, read, new, delete, etc
315
	 *
316
	 * @package Mentions
317
	 * @param int $status
318
	 * @param int $member_id
319
	 */
320 3 View Code Duplication
	protected function _updateMenuCount($status, $member_id)
321
	{
322 3
		require_once(SUBSDIR . '/Members.subs.php');
323
324
		// If its new add to our menu count
325 3
		if ($status === 0)
326 2
			updateMemberData($member_id, array('mentions' => '+'));
327
		// Mark as read we decrease the count
328 1
		elseif ($status === 1)
329 1
			updateMemberData($member_id, array('mentions' => '-'));
330
		// Deleting or un-approving may have been read or not, so a count is required
331
		else
332
			countUserMentions(false, '', $member_id);
333
	}
334
}