Completed
Pull Request — development (#3098)
by John
09:23
created

Mentioning   A

Complexity

Total Complexity 31

Size/Duplication

Total Lines 320
Duplicated Lines 7.5 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 88.64%

Importance

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

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