TrackerInline   B
last analyzed

Complexity

Total Complexity 47

Size/Duplication

Total Lines 300
Duplicated Lines 33.33 %

Coupling/Cohesion

Components 1
Dependencies 11

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
dl 100
loc 300
ccs 0
cts 191
cp 0
rs 8.64
c 0
b 0
f 0
wmc 47
lcom 1
cbo 11

12 Methods

Rating   Name   Duplication   Size   Complexity  
A export() 0 4 1
A __construct() 0 15 2
A update() 15 15 3
A delete() 0 20 5
B import() 25 25 6
B tag_update() 12 26 6
B mass_tag_update() 12 33 8
A _clean_tag_string() 0 7 1
A set_category() 21 21 5
A hide_notice() 0 7 2
A ignore() 15 15 3
A set_mal_id() 0 21 5

How to fix   Duplicated Code    Complexity   

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:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like TrackerInline often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use TrackerInline, and based on these observations, apply Extract Interface, too.

1
<?php defined('BASEPATH') or exit('No direct script access allowed');
2
3
class TrackerInline extends Auth_Controller {
4
	private $userID;
5
6
	public function __construct() {
7
		parent::__construct(FALSE);
8
9
		$this->load->library('Limiter');
10
		$this->load->library('form_validation');
11
12
		//1000 requests per hour to either AJAX request.
13
		if($this->limiter->limit('tracker_general', 1000)) {
14
			$this->output->set_status_header('429', 'Rate limit reached'); //rate limited reached
15
16
			exit_ci();
17
		}
18
19
		$this->userID = (int) $this->User->id;
20
	}
21
22
	/**
23
	 * Used locally to update the users' latest read chapter of a series.
24
	 *
25
	 * REQ_PARAMS: id, chapter
26
	 * METHOD:     POST
27
	 * URL:        /ajax/update_inline
28
	 */
29 View Code Duplication
	public function update() : void {
30
		$this->form_validation->set_rules('id',      'Chapter ID', 'required|ctype_digit');
31
		$this->form_validation->set_rules('chapter', 'Chapter',    'required');
32
33
		if($this->form_validation->run() === TRUE) {
34
			$success = $this->Tracker->list->updateByID($this->userID, $this->input->post('id'), $this->input->post('chapter'));
35
			if($success) {
36
				$this->output->set_status_header('200'); //Success!
37
			} else {
38
				$this->output->set_status_header('400', 'Unable to update?');
39
			}
40
		} else {
41
			$this->output->set_status_header('400', 'Missing/invalid parameters.');
42
		}
43
	}
44
45
	/**
46
	 * Used locally to remove (multiple) series from a users' list.
47
	 *
48
	 * REQ_PARAMS: id[]
49
	 * METHOD:     POST
50
	 * URL:        /ajax/delete_inline
51
	 */
52
	public function delete() : void {
53
		$this->form_validation->set_rules('id[]', 'List of IDs', 'required|ctype_digit');
54
55
		if($this->form_validation->run() === TRUE) {
56
			$status = $this->Tracker->list->deleteByIDList($this->input->post('id[]'));
57
			switch($status['code']) {
58
				case 0:
59
					$this->output->set_status_header('200'); //Success!
60
					break;
61
				case 1:
62
					$this->output->set_status_header('400', 'Request contains invalid IDs');
63
					break;
64
				case 2:
65
					$this->output->set_status_header('400', 'Request contains invalid elements.');
66
					break;
67
			}
68
		} else {
69
			$this->output->set_status_header('400', 'Request contained invalid elements!');
70
		}
71
	}
72
73
	/**
74
	 * Used to import a tracker exported list.
75
	 *
76
	 * REQ_PARAMS: json
77
	 * METHOD:     POST
78
	 * URL:        /import_list
79
	 */
80 View Code Duplication
	public function import() : void {
81
		$this->form_validation->set_rules('json', 'JSON String', 'required|is_valid_json');
82
83
		if($this->form_validation->run() === TRUE) {
84
			$status = $this->Tracker->portation->importFromJSON($this->input->post('json'));
85
			switch($status['code']) {
86
				case 0:
87
					$this->output->set_status_header('200'); //Success
88
					break;
89
				case 1:
90
					$this->output->set_status_header('400', 'JSON contains invalid keys');
91
					break;
92
				case 2:
93
					$this->output->set_status_header('400', 'Unable to add some rows from JSON');
94
					//$this->_render_json(json_encode($status['failed_rows'])); //TODO: We should list what rows these are.
95
					break;
96
			}
97
		} else {
98
			if(!$this->form_validation->isRuleValid('is_valid_json')) {
99
				$this->output->set_status_header('400', 'File isn\'t valid JSON!');
100
			} else {
101
				$this->output->set_status_header('400', 'No file sent');
102
			}
103
		}
104
	}
105
106
	/**
107
	 * Used to export a users' list.
108
	 *
109
	 * REQ_PARAMS: N/A
110
	 * METHOD:     GET/POST
111
	 * URL:        /export_list
112
	 */
113
	public function export() : void {
114
		$trackerData = $this->Tracker->portation->export();
115
		$this->_render_json($trackerData, TRUE);
116
	}
117
118
	/**
119
	 * Used to set title tags
120
	 *
121
	 * REQ_PARAMS: id, tag_string
122
	 * METHOD:     POST
123
	 * URL:        /tag_update
124
	 */
125
	public function tag_update() : void {
126
		$this->form_validation->set_rules('id',         'Chapter ID', 'required|ctype_digit');
127
		$this->form_validation->set_rules('tag_string', 'Tag String', 'max_length[255]|is_valid_tag_string|not_equals[none]');
128
129
		if($this->form_validation->run() === TRUE) {
130
			$tag_string = $this->_clean_tag_string($this->input->post('tag_string'));
131
132
			$success = $this->Tracker->tag->updateByID($this->userID, $this->input->post('id'), $tag_string);
133
			if($success) {
134
				$this->output->set_status_header('200'); //Success!
135
			} else {
136
				$this->output->set_status_header('400', 'Unable to set tags?');
137
			}
138 View Code Duplication
		} else {
139
			$errorArr = $this->form_validation->error_array();
140
			if(in_array('max_length', $errorArr)) {
141
				$this->output->set_status_header('400', 'Tag string is too long! Max length is 255 characters.');
142
			} else if(in_array('not_equals', $errorArr)) {
143
				$this->output->set_status_header('400', '"none" is a restricted tag.');
144
			} else if(in_array('is_valid_tag_string', $errorArr)) {
145
				$this->output->set_status_header('400', 'Tags can only contain: lowercase a-z, 0-9, -, :, & _. They can also only have one MAL metatag.');
146
			} else {
147
				$this->output->set_status_header('400', 'Missing/invalid parameters.');
148
			}
149
		}
150
	}
151
152
	/**
153
	 * Used to mass set title tags
154
	 *
155
	 * REQ_PARAMS: id[], tag_string
156
	 * METHOD:     POST
157
	 * URL:        /tag_update
158
	 */
159
	public function mass_tag_update() : void {
160
		$this->form_validation->set_rules('id[]', 'List of IDs', 'required|ctype_digit');
161
		$this->form_validation->set_rules('tag_string', 'Tag String', 'max_length[255]|is_valid_tag_string|not_equals[none]');
162
163
		if($this->form_validation->run() === TRUE) {
164
			$idList = $this->input->post('id[]');
165
			$tags   = $this->_clean_tag_string($this->input->post('tag_string'));
166
167
			$success = FALSE;
168
			foreach($idList as $id) {
169
				if(!($success = $this->Tracker->tag->updateByID($this->userID, $id, $tags))) {
170
					break; //end if one id fails
171
				};
172
			}
173
174
			if($success) {
175
				$this->output->set_status_header('200'); //Success!
176
			} else {
177
				$this->output->set_status_header('400', 'Unable to set tags?');
178
			}
179 View Code Duplication
		} else {
180
			$errorArr = $this->form_validation->error_array();
181
			if(in_array('max_length', $errorArr)) {
182
				$this->output->set_status_header('400', 'Tag string is too long! Max length is 255 characters.');
183
			} else if(in_array('not_equals', $errorArr)) {
184
				$this->output->set_status_header('400', '"none" is a restricted tag.');
185
			} else if(in_array('is_valid_tag_string', $errorArr)) {
186
				$this->output->set_status_header('400', 'Tags can only contain: lowercase a-z, 0-9, -, :, & _. They can also only have one MAL metatag.');
187
			} else {
188
				$this->output->set_status_header('400', 'Missing/invalid parameters.');
189
			}
190
		}
191
	}
192
193
	/**
194
	 * @param string $tag_string
195
	 *
196
	 * @return string
197
	 */
198
	private function _clean_tag_string(string $tag_string) : string {
199
		$tag_array = explode(',', $tag_string);
200
		$tag_array = array_unique($tag_array);
201
		$tag_array = array_filter($tag_array);
202
203
		return implode(',', $tag_array);
204
	}
205
206
	/**
207
	 * Used to set chapter user category
208
	 *
209
	 * REQ_PARAMS: id[], category
210
	 * METHOD:     POST
211
	 * URL:        /set_category
212
	 */
213 View Code Duplication
	public function set_category() : void {
214
		$this->form_validation->set_rules('id[]',     'List of IDs',   'required|ctype_digit');
215
		$this->form_validation->set_rules('category', 'Category Name', 'required|is_valid_category');
216
217
		if($this->form_validation->run() === TRUE) {
218
			$status = $this->Tracker->category->setByIDList($this->input->post('id[]'), $this->input->post('category'));
219
			switch($status['code']) {
220
				case 0:
221
					$this->output->set_status_header('200'); //Success!
222
					break;
223
				case 1:
224
					$this->output->set_status_header('400', 'Request contains invalid IDs');
225
					break;
226
				case 2:
227
					$this->output->set_status_header('400', 'Request contains invalid category.');
228
					break;
229
			}
230
		} else {
231
			$this->output->set_status_header('400', 'Request contained invalid elements!');
232
		}
233
	}
234
235
	/**
236
	 * Used to permanently hide the current notice.
237
	 *
238
	 * REQ_PARAMS: [none]
239
	 * METHOD:     POST
240
	 * URL:        /ajax/hide_notice
241
	 */
242
	public function hide_notice() : void {
243
		if($this->User->hideLatestNotice()) {
244
			$this->output->set_status_header('200'); //Success!
245
		} else {
246
			$this->output->set_status_header('400', 'Something went wrong');
247
		}
248
	}
249
250
251
	/**
252
	 * Used locally to ignore the latest chapter of a series so it doesn't clog up the current unread list.
253
	 *
254
	 * REQ_PARAMS: id, chapter
255
	 * METHOD:     POST
256
	 * URL:        /ajax/ignore_inline
257
	 */
258 View Code Duplication
	public function ignore() : void {
259
		$this->form_validation->set_rules('id',      'Chapter ID', 'required|ctype_digit');
260
		$this->form_validation->set_rules('chapter', 'Chapter',    'required');
261
262
		if($this->form_validation->run() === TRUE) {
263
			$success = $this->Tracker->list->ignoreByID($this->userID, $this->input->post('id'), $this->input->post('chapter'));
264
			if($success) {
265
				$this->output->set_status_header('200'); //Success!
266
			} else {
267
				$this->output->set_status_header('400', 'Unable to ignore?');
268
			}
269
		} else {
270
			$this->output->set_status_header('400', 'Missing/invalid parameters.');
271
		}
272
	}
273
274
	/**
275
	 * Used to set MAL ID
276
	 *
277
	 * REQ_PARAMS: id, mal_id
278
	 * METHOD:     POST
279
	 * URL:        /set_mal_id
280
	 */
281
	public function set_mal_id() : void {
282
		$this->form_validation->set_rules('id',     'Chapter ID', 'required|ctype_digit');
283
		$this->form_validation->set_rules('mal_id', 'MAL ID',     'regex_match[/^[0-9]*$/]');
284
285
		if($this->form_validation->run() === TRUE) {
286
			$malID = (is_numeric($this->input->post('mal_id')) ? $this->input->post('mal_id') : NULL);
287
			$success = $this->Tracker->list->setMalID($this->userID, $this->input->post('id'), $malID);
288
			if($success) {
289
				$this->output->set_status_header('200'); //Success!
290
			} else {
291
				$this->output->set_status_header('400', 'Unable to set MAL ID?');
292
			}
293
		} else {
294
			$errorArr = $this->form_validation->error_array();
295
			if(in_array('regex_match', $errorArr)) {
296
				$this->output->set_status_header('400', 'MAL id must be numeric or null.');
297
			} else {
298
				$this->output->set_status_header('400', 'Missing/invalid parameters.');
299
			}
300
		}
301
	}
302
}
303