Userscript   A
last analyzed

Complexity

Total Complexity 17

Size/Duplication

Total Lines 119
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 11

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
dl 0
loc 119
ccs 0
cts 79
cp 0
rs 10
c 0
b 0
f 0
wmc 17
lcom 1
cbo 11

4 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 37 7
A update() 0 31 4
A favourite() 0 25 5
A site_fallback() 0 5 1
1
<?php defined('BASEPATH') or exit('No direct script access allowed');
2
3
class Userscript extends AJAX_Controller {
4
	private $userID;
5
6
	public function __construct() {
7
		parent::__construct();
8
9
		$this->load->library('Limiter');
10
		$this->load->library('form_validation');
11
12
		//500 requests per hour to either AJAX request.
13
		if($this->limiter->limit('tracker_userscript', 1000)) {
14
			$this->output->set_status_header('429', 'Rate limit reached'); //rate limited reached
15
		}
16
17
		//API Key is required for all AJAX requests
18
		//We're not using set_rules here since we can't run form_validation twice.
19
		if($this->form_validation->required($this->input->post('api-key')) && ctype_alnum($this->input->post('api-key'))) {
20
			$this->userID = $this->User->get_id_from_api_key($this->input->post('api-key'));
21
			if(!$this->userID) {
22
				$this->output->set_status_header('400', 'Invalid API Key');
23
			}
24
		} else {
25
			$this->output->set_status_header('400', 'Missing/invalid parameters.');
26
		}
27
28
		$updateAvailable = TRUE;
29
		// TODO: We should record this for analytics purposes.
30
		if($userUserscriptVersion = $this->input->get_request_header('X-Userscript-Version')) {
31
			if(version_compare($userUserscriptVersion, '1.11.3', '<=')) {
32
				// 1.11.3 and below uses an old namespace and having the user update through our notification will install a duplicate version of the script
33
				// so instead we return this is FALSE
34
				$updateAvailable = FALSE;
35
			} else {
36
				$updateAvailable = version_compare($userUserscriptVersion, USERSCRIPT_VERSION, '<');
37
			}
38
		} else {
39
40
		}
41
		$this->output->set_header('X-Userscript-Update-Available: '.((int) $updateAvailable));
42
	}
43
44
	/**
45
	 * This is the main update URL for the userscript.
46
	 *
47
	 * REQ_PARAMS: api-key, manga[site], manga[title], manga[chapter]
48
	 * METHOD:     POST
49
	 * URL:        /ajax/userscript/update
50
	 */
51
	public function update() : void {
52
		if($this->output->is_custom_header_set()) { $this->output->reset_status_header(); return; }
53
54
		$this->form_validation->set_rules('manga[site]', 'Manga [Site]', 'required');
55
		$this->form_validation->set_rules('manga[title]', 'Manga [Title]', 'required');
56
		$this->form_validation->set_rules('manga[chapter]', 'Manga [Chapter]', 'required');
57
58
		if($this->form_validation->run() === TRUE) {
59
			$manga = $this->input->post('manga');
60
61
			$titleData = $this->Tracker->list->update($this->userID, $manga['site'], $manga['title'], $manga['chapter'], TRUE, TRUE);
62
			if($titleData) {
63
				$malID = $this->Tracker->list->getMalID($this->userID, $titleData['id']);
64
				$json = [
65
					'mal_sync' => $this->User_Options->get('mal_sync', $this->userID),
66
					'mal_id'   => $malID['id'] ?? NULL,
67
					'chapter'  => $titleData['chapter']
68
				];
69
70
				$this->output
71
				     ->set_status_header('200')
72
				     ->set_content_type('application/json', 'utf-8')
73
				     ->set_output(json_encode($json));
74
			} else {
75
				//TODO: We should probably try and have more verbose errors here. Return via JSON or something.
76
				$this->output->set_status_header('400', 'Unable to update?');
77
			}
78
		} else {
79
			$this->output->set_status_header('400', 'Missing/invalid parameters.');
80
		}
81
	}
82
83
	/**
84
	 * Favourite a chapter via the userscript.
85
	 *
86
	 * REQ_PARAMS: api-key, manga[site], manga[title], manga[chapter]
87
	 * METHOD:     POST
88
	 * URL:        /ajax/userscript/favourite
89
	 */
90
	public function favourite() : void {
91
		if($this->output->is_custom_header_set()) { $this->output->reset_status_header(); return; }
92
93
		if($this->limiter->limit('tracker_userscript_favourite', 250)) {
94
			$this->output->set_status_header('429', 'Rate limit reached'); //rate limited reached
95
		} else {
96
			$this->form_validation->set_rules('manga[site]', 'Manga [Site]', 'required');
97
			$this->form_validation->set_rules('manga[title]', 'Manga [Title]', 'required');
98
			$this->form_validation->set_rules('manga[chapter]', 'Manga [Chapter]', 'required');
99
			$this->form_validation->set_rules('manga[page]', 'Manga [Page]', '');
100
101
			if($this->form_validation->run() === TRUE) {
102
				$manga = $this->input->post('manga');
103
104
				$success = $this->Tracker->favourites->set($this->userID, $manga['site'], $manga['title'], $manga['chapter'], $manga['page'] ?? NULL);
105
				if($success['bool']) {
106
					$this->output->set_status_header('200', $success['status']); //Success!
107
				} else {
108
					$this->output->set_status_header('400', $success['status']);
109
				}
110
			} else {
111
				$this->output->set_status_header('400', 'Missing/invalid parameters.');
112
			}
113
		}
114
	}
115
116
	public function site_fallback() : void {
117
		$this->output
118
			->set_content_type('js') // You could also use ".jpeg" which will have the full stop removed before looking in config/mimes.php
119
			->set_output('console.log('.json_encode('@require site missing? - '.current_url()).');');
120
	}
121
}
122