Completed
Push — master ( e8691f...abab0f )
by Angus
03:04
created

Tracker_Model::get_tracker_from_user_id()   D

Complexity

Conditions 20
Paths 12

Size

Total Lines 123
Code Lines 85

Duplication

Lines 42
Ratio 34.15 %

Code Coverage

Tests 0
CRAP Score 420

Importance

Changes 0
Metric Value
cc 20
eloc 85
nc 12
nop 1
dl 42
loc 123
ccs 0
cts 0
cp 0
crap 420
rs 4.7294
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php declare(strict_types=1); defined('BASEPATH') OR exit('No direct script access allowed');
2
3
class Tracker_Model extends CI_Model {
4
	public $sites;
5
	public $enabledCategories;
6
7 94
	public function __construct() {
8 94
		parent::__construct();
1 ignored issue
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class CI_Model as the method __construct() does only exist in the following sub-classes of CI_Model: Auth_Model, Batoto, DynastyScans, GameOfScanlation, History_Model, KireiCake, KissManga, MangaFox, MangaHere, MangaPanda, MangaStream, Site_Model, Sites_Model, Tracker_Model, User_Model, User_Options_Model, WebToons. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
9
10 94
		$this->load->database();
11
12 94
		$this->enabledCategories = [
13
			'reading'      => 'Reading',
14
			'on-hold'      => 'On-Hold',
15
			'plan-to-read' => 'Plan to Read'
16
		];
17 94
		if($this->User_Options->get('category_custom_1') == 'enabled') {
18
			$this->enabledCategories['custom1'] = $this->User_Options->get('category_custom_1_text');
19
		}
20 94
		if($this->User_Options->get('category_custom_2') == 'enabled') {
21
			$this->enabledCategories['custom2'] = $this->User_Options->get('category_custom_2_text');
22
		}
23 94
		if($this->User_Options->get('category_custom_3') == 'enabled') {
24
			$this->enabledCategories['custom3'] = $this->User_Options->get('category_custom_3_text');
25
		}
26
27 94
		require_once(APPPATH.'models/Site_Model.php');
28 94
		$this->sites = new Sites_Model;
29 94
	}
30
31
	/****** GET TRACKER *******/
32
	public function get_tracker_from_user_id(int $userID) {
33
		$query = $this->db
34
			->select('tracker_chapters.*,
35
			          tracker_titles.site_id, tracker_titles.title, tracker_titles.title_url, tracker_titles.latest_chapter, tracker_titles.last_updated AS title_last_updated, tracker_titles.complete AS title_complete, tracker_titles.last_checked > DATE_SUB(NOW(), INTERVAL 1 WEEK) AS title_active,
36
			          tracker_sites.site, tracker_sites.site_class, tracker_sites.status AS site_status')
37
			->from('tracker_chapters')
38
			->join('tracker_titles', 'tracker_chapters.title_id = tracker_titles.id', 'left')
39
			->join('tracker_sites', 'tracker_sites.id = tracker_titles.site_id', 'left')
40
			->where('tracker_chapters.user_id', $userID)
41
			->where('tracker_chapters.active', 'Y')
42
			->get();
43
44
		$arr = ['series' => [], 'has_inactive' => FALSE];
45
		foreach($this->enabledCategories as $category => $name) {
46
			$arr['series'][$category] = [
47
				'name'         => $name,
48
				'manga'        => [],
49
				'unread_count' => 0
50
			];
51
		}
52
		if($query->num_rows() > 0) {
53
			foreach ($query->result() as $row) {
54
				$is_unread     = intval($row->latest_chapter == $row->current_chapter ? '1' : '0');
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 37 spaces but found 5 spaces

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
55
				$arr['series'][$row->category]['unread_count'] = (($arr['series'][$row->category]['unread_count'] ?? 0) + !$is_unread);
56
				$data = [
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 42 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
57
					'id' => $row->id,
58
					'generated_current_data' => $this->sites->{$row->site_class}->getChapterData($row->title_url, $row->current_chapter),
59
					'generated_latest_data'  => $this->sites->{$row->site_class}->getChapterData($row->title_url, $row->latest_chapter),
60
					'full_title_url'        =>  $this->sites->{$row->site_class}->getFullTitleURL($row->title_url),
61
62
					'new_chapter_exists'    => $is_unread,
63
					'tag_list'              => $row->tags,
64
					'has_tags'              => !empty($row->tags),
65
66
					'title_data' => [
67
						'id'              => $row->title_id,
68
						'title'           => $row->title,
69
						'title_url'       => $row->title_url,
70
						'latest_chapter'  => $row->latest_chapter,
71
						'current_chapter' => $row->current_chapter,
72
						'last_updated'    => $row->title_last_updated,
73
						'active'          => ($row->site_status == 'disabled' || $row->title_complete == 'Y' || $row->title_active == 1)
74
					],
75
					'site_data' => [
76
						'id'         => $row->site_id,
77
						'site'       => $row->site,
78
						'status'     => $row->site_status
79
					]
80
				];
81
				$arr['series'][$row->category]['manga'][] = $data;
82
83
				if(!$arr['has_inactive']) $arr['has_inactive'] = !$data['title_data']['active'];
84
			}
85
86
			//CHECK: Is this good for speed?
87
			//NOTE: This does not sort in the same way as tablesorter, but it works better.
88
			switch($this->User_Options->get('list_sort_type')) {
89
				case 'unread':
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
90
					foreach (array_keys($arr['series']) as $category) {
91
						usort($arr['series'][$category]['manga'], function ($a, $b) {
92
							$a_text = strtolower("{$a['new_chapter_exists']} - {$a['title_data']['title']}");
93
							$b_text = strtolower("{$b['new_chapter_exists']} - {$b['title_data']['title']}");
94
95
							if($this->User_Options->get('list_sort_order') == 'asc') {
96
								return $a_text <=> $b_text;
97
							} else {
98
								return $b_text <=> $a_text;
99
							}
100
						});
101
					}
102
					break;
103
104 View Code Duplication
				case 'alphabetical':
2 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
105
					foreach (array_keys($arr['series']) as $category) {
106
						usort($arr['series'][$category]['manga'], function ($a, $b) {
107
							$a_text = strtolower("{$a['title_data']['title']}");
108
							$b_text = strtolower("{$b['title_data']['title']}");
109
110
							if($this->User_Options->get('list_sort_order') == 'asc') {
111
								return $a_text <=> $b_text;
112
							} else {
113
								return $b_text <=> $a_text;
114
							}
115
						});
116
					}
117
					break;
118
119 View Code Duplication
				case 'my_status':
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
120
					foreach (array_keys($arr['series']) as $category) {
121
						usort($arr['series'][$category]['manga'], function ($a, $b) {
122
							$a_text = strtolower("{$a['generated_current_data']['number']}");
123
							$b_text = strtolower("{$b['generated_current_data']['number']}");
124
125
							if($this->User_Options->get('list_sort_order') == 'asc') {
126
								return $a_text <=> $b_text;
127
							} else {
128
								return $b_text <=> $a_text;
129
							}
130
						});
131
					}
132
					break;
133
134 View Code Duplication
				case 'latest':
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
135
					foreach (array_keys($arr['series']) as $category) {
136
						usort($arr['series'][$category]['manga'], function ($a, $b) {
137
							$a_text = strtolower("{$a['generated_latest_data']['number']}");
138
							$b_text = strtolower("{$b['generated_latest_data']['number']}");
139
140
							if($this->User_Options->get('list_sort_order') == 'asc') {
141
								return $a_text <=> $b_text;
142
							} else {
143
								return $b_text <=> $a_text;
144
							}
145
						});
146
					}
147
					break;
148
149
				default:
150
					break;
151
			}
152
		}
153
		return $arr;
154
	}
155
156
	public function getSiteDataFromURL(string $site_url) {
157
		$query = $this->db->select('id, site_class')
158
		                  ->from('tracker_sites')
159
		                  ->where('site', $site_url)
160
		                  ->get();
161
162
		if($query->num_rows() > 0) {
163
			$siteData = $query->row();
164
		}
165
166
		return $siteData ?? FALSE;
167
	}
168
169
	public function getTitleID(string $titleURL, int $siteID) {
170
		$query = $this->db->select('tracker_titles.id, tracker_titles.title, tracker_titles.title_url, tracker_titles.complete, tracker_sites.site_class, (tracker_titles.last_checked > DATE_SUB(NOW(), INTERVAL 3 DAY)) AS active', FALSE)
171
		                  ->from('tracker_titles')
172
		                  ->join('tracker_sites', 'tracker_sites.id = tracker_titles.site_id', 'left')
173
		                  ->where('tracker_titles.title_url', $titleURL)
174
		                  ->where('tracker_titles.site_id', $siteID)
175
		                  ->get();
176
177
		if($query->num_rows() > 0) {
178
			$id = (int) $query->row('id');
179
180
			//This updates inactive series if they are newly added, as noted in https://github.com/DakuTree/manga-tracker/issues/5#issuecomment-247480804
181
			if(((int) $query->row('active')) === 0 && $query->row('complete') === 'N') {
182
				$titleData = $this->sites->{$query->row('site_class')}->getTitleData($query->row('title_url'));
183
				if(!is_null($titleData['latest_chapter'])) {
184
					if($this->updateTitleById((int) $id, $titleData['latest_chapter'])) {
185
						//Make sure last_checked is always updated on successful run.
186
						//CHECK: Is there a reason we aren't just doing this in updateTitleById?
187
						$this->db->set('last_checked', 'CURRENT_TIMESTAMP', FALSE)
188
						         ->where('id', $id)
189
						         ->update('tracker_titles');
190
					}
191
				} else {
192
					log_message('error', "{$query->row('title')} failed to update successfully");
193
				}
194
			}
195
196
			$titleID = $id;
197
		} else {
198
			//TODO: Check if title is valid URL!
199
			$titleID = $this->addTitle($titleURL, $siteID);
200
		}
201
202
		return $titleID;
203
	}
204
205
	public function updateTracker(int $userID, string $site, string $title, string $chapter) : bool {
206
		$success = FALSE;
207
		if($siteData = $this->Tracker->getSiteDataFromURL($site)) {
208
			//Validate user input
209
			if(!$this->sites->{$siteData->site_class}->isValidTitleURL($title)) {
210
				//Error is already logged via isValidTitleURL
211
				return FALSE;
212
			}
213
			if(!$this->sites->{$siteData->site_class}->isValidChapter($chapter)) {
214
				//Error is already logged via isValidChapter
215
				return FALSE;
216
			}
217
218
			//NOTE: If the title doesn't exist it will be created. This maybe isn't perfect, but it works for now.
219
			$titleID = $this->Tracker->getTitleID($title, (int) $siteData->id);
220
			if($titleID === 0) {
221
				//Something went wrong.
222
				log_message('error', "TitleID = 0 for {$title} @ {$siteData->id}");
223
				return FALSE;
224
			}
225
226
			$idQuery = $this->db->select('id')
227
			                    ->where('user_id', $userID)
228
			                    ->where('title_id', $titleID)
229
			                    ->get('tracker_chapters');
230
			if($idQuery->num_rows() > 0) {
231
				$success = (bool) $this->db->set(['current_chapter' => $chapter, 'active' => 'Y', 'last_updated' => NULL])
232
				                    ->where('user_id', $userID)
233
				                    ->where('title_id', $titleID)
234
				                    ->update('tracker_chapters');
235
236
				if($success) {
237
					$idQueryRow = $idQuery->row();
238
					$this->History->userUpdateTitle((int) $idQueryRow->id, $chapter);
239
				}
240
			} else {
241
				$category = $this->User_Options->get_by_userid('default_series_category', $userID);
242
				$success = (bool) $this->db->insert('tracker_chapters', [
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 2 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
243
					'user_id'         => $userID,
244
					'title_id'        => $titleID,
245
					'current_chapter' => $chapter,
246
					'category'        => $category
247
				]);
248
249
				if($success) {
250
					$this->History->userAddTitle((int) $this->db->insert_id(), $chapter, $category);
0 ignored issues
show
Bug introduced by
The method insert_id() does not exist on CI_DB_query_builder. Did you maybe mean insert()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
251
				}
252
			}
253
		}
254
		return $success;
255
	}
256
257
	public function updateTrackerByID(int $userID, int $chapterID, string $chapter) : bool {
258
		$success = (bool) $this->db->set(['current_chapter' => $chapter, 'active' => 'Y', 'last_updated' => NULL])
259
		                    ->where('user_id', $userID)
260
		                    ->where('id', $chapterID)
261
		                    ->update('tracker_chapters');
262
263
		if($success) {
264
			$this->History->userUpdateTitle($chapterID, $chapter);
265
		}
266
		return  $success;
267
	}
268
269 View Code Duplication
	public function deleteTrackerByID(int $userID, int $chapterID) {
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
270
		//Series are not fully deleted, they are just marked as inactive as to hide them from the user.
271
		//This is to allow user history to function properly.
272
273
		$success = $this->db->set(['active' => 'N', 'last_updated' => NULL])
274
		                    ->where('user_id', $userID)
275
		                    ->where('id', $chapterID)
276
		                    ->update('tracker_chapters');
277
278
		return (bool) $success;
279
	}
280
	private function updateTitleById(int $id, string $latestChapter) {
281
		//FIXME: Really not too happy with how we're doing history stuff here, it just feels messy.
282
		$query = $this->db->select('latest_chapter AS current_chapter')
283
		                  ->from('tracker_titles')
284
		                  ->where('id', $id)
285
		                  ->get();
286
		$row = $query->row();
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 3 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
287
288
		$success = $this->db->set(['latest_chapter' => $latestChapter]) //last_updated gets updated via a trigger if something changes
289
		                    ->where('id', $id)
290
		                    ->update('tracker_titles');
291
292
		//Update History
293
		//NOTE: To avoid doing another query to grab the last_updated time, we just use time() which <should> get the same thing.
294
		//FIXME: The <preferable> solution here is we'd just check against the last_updated time, but that can have a few issues.
295
		$this->History->updateTitleHistory($id, $row->current_chapter, $latestChapter, date('Y-m-d H:i:s'));
296
297
		return (bool) $success;
298
	}
299
	private function updateTitleDataById(int $id, array $titleData) {
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
300
		$success = $this->db->set($titleData)
301
		                    ->where('id', $id)
302
		                    ->update('tracker_titles');
303
304
		return (bool) $success;
305
	}
306
	private function addTitle(string $titleURL, int $siteID) {
307
		$query = $this->db->select('site, site_class')
308
		                  ->from('tracker_sites')
309
		                  ->where('id', $siteID)
310
		                  ->get();
311
312
		$titleData = $this->sites->{$query->row()->site_class}->getTitleData($titleURL);
313
		$this->db->insert('tracker_titles', array_merge($titleData, ['title_url' => $titleURL, 'site_id' => $siteID]));
314
		$titleID = $this->db->insert_id();
0 ignored issues
show
Bug introduced by
The method insert_id() does not exist on CI_DB_query_builder. Did you maybe mean insert()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
315
316
		$this->History->updateTitleHistory((int) $titleID, NULL, $titleData['latest_chapter'], $titleData['last_updated']);
317
		return $titleID;
318
	}
319
320
	/**
321
	 * Checks for any titles that haven't updated in 16 hours and updates them.
322
	 * This is ran every 6 hours via a cron job.
323
	 */
324
	public function updateLatestChapters() {
325
		$query = $this->db->select('
326
				tracker_titles.id,
327
				tracker_titles.title,
328
				tracker_titles.title_url,
329
				tracker_sites.site,
330
				tracker_sites.site_class,
331
				tracker_sites.status,
332
				tracker_titles.latest_chapter,
333
				tracker_titles.last_updated,
334
				from_unixtime(MAX(auth_users.last_login)) AS timestamp
335
			')
336
			->from('tracker_titles')
337
			->join('tracker_sites', 'tracker_sites.id = tracker_titles.site_id', 'left')
338
			->join('tracker_chapters', 'tracker_titles.id = tracker_chapters.title_id', 'left')
339
			->join('auth_users', 'tracker_chapters.user_id = auth_users.id', 'left')
340
			->where('tracker_sites.status', 'enabled')
341
			->where('tracker_chapters.active', 'Y') //CHECK: Does this apply BEFORE the GROUP BY/HAVING is done?
342
			->where('(`complete` = "N" AND (`latest_chapter` = NULL OR `last_checked` < DATE_SUB(NOW(), INTERVAL 12 HOUR)))', NULL, FALSE) //TODO: Each title should have specific interval time?
343
			->or_where('(`complete` = "Y" AND `last_checked` < DATE_SUB(NOW(), INTERVAL 1 WEEK))', NULL, FALSE)
344
			->group_by('tracker_titles.id')
345
			->having('timestamp IS NOT NULL')
346
			->having('timestamp > DATE_SUB(NOW(), INTERVAL 120 HOUR)')
347
			->order_by('tracker_titles.title', 'ASC')
348
			->get();
349
350
		if($query->num_rows() > 0) {
351
			foreach ($query->result() as $row) {
352
				print "> {$row->title} <{$row->site_class}>"; //Print this prior to doing anything so we can more easily find out if something went wrong
353
				$titleData = $this->sites->{$row->site_class}->getTitleData($row->title_url);
354
				if(!is_null($titleData['latest_chapter'])) {
355
					//FIXME: "At the moment" we don't seem to be doing anything with TitleData['last_updated'].
356
					//       Should we even use this? Y/N
357
					if($this->updateTitleById((int) $row->id, $titleData['latest_chapter'])) {
358
						//Make sure last_checked is always updated on successful run.
359
						//CHECK: Is there a reason we aren't just doing this in updateTitleById?
360
						$this->db->set('last_checked', 'CURRENT_TIMESTAMP', FALSE)
361
						         ->where('id', $row->id)
362
						         ->update('tracker_titles');
363
364
						print " - ({$titleData['latest_chapter']})\n";
365
					}
366
				} else {
367
					log_message('error', "{$row->title} failed to update successfully");
368
					print " - FAILED TO PARSE\n";
369
				}
370
			}
371
		}
372
	}
373
374
	public function exportTrackerFromUserID(int $userID) {
375
		$query = $this->db
376
			->select('tracker_chapters.current_chapter,
377
			          tracker_chapters.category,
378
			          tracker_titles.title_url,
379
			          tracker_sites.site')
380
			->from('tracker_chapters')
381
			->join('tracker_titles', 'tracker_chapters.title_id = tracker_titles.`id', 'left')
382
			->join('tracker_sites', 'tracker_sites.id = tracker_titles.site_id', 'left')
383
			->where('tracker_chapters.user_id', $userID)
384
			->where('tracker_chapters.active', 'Y')
385
			->get();
386
387
		$arr = [];
388
		if($query->num_rows() > 0) {
389
			foreach ($query->result() as $row) {
390
				$arr[$row->category][] = [
391
					'site'            => $row->site,
392
					'title_url'       => $row->title_url,
393
					'current_chapter' => $row->current_chapter
394
				];
395
			}
396
397
			return $arr;
398
		}
399
	}
400
401
	public function importTrackerFromJSON(string $json_string) : array {
402
		//We already know the this is a valid JSON string as it was validated by form_validator.
403
		$json = json_decode($json_string, TRUE);
404
405
		/*
406
		 * 0 = Success
407
		 * 1 = Invalid keys.
408
		 * 2 = Has failed rows
409
		 */
410
		$status = ['code' => 0, 'failed_rows' => []];
411
412
		$categories = array_keys($json);
413
		if(count($categories) === array_intersect(['reading', 'on-hold', 'plan-to-read', 'custom1', 'custom2', 'custom3'], $categories)) {
414
			$json_keys = array_keys(call_user_func_array('array_merge', $json));
415
416
			if(count($json_keys) === 3 && !array_diff(array('site', 'title_url', 'current_chapter'), $json_keys)) {
417
				foreach($categories as $category) {
418
					foreach($json[$category] as $row) {
419
						$success = $this->updateTracker($this->User->id, $row['site'], $row['title_url'], $row['current_chapter']);
420
						if(!$success) {
421
							$status['code']          = 2;
422
							$status['failed_rows'][] = $row;
423
						}
424
					}
425
				}
426
			} else {
427
				$status['code'] = 1;
428
			}
429
		} else {
430
			$status['code'] = 1;
431
		}
432
		return $status;
433
	}
434
435
	public function deleteTrackerByIDList(array $idList) : array {
436
		/*
437
		 * 0 = Success
438
		 * 1 = Invalid IDs
439
		 */
440
		$status = ['code' => 0];
441
442 View Code Duplication
		foreach($idList as $id) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
443
			if(!(ctype_digit($id) && $this->deleteTrackerByID($this->User->id, (int) $id))) {
444
				$status['code'] = 1;
445
			} else {
446
				//Delete was successful, update history too.
447
				$this->History->userRemoveTitle((int) $id);
448
			}
449
		}
450
451
		return $status;
452
	}
453
454
	public function setCategoryByIDList(array $idList, string $category) : array {
455
		/*
456
		 * 0 = Success
457
		 * 1 = Invalid IDs
458
		 * 2 = Invalid category
459
		 */
460
		$status = ['code' => 0];
461
462
		if(in_array($category, array_keys($this->enabledCategories))) {
463 View Code Duplication
			foreach($idList as $id) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
464
				if(!(ctype_digit($id) && $this->setCategoryTrackerByID($this->User->id, (int) $id, $category))) {
465
					$status['code'] = 1;
466
				} else {
467
					//Category update was successful, update history too.
468
					$this->History->userUpdateCategory((int) $id, $category);
469
				}
470
			}
471
		} else {
472
			$status['code'] = 2;
473
		}
474
475
		return $status;
476
	}
477 View Code Duplication
	public function setCategoryTrackerByID(int $userID, int $chapterID, string $category) : bool {
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
478
		$success = $this->db->set(['category' => $category, 'active' => 'Y', 'last_updated' => NULL])
479
		                    ->where('user_id', $userID)
480
		                    ->where('id', $chapterID)
481
		                    ->update('tracker_chapters');
482
483
		return (bool) $success;
484
	}
485
486
487
	public function updateTagsByID(int $userID, int $chapterID, string $tag_string) : bool {
488
		$success = FALSE;
489
		if(preg_match("/^[a-z0-9-_,]{0,255}$/", $tag_string)) {
490
			$success = (bool) $this->db->set(['tags' => $tag_string, 'active' => 'Y', 'last_updated' => NULL])
491
			                           ->where('user_id', $userID)
492
			                           ->where('id', $chapterID)
493
			                           ->update('tracker_chapters');
494
		}
495
496
		if($success) {
497
			//Tag update was successful, update history
498
			$this->History->userUpdateTags($chapterID, $tag_string);
499
		}
500
		return $success;
501
	}
502
503
	public function favouriteChapter(int $userID, string $site, string $title, string $chapter) : array {
504
		$success = array(
505
			'status' => 'Something went wrong',
506
			'bool'   => FALSE
507
		);
508
		if($siteData = $this->Tracker->getSiteDataFromURL($site)) {
509
			//Validate user input
510
			if(!$this->sites->{$siteData->site_class}->isValidTitleURL($title)) {
511
				//Error is already logged via isValidTitleURL
512
				$success['status'] = 'Title URL is not valid';
513
				return $success;
514
			}
515
			if(!$this->sites->{$siteData->site_class}->isValidChapter($chapter)) {
516
				//Error is already logged via isValidChapter
517
				$success['status'] = 'Chapter URL is not valid';
518
				return $success;
519
			}
520
521
			//NOTE: If the title doesn't exist it will be created. This maybe isn't perfect, but it works for now.
522
			$titleID = $this->Tracker->getTitleID($title, (int) $siteData->id);
523
			if($titleID === 0) {
524
				//Something went wrong.
525
				log_message('error', "TitleID = 0 for {$title} @ {$siteData->id}");
526
				return $success;
527
			}
528
529
			//We need the series to be tracked
530
			$idCQuery = $this->db->select('id')
531
			                    ->where('user_id', $userID)
532
			                    ->where('title_id', $titleID)
533
			                    ->get('tracker_chapters');
534
			if($idCQuery->num_rows() > 0) {
535
				$idCQueryRow = $idCQuery->row();
536
537
				//Check if it is already favourited
538
				$idFQuery = $this->db->select('id')
539
				                    ->where('chapter_id', $idCQueryRow->id)
540
				                    ->where('chapter', $chapter)
541
				                    ->get('tracker_favourites');
542
				if($idFQuery->num_rows() > 0) {
543
					//Chapter is already favourited, so remove it from DB
544
					$idFQueryRow = $idFQuery->row();
545
546
					$isSuccess = (bool) $this->db->where('id', $idFQueryRow->id)
547
					                           ->delete('tracker_favourites');
548
549
					if($isSuccess) {
550
						$success = array(
551
							'status' => 'Unfavourited',
552
							'bool'   => TRUE
553
						);
554
						$this->History->userRemoveFavourite((int) $idCQueryRow->id, $chapter);
555
					}
556
				} else {
557
					//Chapter is not favourited, so add to DB.
558
					$isSuccess = (bool) $this->db->insert('tracker_favourites', [
559
						'chapter_id'      => $idCQueryRow->id,
560
						'chapter'         => $chapter,
561
						'updated_at'      => date('Y-m-d H:i:s')
562
					]);
563
564
					if($isSuccess) {
565
						$success = array(
566
							'status' => 'Favourited',
567
							'bool'   => TRUE
568
						);
569
						$this->History->userAddFavourite((int) $idCQueryRow->id, $chapter);
570
					}
571
				}
572
			} else {
573
				$success['status'] = 'Series needs to be tracked before we can favourite chapters';
574
			}
575
		}
576
		return $success;
577
	}
578
	public function getFavourites(int $page) : array {
579
		$rowsPerPage = 50;
580
		$query = $this->db
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 7 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
581
			->select('SQL_CALC_FOUND_ROWS
582
			          tt.title, tt.title_url,
583
			          ts.site, ts.site_class,
584
			          tf.chapter, tf.updated_at', FALSE)
585
			->from('tracker_favourites AS tf')
586
			->join('tracker_chapters AS tc', 'tf.chapter_id = tc.id', 'left')
587
			->join('tracker_titles AS tt', 'tc.title_id = tt.id', 'left')
588
			->join('tracker_sites AS ts', 'tt.site_id = ts.id', 'left')
589
			->where('tc.user_id', $this->User->id) //CHECK: Is this inefficient? Would it be better to have a user_id column in tracker_favourites?
590
			->order_by('tf.id DESC')
591
			->limit($rowsPerPage, ($rowsPerPage * ($page - 1)))
592
			->get();
593
594
		$arr = ['rows' => [], 'totalPages' => 1];
595
		if($query->num_rows() > 0) {
596
			foreach($query->result() as $row) {
597
				$arrRow = [];
598
599
				$arrRow['updated_at'] = $row->updated_at;
600
				$arrRow['title']      = $row->title;
601
				$arrRow['title_url']  = $this->Tracker->sites->{$row->site_class}->getFullTitleURL($row->title_url);
602
603
				$arrRow['site'] = $row->site;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 8 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
604
				$arrRow['site_sprite'] = str_replace('.', '-', $row->site);
605
606
				$chapterData = $this->Tracker->sites->{$row->site_class}->getChapterData($row->title_url, $row->chapter);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 7 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
607
				$arrRow['chapter'] = "<a href=\"{$chapterData['url']}\">{$chapterData['number']}</a>";
608
				$arr['rows'][] = $arrRow;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 5 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
609
			}
610
			$arr['totalPages'] = ceil($this->db->query('SELECT FOUND_ROWS() count;')->row()->count / $rowsPerPage);
611
		}
612
		return $arr;
613
614
	}
615
616
	public function getUsedCategories(int $userID) : array {
0 ignored issues
show
Unused Code introduced by
The parameter $userID is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
617
		$usedCategories = [];
0 ignored issues
show
Unused Code introduced by
$usedCategories is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
618
619
		$query = $this->db->distinct()
620
		                  ->select('category')
621
		                  ->from('tracker_chapters')
622
		                  ->where('tracker_chapters.active', 'Y')
623
		                  ->get();
624
625
		return array_column($query->result_array(), 'category');
626
	}
627
628
	public function getStats() : array {
629
		$stats = array();
630
631
		//CHECK: Is it possible to merge some of these queries?
632
633
		//$this->db->cache_on();
634
		$queryUsers = $this->db->select([
0 ignored issues
show
Documentation introduced by
array('COUNT(*) AS total... END) AS active_users') is of type array<integer,string,{"0..."string","2":"string"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
635
		                      'COUNT(*) AS total_users',
636
		                      'SUM(CASE WHEN api_key IS NOT NULL THEN 1 ELSE 0 END) AS validated_users',
637
		                      'SUM(CASE WHEN (api_key IS NOT NULL AND from_unixtime(last_login) > DATE_SUB(NOW(), INTERVAL 7 DAY)) THEN 1 ELSE 0 END) AS active_users'
638
		                  ], FALSE)
639
		                  ->from('auth_users')
640
		                  ->get();
641
		$stats = array_merge($stats, $queryUsers->result_array()[0]);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 6 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
642
643
		$queryCounts = $this->db->select([
0 ignored issues
show
Documentation introduced by
array('tracker_titles.ti...rs.title_id) AS count') is of type array<integer,string,{"0":"string","1":"string"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 18 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
644
		                      'tracker_titles.title',
645
		                      'COUNT(tracker_chapters.title_id) AS count'
646
		                  ], FALSE)
647
		                  ->from('tracker_chapters')
648
		                  ->join('tracker_titles', 'tracker_titles.id = tracker_chapters.title_id', 'left')
649
		                  ->group_by('tracker_chapters.title_id')
650
		                  ->having('count > 1')
651
		                  ->order_by('count DESC')
652
		                  ->get();
653
		$stats['titles_tracked_more'] = count($queryCounts->result_array());
654
		$stats['top_title_name']  = $queryCounts->result_array()[0]['title'] ?? 'N/A';
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 6 spaces but found 2 spaces

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
655
		$stats['top_title_count'] = $queryCounts->result_array()[0]['count'] ?? 'N/A';
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 5 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
656
657
		$queryTitles = $this->db->select([
0 ignored issues
show
Documentation introduced by
array('COUNT(DISTINCT tr...ND) AS updated_titles') is of type array<integer,string,{"0..."string","3":"string"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
658
		                      'COUNT(DISTINCT tracker_titles.id) AS total_titles',
659
		                      'COUNT(DISTINCT tracker_titles.site_id) AS total_sites',
660
		                      'SUM(CASE WHEN from_unixtime(auth_users.last_login) > DATE_SUB(NOW(), INTERVAL 120 HOUR) IS NOT NULL THEN 0 ELSE 1 END) AS inactive_titles',
661
		                      'SUM(CASE WHEN (tracker_titles.last_updated > DATE_SUB(NOW(), INTERVAL 24 HOUR)) THEN 1 ELSE 0 END) AS updated_titles'
662
		                  ], FALSE)
663
		                  ->from('tracker_titles')
664
		                  ->join('tracker_sites', 'tracker_sites.id = tracker_titles.site_id', 'left')
665
		                  ->join('tracker_chapters', 'tracker_titles.id = tracker_chapters.title_id', 'left')
666
		                  ->join('auth_users', 'tracker_chapters.user_id = auth_users.id', 'left')
667
		                  ->get();
668
		$stats = array_merge($stats, $queryTitles->result_array()[0]);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 7 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
669
670
		$querySites = $this->db->select([
0 ignored issues
show
Documentation introduced by
array('tracker_sites.site', 'COUNT(*) AS count') is of type array<integer,string,{"0":"string","1":"string"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 16 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
671
		                      'tracker_sites.site',
672
		                      'COUNT(*) AS count'
673
		                  ], FALSE)
674
		                  ->from('tracker_titles')
675
		                  ->join('tracker_sites', 'tracker_sites.id = tracker_titles.site_id', 'left')
676
		                  ->group_by('tracker_titles.site_id')
677
		                  ->order_by('count DESC')
678
		                  ->limit(3)
679
		                  ->get();
680
		$querySitesResult = $querySites->result_array();
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 10 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
681
		$stats['rank1_site']       = $querySitesResult[0]['site'];
682
		$stats['rank1_site_count'] = $querySitesResult[0]['count'];
683
		$stats['rank2_site']       = $querySitesResult[1]['site'];
684
		$stats['rank2_site_count'] = $querySitesResult[1]['count'];
685
		$stats['rank3_site']       = $querySitesResult[2]['site'];
686
		$stats['rank3_site_count'] = $querySitesResult[2]['count'];
687
688
		$queryTitlesU = $this->db->select([
0 ignored issues
show
Documentation introduced by
array('COUNT(*) AS title_updated_count') is of type array<integer,string,{"0":"string"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
689
		                      'COUNT(*) AS title_updated_count'
690
		                  ], FALSE)
691
		                  ->from('tracker_titles_history')
692
		                  ->get();
693
		$stats = array_merge($stats, $queryTitlesU->result_array()[0]);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 8 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
694
695
		$queryUsersU = $this->db->select([
0 ignored issues
show
Documentation introduced by
array('COUNT(*) AS user_updated_count') is of type array<integer,string,{"0":"string"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
696
		                      'COUNT(*) AS user_updated_count'
697
		                  ], FALSE)
698
		                  ->from('tracker_user_history')
699
		                  ->get();
700
		$stats = array_merge($stats, $queryUsersU->result_array()[0]);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 7 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
701
702
		$stats['live_time'] = timespan(/*2016-09-10T03:17:19*/ 1473477439, time(), 2);
703
704
		//$this->db->cache_off();
705
		return $stats;
706
	}
707
708
	//FIXME: Should this be moved elsewhere??
709
	public function getNextUpdateTime() : string {
710
		$temp_now = new DateTime();
711
		$temp_now->setTimezone(new DateTimeZone('America/New_York'));
712
		$temp_now_formatted = $temp_now->format('Y-m-d H:i:s');
713
714
		//NOTE: PHP Bug: DateTime:diff doesn't play nice with setTimezone, so we need to create another DT object
715
		$now         = new DateTime($temp_now_formatted);
716
		$future_date = new DateTime($temp_now_formatted);
717
		$now_hour    = (int) $now->format('H');
718
		if($now_hour < 4) {
719
			//Time until 4am
720
			$future_date->setTime(4, 00);
721
		} elseif($now_hour < 8) {
722
			//Time until 8am
723
			$future_date->setTime(8, 00);
724
		} elseif($now_hour < 12) {
725
			//Time until 12pm
726
			$future_date->setTime(12, 00);
727
		} elseif($now_hour < 16) {
728
			//Time until 4pm
729
			$future_date->setTime(16, 00);
730
		} elseif($now_hour < 20) {
731
			//Time until 8pm
732
			$future_date->setTime(20, 00);
733
		} else {
734
			//Time until 12am
735
			$future_date->setTime(00, 00);
736
			$future_date->add(new DateInterval('P1D'));
737
		}
738
739
		$interval = $future_date->diff($now);
740
		return $interval->format("%H:%I:%S");
741
	}
742
743
	public function reportBug(string $text, $userID = NULL, $url = NULL) : bool {
744
		$this->load->library('email');
745
746
		//This is pretty barebones bug reporting, and honestly not a great way to do it, but it works for now (until the Github is public).
747
		$body = "".
748
		(!is_null($url) && !empty($url) ? "URL: ".htmlspecialchars(substr($url, 0, 255))."<br>\n" : "").
749
		"Submitted by: ".$this->input->ip_address().(!is_null($userID) ? "| {$userID}" : "")."<br>\n".
750
		"<br>Bug report: ".htmlspecialchars(substr($text, 0, 1000));
751
752
		$success = TRUE;
753
		$this->email->from('[email protected]', $this->config->item('site_title', 'ion_auth'));
754
		$this->email->to($this->config->item('admin_email', 'ion_auth'));
755
		$this->email->subject($this->config->item('site_title', 'ion_auth')." - Bug Report");
756
		$this->email->message($body);
757
		if(!$this->email->send()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->email->send() of type null|boolean is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
758
			$success = FALSE;
759
		}
760
		return $success;
761
	}
762
763
	/*************************************************/
764
	public function sites() {
765
		return $this;
766
	}
767
}
768